You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pinot.apache.org by aa...@apache.org on 2019/01/28 22:04:05 UTC

[incubator-pinot] branch master updated: Email share (#3753)

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

aaronucsd pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-pinot.git


The following commit(s) were added to refs/heads/master by this push:
     new 587c597  Email share (#3753)
587c597 is described below

commit 587c597e70fa5d08d0824c261a161a5f797387d7
Author: Harley Jackson <ha...@gmail.com>
AuthorDate: Mon Jan 28 14:03:58 2019 -0800

    Email share (#3753)
    
    * [TE] frontend - harleyjj/share-dashboard - maintains columns and dashboard-level comments in share link
    
    * [TE] frontend - harleyjj/share-dashboard - Align email options with columns after share
---
 .../app/pods/home/share-dashboard/controller.js    | 238 ++++++++-------------
 .../app/pods/home/share-dashboard/route.js         |  70 +++++-
 .../app/pods/home/share-dashboard/template.hbs     |   8 +-
 3 files changed, 157 insertions(+), 159 deletions(-)

diff --git a/thirdeye/thirdeye-frontend/app/pods/home/share-dashboard/controller.js b/thirdeye/thirdeye-frontend/app/pods/home/share-dashboard/controller.js
index 5f2e7c8..f676376 100644
--- a/thirdeye/thirdeye-frontend/app/pods/home/share-dashboard/controller.js
+++ b/thirdeye/thirdeye-frontend/app/pods/home/share-dashboard/controller.js
@@ -10,7 +10,6 @@ import $ from 'jquery';
 import _ from 'lodash';
 import * as anomalyUtil from 'thirdeye-frontend/utils/anomaly';
 import { isBlank } from '@ember/utils';
-import { task } from 'ember-concurrency';
 import { inject as service } from '@ember/service';
 import {
   get,
@@ -18,26 +17,28 @@ import {
   setProperties,
   computed
 } from '@ember/object';
-import { appendFilters } from 'thirdeye-frontend/utils/rca-utils';
-import { humanizeFloat, humanizeChange, checkStatus } from 'thirdeye-frontend/utils/utils';
-import floatToPercent from 'thirdeye-frontend/utils/float-to-percent';
+import { reads } from '@ember/object/computed';
 
 const CUSTOMIZE_OPTIONS = [{
   id: 0,
   label: 'Nothing',
-  value: 'Nothing'
+  value: 'Nothing',
+  selected: null
 }, {
   id: 1,
   label: 'WoW',
-  value: 'Wow'
+  value: 'Wow',
+  selected: null
 }, {
   id: 2,
   label: 'Wo2W',
-  value: 'Wo2w'
+  value: 'Wo2w',
+  selected: null
 }, {
   id: 3,
   label: 'Median4W',
-  value: 'Median4w'
+  value: 'Median4w',
+  selected: null
 }];
 
 export default Controller.extend({
@@ -46,14 +47,14 @@ export default Controller.extend({
   showCopyTooltip: false,
   showSharedTooltip: false,
   shareUrl: null,
-  showWow: false,
-  showWo2w: false,
-  showMedian4w: false,
-  showDashboardSummary: false,
-  offsetsMap: {},
-  options: [],
-  options_two: [],
-  colspanNum: 4,
+  showDashboardSummary: reads('tree.firstObject.showDashboardSummary'),
+  showCustomizeEmailTemplate: reads('tree.firstObject.showCustomizeEmailTemplate'),
+  showWow: reads('tree.firstObject.showWow'),
+  showWo2w: reads('tree.firstObject.showWo2w'),
+  showMedian4w: reads('tree.firstObject.showMedian4w'),
+  options: reads('tree.firstObject.options'),
+  options_two: reads('tree.firstObject.options_two'),
+  colspanNum: reads('tree.firstObject.colspanNum'),
 
   init() {
     this._super(...arguments);
@@ -65,22 +66,9 @@ export default Controller.extend({
       status: 'All Resolutions'
     });
     set(this, 'anomalyResponseFilterTypes', anomalyResponseFilterTypes);
-
-    // Add the options for compare
-    set(this, 'options_two', set(this, 'options', CUSTOMIZE_OPTIONS));
   },
 
   /**
-   * @summary Returns the
-   * @return {Array}
-   */
-  _getAnomalyByOffsetsMapping: task (function * () {
-    const applicationAnomalies = get(this, 'model.anomalyMapping');
-    const offsetsMapping = yield get(this, '_fetchOffsets').perform(applicationAnomalies);
-    return offsetsMapping;
-  }).drop(),
-
-  /**
    * @summary Returns the config object for the custom share template header.
    * @return {object} The object of metrics, with array of alerts and array anomalies
    * @example
@@ -184,13 +172,13 @@ export default Controller.extend({
             if (typeof metric === 'string') {
               const metricId = filteredAnomalyMapping[metric].metricId;
               const functionId = filteredAnomalyMapping[metric].items[alert].functionId;
-              filteredAnomalyMapping[metric].items[alert].viewTreeNode = viewTreeFirstChild.find(metric => metric.id === metricId).children.find(alert => alert.id === functionId);
+              set(filteredAnomalyMapping[metric].items[alert], 'viewTreeNode', viewTreeFirstChild.find(metric => metric.id === metricId).children.find(alert => alert.id === functionId));
             }
           });
           // Keeping a reference to this new tree node for this metric in filteredAnomalyMapping
           if (typeof metric === 'string') {
             const metricId = filteredAnomalyMapping[metric].metricId;
-            filteredAnomalyMapping[metric].viewTreeNode = viewTreeFirstChild.find(metric => metric.id === metricId);
+            set(filteredAnomalyMapping[metric], 'viewTreeNode', viewTreeFirstChild.find(metric => metric.id === metricId));
           }
         });
         return viewTree;
@@ -205,7 +193,15 @@ export default Controller.extend({
         isVisible: true,
         isChecked: true,
         comment: null,
-        children: []
+        children: [],
+        showWow: false,
+        showWo2w: false,
+        showMedian4w: false,
+        showDashboardSummary: false,
+        showCustomizeEmailTemplate: false,
+        options: [ ...CUSTOMIZE_OPTIONS],
+        options_two: [ ...CUSTOMIZE_OPTIONS],
+        colspan: 4
       }];
 
       viewTreeFirstChild = viewTree.get('firstObject').children;
@@ -226,7 +222,7 @@ export default Controller.extend({
               children: []
             });
             // Keeping a reference to this new tree node for this alert in filteredAnomalyMapping
-            filteredAnomalyMapping[metric].items[alert].viewTreeNode = tempChildren[index];
+            set(filteredAnomalyMapping[metric].items[alert], 'viewTreeNode', tempChildren[index]);
           });
 
           //add each metric to tree selection
@@ -243,7 +239,7 @@ export default Controller.extend({
             children: tempChildren
           });
           // Keeping a reference to this new tree node for this metric in filteredAnomalyMapping
-          filteredAnomalyMapping[metric].viewTreeNode = viewTreeFirstChild[index];
+          set(filteredAnomalyMapping[metric], 'viewTreeNode', viewTreeFirstChild[index]);
         }
       });
 
@@ -269,68 +265,6 @@ export default Controller.extend({
     });
   },
 
-  _fetchOffsets: task (function * (anomalyMapping) {
-    if (!anomalyMapping) { return; }
-
-    let map = {};
-    let index = 1;
-    // Iterate through each anomaly
-    yield Object.keys(anomalyMapping).some(function(metric) {
-      Object.keys(anomalyMapping[metric].items).some(function(alert) {
-        anomalyMapping[metric].items[alert].items.forEach(async (item) => {
-          const metricName = get(item.anomaly, 'metricName');
-          const metricId = get(item.anomaly, 'metricId');
-          const functionName = get(item.anomaly, 'functionName');
-          const functionId = get(item.anomaly, 'functionId');
-
-          const dimensions = get(item.anomaly, 'dimensions');
-          const start = get(item.anomaly, 'start');
-          const end = get(item.anomaly, 'end');
-
-          if (!map[metricName]) {
-            map[metricName] = { 'metricId': metricId, items: {}, count: index };
-            index++;
-          }
-
-          if(!map[metricName].items[functionName]) {
-            map[metricName].items[functionName] = { 'functionId': functionId, items: [] };
-          }
-
-          const filteredDimensions = Object.keys(dimensions).map(key => [key, '=', dimensions[key]]);
-          //build new urn
-          const metricUrn = appendFilters(`thirdeye:metric:${metricId}`, filteredDimensions);
-          //Get all in the following order - current,wo2w,median4w
-          const offsets = await fetch(`/rootcause/metric/aggregate/batch?urn=${metricUrn}&start=${start}&end=${end}&offsets=wo1w,wo2w,median4w&timezone=America/Los_Angeles`).then(checkStatus).then(res => res);
-
-          const current = get(item.anomaly, 'current');
-          const wow = humanizeFloat(offsets[0]);
-          const wo2w = humanizeFloat(offsets[1]);
-          const median4w = humanizeFloat(offsets[2]);
-          const wowChange = floatToPercent(Number((current - offsets[0]) / offsets[0]));
-          const wo2wChange = floatToPercent(Number((current - offsets[1]) / offsets[1]));
-          const median4wChange = floatToPercent(Number((current - offsets[2]) / offsets[2]));
-          const wowHumanizeChange = humanizeChange(Number((current - offsets[0]) / offsets[0]));
-          const wo2wHumanizeChange = humanizeChange(Number((current - offsets[1]) / offsets[1]));
-          const median4wHumanizeChange = humanizeChange(Number((current - offsets[2]) / offsets[2]));
-
-          set(item.anomaly, 'offsets',  offsets ? {
-            'wow': { value: wow, change: wowChange, humanizedChangeDisplay: wowHumanizeChange },
-            'wo2w': { value: wo2w, change: wo2wChange, humanizedChangeDisplay: wo2wHumanizeChange },
-            'median4w': { value: median4w, change: median4wChange, humanizedChangeDisplay: median4wHumanizeChange },
-          } : {
-            'wow': '-',
-            'wo2w': '-',
-            'median4w': '-'
-          });
-
-          map[metricName].items[functionName].items.push(item);
-        });
-      });
-    });
-    // return updated anomalyMapping
-    return anomalyMapping;
-  }).drop(),
-
   /**
    * Helper for getting the matching selected response feedback object
    * @param {string} text - this could be anything that we want to do a copy to clipboard
@@ -361,45 +295,44 @@ export default Controller.extend({
     return text;
   },
 
-  async _getOffsets() {
-      const map = get(this, 'offsetsMap');
-      if (Object.keys(map).length === 0 ) {//Only fetch if empty
-        const result = await get(this, '_getAnomalyByOffsetsMapping').perform();
-        let index = 1;
-        // Iterate through each anomaly
-        Object.keys(result).some(function(metric) {
-          Object.keys(result[metric].items).some(function(alert) {
-            result[metric].items[alert].items.forEach(item => {
-                const metricName = get(item.anomaly, 'metricName');
-                const metricId = get(item.anomaly, 'metricId');
-                const functionName = get(item.anomaly, 'functionName');
-                const functionId = get(item.anomaly, 'functionId');
 
-                if (!map[metricName]) {
-                  map[metricName] = { 'metricId': metricId, items: {}, count: index };
-                  index++;
-                }
-
-                if(!map[metricName].items[functionName]) {
-                  map[metricName].items[functionName] = { 'functionId': functionId, items: [] };
-                }
+  /**
+   * Helper for toggling show properties in tree.firstObject
+   * @param {string} property - showWow, showWo2w, showMedian4w, showDashboardSummary, or showCustomizeEmailTemplate
+   * @return {none} - this helper is just a setter
+   */
+  _toggleTreeProperty(property) {
+    let currentState = get(this, 'tree.firstObject');
+    set(currentState, property, !currentState[property]);
+  },
 
-                map[metricName].items[functionName].items.push(item);
-            });
-          });
-        });
-        setProperties(this, {
-          'model.anomalyMapping': map,
-          'offsetsMap': map
-        });
+  /**
+   * Helper for setting selected in email selectors
+   * @param {string} selectedBaseline - Wow, Wo2w, Median4w
+   * @param {Array} choices - [{}, {}, ...]
+   * @return {none} - this helper is just a setter
+   */
+  _selectOption(choices, selectedBaseline) {
+    const newChoices = [];
+    choices.forEach(choice => {
+      const newObj = { ...choice};
+      if (newObj.value === selectedBaseline) {
+        newObj.selected = 'selected';
+      } else {
+        newObj.selected = null;
       }
+      newChoices.push(newObj);
+    });
+    return newChoices;
   },
 
   _customizeEmailHelper(option, type) {
     //reset both selects' options
-    set(this, 'options_two', set(this, 'options', CUSTOMIZE_OPTIONS));
+    set(this, 'tree.firstObject.options', [ ...CUSTOMIZE_OPTIONS]);
+    set(this, 'tree.firstObject.options_two', [ ...CUSTOMIZE_OPTIONS]);
     //hide all except if the sibling's value and not `nothing`
-    setProperties(this, {
+    let currentState = get(this, 'tree.firstObject');
+    setProperties(currentState, {
       'showWow': false,
       'showWo2w': false,
       'showMedian4w': false
@@ -409,58 +342,56 @@ export default Controller.extend({
     const customizeEmail1 = document.getElementById('customizeEmail1').value;
     const customizeEmail2 = document.getElementById('customizeEmail2').value;
 
-    const sibingValue = type === 'one' ? customizeEmail2 : customizeEmail1;
-    this.toggleProperty(`show${sibingValue}`);
+    const siblingValue = type === 'one' ? customizeEmail2 : customizeEmail1;
+    this._toggleTreeProperty(`show${siblingValue}`);
 
     //Calculates colspan Number
     const showCustomizeEmailTemplate = get(this, 'showCustomizeEmailTemplate');
     if(showCustomizeEmailTemplate && (customizeEmail1 === 'Nothing' || customizeEmail2 === 'Nothing')) {
-      set(this, 'colspanNum', 5);
+      set(this, 'tree.firstObject.colspanNum', 5);
     } else if (showCustomizeEmailTemplate) {
-      set(this, 'colspanNum', 6);
+      set(this, 'tree.firstObject.colspanNum', 6);
     } else {
-      set(this, 'colspanNum', 4);
+      set(this, 'tree.firstObject.colspanNum', 4);
     }
 
+    let limitedSelfOptions = this._selectOption([ ...CUSTOMIZE_OPTIONS], option);
+    let limitedSiblingOptions = this._selectOption([ ...CUSTOMIZE_OPTIONS], siblingValue);
+
     //limited sibling list to selected choice
-    const limitedSiblingOptions = CUSTOMIZE_OPTIONS.filter(function(item){
+    limitedSiblingOptions = limitedSiblingOptions.filter(function(item){
       return item.value !== option;
     });
     //limited current list to sibling's existing selected
-    const limitedSelfOptions = CUSTOMIZE_OPTIONS.filter(function(item){
-      return item.value !== sibingValue;
+    limitedSelfOptions = limitedSelfOptions.filter(function(item){
+      return item.value !== siblingValue;
     });
 
-    if (type === 'one') {
-      set(this, 'options_two', limitedSiblingOptions);
-      set(this, 'options', limitedSelfOptions);
-    } else {
-      set(this, 'options', limitedSiblingOptions);
-      set(this, 'options_two', limitedSelfOptions);
-    }
-
-    // fetch base on offset
-    if (option !== 'Nothing') {
-      this._getOffsets();
-    }
-
     switch(option) {
       case 'Nothing':
         //hide accordingly
         break;
       case 'Wow':
         //show wow column and it's sibling
-        this.toggleProperty('showWow');
+        this._toggleTreeProperty('showWow');
         break;
       case 'Wo2w':
         //show wo2w column and it's sibling
-        this.toggleProperty('showWo2w');
+        this._toggleTreeProperty('showWo2w');
         break;
       case 'Median4w':
         //show median4w column and it's sibling
-        this.toggleProperty('showMedian4w');
+        this._toggleTreeProperty('showMedian4w');
         break;
     }
+
+    if (type === 'one') {
+      set(this, 'tree.firstObject.options_two', limitedSiblingOptions);
+      set(this, 'tree.firstObject.options', limitedSelfOptions);
+    } else {
+      set(this, 'tree.firstObject.options', limitedSiblingOptions);
+      set(this, 'tree.firstObject.options_two', limitedSelfOptions);
+    }
   },
 
   actions: {
@@ -529,7 +460,6 @@ export default Controller.extend({
         let res = get(this, 'tree.firstObject.children').filter(metric => metric.id === id);
         set(res, 'firstObject.comment', userComment);
       }
-
     },
 
     /**
@@ -552,9 +482,9 @@ export default Controller.extend({
       }
 
       if (id === 'dashboard_summary') {
-        this.toggleProperty('showDashboardSummary');
+        this._toggleTreeProperty('showDashboardSummary');
       } else if (id === 'customize_email') {
-        this.toggleProperty('showCustomizeEmailTemplate');
+        this._toggleTreeProperty('showCustomizeEmailTemplate');
       }
     },
 
diff --git a/thirdeye/thirdeye-frontend/app/pods/home/share-dashboard/route.js b/thirdeye/thirdeye-frontend/app/pods/home/share-dashboard/route.js
index a3b3625..9953a5d 100644
--- a/thirdeye/thirdeye-frontend/app/pods/home/share-dashboard/route.js
+++ b/thirdeye/thirdeye-frontend/app/pods/home/share-dashboard/route.js
@@ -11,6 +11,9 @@ import {
   set,
   setProperties
 } from '@ember/object';
+import { appendFilters } from 'thirdeye-frontend/utils/rca-utils';
+import { humanizeFloat, humanizeChange, checkStatus } from 'thirdeye-frontend/utils/utils';
+import floatToPercent from 'thirdeye-frontend/utils/float-to-percent';
 
 const queryParamsConfig = {
   refreshModel: true
@@ -70,7 +73,7 @@ export default Route.extend(AuthenticatedRouteMixin, {
 
     return new RSVP.Promise(async (resolve, reject) => {
       try {
-        const anomalyMapping = appName ? await get(this, '_getAnomalyMapping').perform(model) : [];//DEMO:
+        const anomalyMapping = appName ? await get(this, '_getAnomalyMapping').perform(model) : []; //DEMO:
         const shareMetaData = shareId ? await get(this, 'shareDashboardApiService').queryShareMetaById(shareId) : [];
         const shareTemplateConfig = appName ? await get(this, 'shareTemplateConfigApiService').queryShareTemplateConfigByAppName(appName) : {};
         const defaultParams = {
@@ -123,6 +126,71 @@ export default Route.extend(AuthenticatedRouteMixin, {
       anomalyMapping[metricName].items[functionName].items.push(get(this, 'anomaliesApiService').getHumanizedEntity(anomaly, humanizedObject));
     });
 
+    anomalyMapping = yield get(this, '_fetchOffsets').perform(anomalyMapping);
+    return anomalyMapping;
+  }).drop(),
+
+  _fetchOffsets: task (function * (anomalyMapping) {
+    if (!anomalyMapping) { return; }
+
+    let map = {};
+    let index = 1;
+    // Iterate through each anomaly
+    yield Object.keys(anomalyMapping).some(function(metric) {
+      Object.keys(anomalyMapping[metric].items).some(function(alert) {
+        anomalyMapping[metric].items[alert].items.forEach(async (item) => {
+
+          const anomaly = item.anomaly;
+          const metricName = get(anomaly, 'metricName');
+          const metricId = get(anomaly, 'metricId');
+          const functionName = get(anomaly, 'functionName');
+          const functionId = get(anomaly, 'functionId');
+
+          const dimensions = get(anomaly, 'dimensions');
+          const start = get(anomaly, 'start');
+          const end = get(anomaly, 'end');
+
+          if (!map[metricName]) {
+            map[metricName] = { 'metricId': metricId, items: {}, count: index };
+            index++;
+          }
+
+          if(!map[metricName].items[functionName]) {
+            map[metricName].items[functionName] = { 'functionId': functionId, items: [] };
+          }
+
+          const filteredDimensions = Object.keys(dimensions).map(key => [key, '=', dimensions[key]]);
+          //build new urn
+          const metricUrn = appendFilters(`thirdeye:metric:${metricId}`, filteredDimensions);
+          //Get all in the following order - current,wo2w,median4w
+          const offsets = await fetch(`/rootcause/metric/aggregate/batch?urn=${metricUrn}&start=${start}&end=${end}&offsets=wo1w,wo2w,median4w&timezone=America/Los_Angeles`).then(checkStatus).then(res => res);
+
+          const current = get(anomaly, 'current');
+          const wow = humanizeFloat(offsets[0]);
+          const wo2w = humanizeFloat(offsets[1]);
+          const median4w = humanizeFloat(offsets[2]);
+          const wowChange = floatToPercent(Number((current - offsets[0]) / offsets[0]));
+          const wo2wChange = floatToPercent(Number((current - offsets[1]) / offsets[1]));
+          const median4wChange = floatToPercent(Number((current - offsets[2]) / offsets[2]));
+          const wowHumanizeChange = humanizeChange(Number((current - offsets[0]) / offsets[0]));
+          const wo2wHumanizeChange = humanizeChange(Number((current - offsets[1]) / offsets[1]));
+          const median4wHumanizeChange = humanizeChange(Number((current - offsets[2]) / offsets[2]));
+
+          set(anomaly, 'offsets',  offsets ? {
+            'wow': { value: wow, change: wowChange, humanizedChangeDisplay: wowHumanizeChange },
+            'wo2w': { value: wo2w, change: wo2wChange, humanizedChangeDisplay: wo2wHumanizeChange },
+            'median4w': { value: median4w, change: median4wChange, humanizedChangeDisplay: median4wHumanizeChange }
+          } : {
+            'wow': '-',
+            'wo2w': '-',
+            'median4w': '-'
+          });
+
+          map[metricName].items[functionName].items.push(item);
+        });
+      });
+    });
+    // return updated anomalyMapping
     return anomalyMapping;
   }).drop(),
 
diff --git a/thirdeye/thirdeye-frontend/app/pods/home/share-dashboard/template.hbs b/thirdeye/thirdeye-frontend/app/pods/home/share-dashboard/template.hbs
index 804262c..d54f9f3 100644
--- a/thirdeye/thirdeye-frontend/app/pods/home/share-dashboard/template.hbs
+++ b/thirdeye/thirdeye-frontend/app/pods/home/share-dashboard/template.hbs
@@ -54,7 +54,7 @@
       <h4 style="color: rgba(0,0,0,0.9);font-size: 18px;font-weight: bold;">Configure summary</h4>
       <div>
         <div style="margin-left: 15px;display: inline-block;">
-          <p><input type="checkbox" {{action 'toggleSelection' "dashboard_summary" on="change"}}> Add dashboard-level comments</p>
+          <p><input type="checkbox" checked={{showDashboardSummary}} {{action 'toggleSelection' "dashboard_summary" on="change"}}> Add dashboard-level comments</p>
           {{#if showDashboardSummary}}
             <p>
               <textarea {{action 'updateComment' "dashboard_summary" on="change"}} value="{{dashboard_summary_comment}}" placeholder="Comment for dashboard summary" name="dashboard_summary" id="dashboard_summary" style="border: none;border-bottom: 1px solid grey;margin-left: 15px;width: 800px;">
@@ -80,20 +80,20 @@
       <!-- Customize email template -->
       <div>
         <div style="margin-left: 15px;display: inline-block;">
-          <p><input type="checkbox" {{action 'toggleSelection' "customize_email" on="change"}}> Customize email template</p>
+          <p><input type="checkbox" checked={{showCustomizeEmailTemplate}} {{action 'toggleSelection' "customize_email" on="change"}}> Customize email template</p>
           <p style="margin-left: 15px;">
             {{#if showCustomizeEmailTemplate}}
               {{!-- <select onchange={{action (mut field.value) value="target.value"}} class={{field.className}}> --}}
               Compare current with
               <select id="customizeEmail1" onchange={{action "onCustomizeEmail1" value="target.value"}}>
               {{#each options as |option|}}
-                <option value={{option.value}}>{{option.label}}</option>
+                <option value={{option.value}} selected={{option.selected}}>{{option.label}}</option>
               {{/each}}
               </select>
               and
               <select id="customizeEmail2" onchange={{action "onCustomizeEmail2" value="target.value"}}>
               {{#each options_two as |option|}}
-                <option value={{option.value}}>{{option.label}}</option>
+                <option value={{option.value}} selected={{option.selected}}>{{option.label}}</option>
               {{/each}}
               </select>
             {{/if}}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@pinot.apache.org
For additional commands, e-mail: commits-help@pinot.apache.org