You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airavata.apache.org by ma...@apache.org on 2020/06/12 19:51:17 UTC

[airavata-django-portal] 04/05: AIRAVATA-3285 range and stepper numeric interactive parameter widgets

This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch AIRAVATA-3285--Interactive-output-view-providers
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git

commit 5bbf1f9c7598eb295d9c46d49f195598141f7a6f
Author: Marcus Christie <ma...@apache.org>
AuthorDate: Thu May 28 09:57:51 2020 -0400

    AIRAVATA-3285 range and stepper numeric interactive parameter widgets
---
 django_airavata/apps/api/output_views.py           | 43 ++++++++++++----
 .../InteractiveParameterRangeWidget.vue            | 59 ++++++++++++++++++++++
 .../InteractiveParameterStepperWidget.vue          | 58 +++++++++++++++++++++
 .../InteractiveParameterWidgetContainer.vue        | 32 ++++++++++--
 4 files changed, 177 insertions(+), 15 deletions(-)

diff --git a/django_airavata/apps/api/output_views.py b/django_airavata/apps/api/output_views.py
index dfc3808..d4fd1ee 100644
--- a/django_airavata/apps/api/output_views.py
+++ b/django_airavata/apps/api/output_views.py
@@ -210,19 +210,26 @@ def _generate_data(request,
                                               experiment,
                                               output_file=output_file,
                                               **kwargs)
-    _convert_options(data)
+    _process_interactive_params(data)
     return data
 
 
-def _convert_options(data):
-    """Convert interactive options to explicit text/value dicts."""
+def _process_interactive_params(data):
     if 'interactive' in data:
+        _convert_options(data)
         for param in data['interactive']:
-            if 'options' in param and isinstance(param['options'][0], str):
-                param['options'] = _convert_options_strings(param['options'])
-            elif 'options' in param and isinstance(
-                    param['options'][0], collections.Sequence):
-                param['options'] = _convert_options_sequences(param['options'])
+            if 'type' not in param:
+                param['type'] = _infer_interactive_param_type(param)
+
+
+def _convert_options(data):
+    """Convert interactive options to explicit text/value dicts."""
+    for param in data['interactive']:
+        if 'options' in param and isinstance(param['options'][0], str):
+            param['options'] = _convert_options_strings(param['options'])
+        elif 'options' in param and isinstance(
+                param['options'][0], collections.Sequence):
+            param['options'] = _convert_options_sequences(param['options'])
 
 
 def _convert_options_strings(options):
@@ -233,6 +240,18 @@ def _convert_options_sequences(options):
     return [{"text": o[0], "value": o[1]} for o in options]
 
 
+def _infer_interactive_param_type(param):
+    v = param['value']
+    if isinstance(v, float):
+        return "float"
+    elif isinstance(v, int):
+        return "integer"
+    elif isinstance(v, str):
+        return "string"
+    elif isinstance(v, bool):
+        return "boolean"
+
+
 def _convert_params_to_type(output_view_provider, params):
     method_sig = inspect.signature(output_view_provider.generate_data)
     method_params = method_sig.parameters
@@ -242,8 +261,12 @@ def _convert_params_to_type(output_view_provider, params):
                 method_params[k].default is not None):
             # TODO: handle lists?
             # Handle boolean and numeric values, converting from string
-            if type(method_params[k].default) is not str:
-                params[k] = json.loads(v)
+            if isinstance(method_params[k].default, bool):
+                params[k] = v == "true"
+            elif isinstance(method_params[k].default, float):
+                params[k] = float(v)
+            elif isinstance(method_params[k].default, int):
+                params[k] = int(v)
             else:
                 params[k] = v
     return params
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/interactive-parameters/InteractiveParameterRangeWidget.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/interactive-parameters/InteractiveParameterRangeWidget.vue
new file mode 100644
index 0000000..8e8e9d2
--- /dev/null
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/interactive-parameters/InteractiveParameterRangeWidget.vue
@@ -0,0 +1,59 @@
+<template>
+  <div>
+    <b-form-input
+      ref="rangeInput"
+      type="range"
+      :value="value"
+      :min="parameter.min"
+      :max="parameter.max"
+      :step="parameter.step || 'any'"
+      @input="updateValue"
+      @mouseup="mouseUp"
+      @keyup="keyUp"
+    />
+    <div>Value: {{ currentValue }}</div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "interactive-parameter-range-widget",
+  props: {
+    value: {
+      type: Number,
+      required: true
+    },
+    parameter: {
+      type: Object
+    }
+  },
+  data() {
+    return {
+      currentValue: parseFloat(this.value)
+    };
+  },
+  computed: {
+    disabled() {
+      return this.currentValue === this.initialValue;
+    },
+    initialValue() {
+      return parseFloat(this.value);
+    }
+  },
+  methods: {
+    updateValue(newValue) {
+      this.currentValue = parseFloat(newValue);
+    },
+    submit() {
+      this.$emit("input", this.currentValue);
+    },
+    mouseUp() {
+      this.$refs.rangeInput.blur();
+      if (!this.disabled) {
+        this.submit();
+      }
+    },
+    keyUp() {}
+  }
+};
+</script>
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/interactive-parameters/InteractiveParameterStepperWidget.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/interactive-parameters/InteractiveParameterStepperWidget.vue
new file mode 100644
index 0000000..a048f1e
--- /dev/null
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/interactive-parameters/InteractiveParameterStepperWidget.vue
@@ -0,0 +1,58 @@
+<template>
+  <b-input-group>
+    <b-form-input
+      ref="textInput"
+      type="number"
+      :value="value"
+      :min="parameter.min"
+      :max="parameter.max"
+      :step="parameter.step || 'any'"
+      @input="updateValue"
+      @keydown.native.enter="enterKeyPressed"
+    />
+    <b-input-group-append>
+      <b-button variant="primary" :disabled="disabled" @click="submit"
+        >Submit</b-button
+      >
+    </b-input-group-append>
+  </b-input-group>
+</template>
+
+<script>
+export default {
+  name: "interactive-parameter-stepper-widget",
+  props: {
+    value: {
+      type: Number,
+      required: true
+    },
+    parameter: {
+      type: Object
+    }
+  },
+  data() {
+    return {
+      currentValue: parseFloat(this.value)
+    };
+  },
+  computed: {
+    disabled() {
+      return this.currentValue === parseFloat(this.value);
+    }
+  },
+  methods: {
+    updateValue(newValue) {
+      this.currentValue = parseFloat(newValue);
+    },
+    submit() {
+      this.$emit("input", this.currentValue);
+    },
+    enterKeyPressed() {
+      if (!this.disabled) {
+        this.$refs.textInput.blur();
+        this.submit();
+      }
+    }
+  }
+};
+</script>
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/interactive-parameters/InteractiveParameterWidgetContainer.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/interactive-parameters/InteractiveParameterWidgetContainer.vue
index 8886a5a..c9d21c1 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/interactive-parameters/InteractiveParameterWidgetContainer.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/output-displays/interactive-parameters/InteractiveParameterWidgetContainer.vue
@@ -1,5 +1,6 @@
 <template>
-  <compoment :is="widgetComponent"
+  <compoment
+    :is="widgetComponent"
     :value="parameter.value"
     :parameter="parameter"
     @input="$emit('input', $event)"
@@ -8,7 +9,9 @@
 
 <script>
 import InteractiveParameterCheckboxWidget from "./InteractiveParameterCheckboxWidget";
+import InteractiveParameterRangeWidget from "./InteractiveParameterRangeWidget.vue";
 import InteractiveParameterSelectWidget from "./InteractiveParameterSelectWidget";
+import InteractiveParameterStepperWidget from "./InteractiveParameterStepperWidget.vue";
 import InteractiveParameterTextInputWidget from "./InteractiveParameterTextInputWidget";
 
 export default {
@@ -21,16 +24,36 @@ export default {
   },
   components: {
     InteractiveParameterCheckboxWidget,
-    InteractiveParameterSelectWidget
+    InteractiveParameterRangeWidget,
+    InteractiveParameterSelectWidget,
+    InteractiveParameterStepperWidget,
+    InteractiveParameterTextInputWidget
   },
   computed: {
     widgetComponent() {
       if (this.parameter.options) {
         return InteractiveParameterSelectWidget;
-      } else if (typeof this.parameter.value === "boolean" || (this.parameter.widget && this.parameter.widget === 'checkbox')) {
+      } else if (
+        this.parameter.type === "boolean" ||
+        (this.parameter.widget && this.parameter.widget === "checkbox")
+      ) {
         return InteractiveParameterCheckboxWidget;
-      } else if (typeof this.parameter.value === "string" || (this.parameter.widget && this.parameter.widget === 'textinput')) {
+      } else if (
+        this.parameter.type === "string" ||
+        (this.parameter.widget && this.parameter.widget === "textinput")
+      ) {
         return InteractiveParameterTextInputWidget;
+      } else if (
+        this.parameter.type === "float" &&
+        "min" in this.parameter &&
+        "max" in this.parameter
+      ) {
+        return InteractiveParameterRangeWidget;
+      } else if (
+        this.parameter.type === "float" ||
+        (this.parameter.widget && this.parameter.widget === "stepper")
+      ) {
+        return InteractiveParameterStepperWidget;
       } else {
         return null;
       }
@@ -38,4 +61,3 @@ export default {
   }
 };
 </script>
-