Repository: ambari
Updated Branches:
  refs/heads/trunk 52d2cc421 -> 0fe39d5e8


AMBARI-11166 Graph widgets: convert % metrics to show 0 - 100% rather than 0 - 
1. (atkach)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/0fe39d5e
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/0fe39d5e
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/0fe39d5e

Branch: refs/heads/trunk
Commit: 0fe39d5e856f43ccfca00a70b9354e8bf899e768
Parents: 52d2cc4
Author: Andrii Tkach <[email protected]>
Authored: Fri May 15 15:03:02 2015 +0300
Committer: Andrii Tkach <[email protected]>
Committed: Fri May 15 17:54:47 2015 +0300

----------------------------------------------------------------------
 .../service/widgets/create/step2_controller.js  |  53 +++++---
 ambari-web/app/messages.js                      |   1 +
 .../app/mixins/common/widgets/widget_mixin.js   |  11 +-
 ambari-web/app/routes/add_widget.js             |   2 -
 .../app/styles/enhanced_service_dashboard.less  |  16 ++-
 .../main/service/widgets/create/expression.hbs  |   5 +
 ambari-web/app/utils/number_utils.js            |   9 ++
 .../views/common/widget/graph_widget_view.js    |  28 ++--
 .../service/widgets/create/expression_view.js   | 134 +++++++++++++++----
 9 files changed, 189 insertions(+), 70 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/0fe39d5e/ambari-web/app/controllers/main/service/widgets/create/step2_controller.js
----------------------------------------------------------------------
diff --git 
a/ambari-web/app/controllers/main/service/widgets/create/step2_controller.js 
b/ambari-web/app/controllers/main/service/widgets/create/step2_controller.js
index b6a3782..9c48872 100644
--- a/ambari-web/app/controllers/main/service/widgets/create/step2_controller.js
+++ b/ambari-web/app/controllers/main/service/widgets/create/step2_controller.js
@@ -343,7 +343,14 @@ App.WidgetWizardStep2Controller = Em.Controller.extend({
       value = '${';
       expression.data.forEach(function (element) {
         if (element.isMetric) {
-          metrics.push(element);
+          metrics.push({
+            "name": element.name,
+            "service_name": element.serviceName,
+            "component_name": element.componentName,
+            "metric_path": element.metricPath,
+            "host_component_criteria": element.hostComponentCriteria,
+            "category": element.category
+          });
         }
         value += element.name;
       }, this);
@@ -463,21 +470,11 @@ App.WidgetWizardStep2Controller = Em.Controller.extend({
     var str = '';
     var data = [];
     var id = 0;
-    var metric;
 
     for (var i = 0, l = expression.length; i < l; i++) {
       if (this.get('OPERATORS').contains(expression[i])) {
         if (str.trim().length > 0) {
-          metric = metrics.findProperty('name', str.trim());
-          data.pushObject(Em.Object.create({
-            id: ++id,
-            name: str.trim(),
-            isMetric: true,
-            componentName: metric.component_name,
-            serviceName: metric.service_name,
-            metricPath: metric.metric_path,
-            hostComponentCriteria: metric.host_component_criteria
-          }));
+          data.pushObject(this.getExpressionVariable(str.trim(), id, metrics));
           str = '';
         }
         data.pushObject(Em.Object.create({
@@ -490,18 +487,40 @@ App.WidgetWizardStep2Controller = Em.Controller.extend({
       }
     }
     if (str.trim().length > 0) {
-      metric = metrics.findProperty('name', str.trim());
-      data.pushObject(Em.Object.create({
+      data.pushObject(this.getExpressionVariable(str.trim(), id, metrics));
+    }
+    return data;
+  },
+
+  /**
+   * get variable of expression
+   * could be name of metric "m1" or constant number "1"
+   * @param {string} name
+   * @param {Array} metrics
+   * @param {number} id
+   * @returns {Em.Object}
+   */
+  getExpressionVariable: function (name, id, metrics) {
+    var metric;
+
+    if (isNaN(Number(name))) {
+      metric = metrics.findProperty('name', name);
+      return Em.Object.create({
         id: ++id,
-        name: str.trim(),
+        name: metric.name,
         isMetric: true,
         componentName: metric.component_name,
         serviceName: metric.service_name,
         metricPath: metric.metric_path,
         hostComponentCriteria: metric.host_component_criteria
-      }));
+      });
+    } else {
+      return Em.Object.create({
+        id: ++id,
+        name: name,
+        isNumber: true
+      });
     }
-    return data;
   },
 
   next: function () {

http://git-wip-us.apache.org/repos/asf/ambari/blob/0fe39d5e/ambari-web/app/messages.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index d54cdd7..67ad9fd 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -2579,6 +2579,7 @@ Em.I18n.translations = {
   'dashboard.widgets.wizard.step2.addMetrics': 'Add Metrics and operators 
here...',
   'dashboard.widgets.wizard.step2.newMetric': '+ Add Metric',
   'dashboard.widgets.wizard.step2.newOperator': '+ Add Operator',
+  'dashboard.widgets.wizard.step2.newNumber': '+ Add Number',
   'dashboard.widgets.wizard.step2.Component': 'Component',
   'dashboard.widgets.wizard.step2.Metric': 'Metric',
   'dashboard.widgets.wizard.step2.selectComponent': 'Select a Component',

http://git-wip-us.apache.org/repos/asf/ambari/blob/0fe39d5e/ambari-web/app/mixins/common/widgets/widget_mixin.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mixins/common/widgets/widget_mixin.js 
b/ambari-web/app/mixins/common/widgets/widget_mixin.js
index a7a748a..7f9834d 100644
--- a/ambari-web/app/mixins/common/widgets/widget_mixin.js
+++ b/ambari-web/app/mixins/common/widgets/widget_mixin.js
@@ -617,16 +617,7 @@ App.WidgetPreviewMixin = Ember.Mixin.create({
       'values': this.get('controller.widgetValues'),
       'properties': this.get('controller.widgetProperties'),
       'widgetName': this.get('controller.widgetName'),
-      'metrics': this.get('controller.widgetMetrics').map(function (metric) {
-        return {
-          "name": metric.name,
-          "service_name": metric.serviceName,
-          "component_name": metric.componentName,
-          "metric_path": metric.metricPath,
-          "host_component_criteria": metric.hostComponentCriteria,
-          "category": metric.category
-        }
-      })
+      'metrics': this.get('controller.widgetMetrics')
     });
     this._super();
   }.observes('controller.widgetProperties', 'controller.widgetValues', 
'controller.widgetMetrics', 'controller.widgetName'),

http://git-wip-us.apache.org/repos/asf/ambari/blob/0fe39d5e/ambari-web/app/routes/add_widget.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/routes/add_widget.js 
b/ambari-web/app/routes/add_widget.js
index 8c427f4..2631b16 100644
--- a/ambari-web/app/routes/add_widget.js
+++ b/ambari-web/app/routes/add_widget.js
@@ -111,8 +111,6 @@ module.exports = App.WizardRoute.extend({
       widgetWizardController.save('widgetProperties', 
widgetStep2controller.get('widgetProperties'));
       widgetWizardController.save('widgetMetrics', 
widgetStep2controller.get('widgetMetrics'));
       widgetWizardController.save('widgetValues', 
widgetStep2controller.get('widgetValues'));
-      widgetWizardController.save('expressions', 
widgetStep2controller.get('expressions'));
-      widgetWizardController.save('dataSets', 
widgetStep2controller.get('dataSets'));
       widgetWizardController.save('templateValue', 
widgetStep2controller.get('templateValue'));
       widgetWizardController.save('widgetName', "");
       widgetWizardController.save('widgetDescription', "");

http://git-wip-us.apache.org/repos/asf/ambari/blob/0fe39d5e/ambari-web/app/styles/enhanced_service_dashboard.less
----------------------------------------------------------------------
diff --git a/ambari-web/app/styles/enhanced_service_dashboard.less 
b/ambari-web/app/styles/enhanced_service_dashboard.less
index 4ca4b54..1cc27b8 100644
--- a/ambari-web/app/styles/enhanced_service_dashboard.less
+++ b/ambari-web/app/styles/enhanced_service_dashboard.less
@@ -19,6 +19,7 @@
 @import 'common.less';
 
 @border-color: #ddd;
+@invalid-color: red;
 
 .service-metrics-block {
 
@@ -319,7 +320,7 @@
     .is-invalid.controls,
     .metric-container.is-invalid {
       .metric-field {
-        border-color: red;
+        border-color: @invalid-color;
       }
     }
     .metric-container {
@@ -337,6 +338,15 @@
         .add-operator {
           margin-left: 25px;
         }
+        .add-number {
+          margin-left: 40px;
+          .add-on {
+            height: inherit;
+          }
+        }
+        .is-invalid {
+          border-color: @invalid-color
+        }
       }
       .metric-field {
         height: 85%;
@@ -375,7 +385,7 @@
         border-top: none;
       }
       .controls.is-invalid {
-        border-color: red;
+        border-color: @invalid-color;
       }
       .add-item-input {
         display: inline-block!important;
@@ -400,7 +410,7 @@
           transition: none;
         }
         .ember-text-field.is-invalid {
-          border: 1px solid red;
+          border: 1px solid @invalid-color;
         }
       }
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/0fe39d5e/ambari-web/app/templates/main/service/widgets/create/expression.hbs
----------------------------------------------------------------------
diff --git 
a/ambari-web/app/templates/main/service/widgets/create/expression.hbs 
b/ambari-web/app/templates/main/service/widgets/create/expression.hbs
index 3398725..969c56e 100644
--- a/ambari-web/app/templates/main/service/widgets/create/expression.hbs
+++ b/ambari-web/app/templates/main/service/widgets/create/expression.hbs
@@ -29,6 +29,11 @@
       {{/each}}
     </ul>
   </div>
+  <div class="input-append span2 add-number">
+    {{view App.AddNumberExpressionView valueBinding="view.numberValue" 
class="input-small"}}
+    <button class="btn add-on" {{action addNumber target="view"}} {{bindAttr 
disabled="view.isNumberValueInvalid"}}>{{t 
dashboard.widgets.wizard.step2.newNumber}}</button>
+  </div>
+
 </div>
 <div class="metric-field">
   {{#if view.expression.isRemovable}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/0fe39d5e/ambari-web/app/utils/number_utils.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/number_utils.js 
b/ambari-web/app/utils/number_utils.js
index b39496e..4331f85 100644
--- a/ambari-web/app/utils/number_utils.js
+++ b/ambari-web/app/utils/number_utils.js
@@ -114,5 +114,14 @@ module.exports = {
     decimals = decimals[1] ? decimals[1].length : 0;
 
     return decimals;
+  },
+
+  /**
+   * @param n
+   * @return {boolean}
+   */
+  isPositiveNumber: function(n) {
+    var number = Number(n);
+    return !isNaN(number) && isFinite(number) && (number > 0);
   }
 };

http://git-wip-us.apache.org/repos/asf/ambari/blob/0fe39d5e/ambari-web/app/views/common/widget/graph_widget_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/common/widget/graph_widget_view.js 
b/ambari-web/app/views/common/widget/graph_widget_view.js
index 6c9b9b5..6483608 100644
--- a/ambari-web/app/views/common/widget/graph_widget_view.js
+++ b/ambari-web/app/views/common/widget/graph_widget_view.js
@@ -111,15 +111,17 @@ App.GraphWidgetView = Em.View.extend(App.WidgetMixin, {
 
     //replace values with metrics data
     expression.match(this.get('VALUE_NAME_REGEX')).forEach(function (match) {
-      if (metrics.someProperty('name', match)) {
-        dataLinks[match] = metrics.findProperty('name', match).data;
-        if (!isDataCorrupted) {
-          isDataCorrupted = (dataLength !== -1 && dataLength !== 
dataLinks[match].length);
+      if (isNaN(match)) {
+        if (metrics.someProperty('name', match)) {
+          dataLinks[match] = metrics.findProperty('name', match).data;
+          if (!isDataCorrupted) {
+            isDataCorrupted = (dataLength !== -1 && dataLength !== 
dataLinks[match].length);
+          }
+          dataLength = (dataLinks[match].length > dataLength) ? 
dataLinks[match].length : dataLength;
+        } else {
+          validExpression = false;
+          console.error('Metrics with name "' + match + '" not found to 
compute expression');
         }
-        dataLength = (dataLinks[match].length > dataLength) ? 
dataLinks[match].length : dataLength;
-      } else {
-        validExpression = false;
-        console.error('Metrics with name "' + match + '" not found to compute 
expression');
       }
     });
 
@@ -130,9 +132,13 @@ App.GraphWidgetView = Em.View.extend(App.WidgetMixin, {
       for (var i = 0, timestamp; i < dataLength; i++) {
         isPointNull = false;
         beforeCompute = expression.replace(this.get('VALUE_NAME_REGEX'), 
function (match) {
-          timestamp = dataLinks[match][i][1];
-          isPointNull = (isPointNull) ? true : 
(Em.isNone(dataLinks[match][i][0]));
-          return dataLinks[match][i][0];
+          if (isNaN(match)) {
+            timestamp = dataLinks[match][i][1];
+            isPointNull = (isPointNull) ? true : 
(Em.isNone(dataLinks[match][i][0]));
+            return dataLinks[match][i][0];
+          } else {
+            return match;
+          }
         });
         var dataLinkPointValue = isPointNull ? null : 
Number(window.eval(beforeCompute));
         // expression resulting into `0/0` will produce NaN Object which is 
not a valid series data value for RickShaw graphs

http://git-wip-us.apache.org/repos/asf/ambari/blob/0fe39d5e/ambari-web/app/views/main/service/widgets/create/expression_view.js
----------------------------------------------------------------------
diff --git 
a/ambari-web/app/views/main/service/widgets/create/expression_view.js 
b/ambari-web/app/views/main/service/widgets/create/expression_view.js
index 5c12ca9..a232d89 100644
--- a/ambari-web/app/views/main/service/widgets/create/expression_view.js
+++ b/ambari-web/app/views/main/service/widgets/create/expression_view.js
@@ -17,6 +17,7 @@
  */
 
 var misc = require('utils/misc');
+var number_utils = require("utils/number_utils");
 
 App.WidgetWizardExpressionView = Em.View.extend({
   templateName: require('templates/main/service/widgets/create/expression'),
@@ -39,6 +40,18 @@ App.WidgetWizardExpressionView = Em.View.extend({
   OPERATORS: ["+", "-", "*", "/", "(", ")"],
 
   /**
+   * @type {Array}
+   * @const
+   */
+  AGGREGATE_FUNCTIONS: ['avg', 'sum', 'min', 'max'],
+
+  /**
+   * @type {RegExp}
+   * @const
+   */
+  VALID_EXPRESSION_REGEX: /^((\(\s)*[\d]+)[\(\)\+\-\*\/\.\d\s]*[\d\)]*$/,
+
+  /**
    * contains expression data before editing in order to restore previous state
    */
   dataBefore: [],
@@ -54,6 +67,19 @@ App.WidgetWizardExpressionView = Em.View.extend({
   isInvalid: false,
 
   /**
+   * contains value of number added to expression
+   * @type {string}
+   */
+  numberValue: "",
+
+  /**
+   * @type {boolean}
+   */
+  isNumberValueInvalid: function () {
+    return this.get('numberValue').trim() === "" || 
!number_utils.isPositiveNumber(this.get('numberValue').trim());
+  }.property('numberValue'),
+
+  /**
    * add operator to expression data
    * @param event
    */
@@ -69,6 +95,22 @@ App.WidgetWizardExpressionView = Em.View.extend({
   },
 
   /**
+   * add operator to expression data
+   * @param event
+   */
+  addNumber: function (event) {
+    var data = this.get('expression.data');
+    var lastId = (data.length > 0) ? Math.max.apply(this, 
data.mapProperty('id')) : 0;
+
+    data.pushObject(Em.Object.create({
+      id: ++lastId,
+      name: this.get('numberValue'),
+      isNumber: true
+    }));
+    this.set('numberValue', "");
+  },
+
+  /**
    * redraw expression
    * NOTE: needed in order to avoid collision between scrollable lib and 
metric action event
    */
@@ -117,7 +159,7 @@ App.WidgetWizardExpressionView = Em.View.extend({
     }, this).join(" ");
 
     if (expression.length > 0) {
-      if (/^((\(\s)*[\d]+)[\(\)\+\-\*\/\d\s]*[\d\)]*$/.test(expression)) {
+      if (this.get('VALID_EXPRESSION_REGEX').test(expression)) {
         try {
           isInvalid = !isFinite(window.eval(expression));
         } catch (e) {
@@ -134,13 +176,23 @@ App.WidgetWizardExpressionView = Em.View.extend({
     if (!isInvalid) {
       this.get('controller').updateExpressions();
     }
-  }.observes('expression.data.length'),
+  }.observes('expression.data.length')
+});
+
+/**
+ * input used to add number to expression
+ * @type {Em.TextField}
+ * @class
+ */
+App.AddNumberExpressionView = Em.TextField.extend({
+  classNameBindings: ['isInvalid'],
 
   /**
-   * @type {Array}
-   * @const
+   * @type {boolean}
    */
-  AGGREGATE_FUNCTIONS: ['avg', 'sum', 'min', 'max']
+  isInvalid: function () {
+    return this.get('value').trim().length > 0 && 
!number_utils.isPositiveNumber(this.get('value').trim());
+  }.property('value')
 });
 
 
@@ -212,10 +264,10 @@ App.AddMetricExpressionView = Em.View.extend({
    */
   addMetric: function (event) {
     var selectedMetric = event.context.get('selectedMetric'),
-      aggregateFunction = event.context.get('selectedAggregation');
-    var isAddEnabled = event.context.get('isAddEnabled');
-    var result =  jQuery.extend(true, {}, selectedMetric);
-    if (isAddEnabled) {
+        aggregateFunction = event.context.get('selectedAggregation'),
+        result = Em.Object.create(selectedMetric);
+
+    if (event.context.get('isAddEnabled')) {
       var data = this.get('parentView').get('expression.data'),
         id = (data.length > 0) ? Math.max.apply(this.get('parentView'), 
data.mapProperty('id')) + 1 : 1;
       result.set('id', id);
@@ -347,28 +399,36 @@ App.InputCursorTextfieldView = Ember.TextField.extend({
     Em.run.next( function() { $('.add-item-input .ember-text-field').focus(); 
});
   }.observes('parentView.expression.data.length'),
 
+  focusOut: function(evt) {
+    this.saveNumber();
+  },
+
   validateInput: function () {
     var value = this.get('value');
     var parentView = this.get('parentView');
-    this.set('isInvalid', false);
-    if (value && parentView.get('OPERATORS').contains(value)) {
-      // add operator
-      var data = parentView.get('expression.data');
-      var lastId = (data.length > 0) ? Math.max.apply(parentView, 
data.mapProperty('id')) : 0;
-      data.pushObject(Em.Object.create({
-        id: ++lastId,
-        name: value,
-        isOperator: true
-      }));
-      this.set('value', '');
-    } else if (value && value == 'm') {
-      // open add metric menu
-      $('#add-metric-menu > div > a').click();
-      this.set('value', '');
-    } else if (value) {
-      // invalid operator
-      this.set('isInvalid', true);
+    var isInvalid = false;
+
+    if (!number_utils.isPositiveNumber(value))  {
+      if (value && parentView.get('OPERATORS').contains(value)) {
+        // add operator
+        var data = parentView.get('expression.data');
+        var lastId = (data.length > 0) ? Math.max.apply(parentView, 
data.mapProperty('id')) : 0;
+        data.pushObject(Em.Object.create({
+          id: ++lastId,
+          name: value,
+          isOperator: true
+        }));
+        this.set('value', '');
+      } else if (value && value == 'm') {
+        // open add metric menu
+        $('#add-metric-menu > div > a').click();
+        this.set('value', '');
+      } else if (value) {
+        // invalid operator
+        isInvalid = true;
+      }
     }
+    this.set('isInvalid', isInvalid);
   }.observes('value'),
 
   keyDown: function (event) {
@@ -377,6 +437,26 @@ App.InputCursorTextfieldView = Ember.TextField.extend({
       if (data.length >= 1) {
         data.removeObject(data[data.length - 1]);
       }
+    } else if (event.keyCode == 13) { //Enter
+      this.saveNumber();
+    }
+  },
+
+  saveNumber: function() {
+    var number_utils = require("utils/number_utils");
+    var value = this.get('value');
+    if (number_utils.isPositiveNumber(value))  {
+      var parentView = this.get('parentView');
+      var data = parentView.get('expression.data');
+      var lastId = (data.length > 0) ? Math.max.apply(this, 
data.mapProperty('id')) : 0;
+      data.pushObject(Em.Object.create({
+        id: ++lastId,
+        name: this.get('value'),
+        isNumber: true
+      }));
+      this.set('numberValue', "");
+      this.set('isInvalid', false);
+      this.set('value', '');
     }
   }
 });

Reply via email to