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 0e7d657  [TE] frontend - aaronucsd/Added the new preview pills and 
performance header (#3792)
0e7d657 is described below

commit 0e7d6573dd35dcc52e038629884cb9cfc6be3546
Author: Long Huynh <[email protected]>
AuthorDate: Tue Feb 5 12:24:15 2019 -0800

    [TE] frontend - aaronucsd/Added the new preview pills and performance 
header (#3792)
    
    1. Added pills to preview
    2. Added performance stats header
    3. Added loading message on alert preview updates
    4. Added error message preview
    5. Added refresh on alert changed message and refresh button
---
 .../app/pods/components/alert-details/component.js | 217 +++++++++++++++++++++
 .../app/pods/components/alert-details/template.hbs |  46 +++++
 .../app/pods/components/stats-cards/template.hbs   |  13 +-
 .../app/pods/components/yaml-editor/component.js   |  14 +-
 .../app/pods/components/yaml-editor/template.hbs   |  12 +-
 .../thirdeye-frontend/app/pods/home/index/route.js |   2 +-
 .../app/pods/self-serve/create-alert/template.hbs  |   3 +-
 .../pods/components/stats-cards/component-test.js  |  14 --
 8 files changed, 289 insertions(+), 32 deletions(-)

diff --git 
a/thirdeye/thirdeye-frontend/app/pods/components/alert-details/component.js 
b/thirdeye/thirdeye-frontend/app/pods/components/alert-details/component.js
new file mode 100644
index 0000000..95d7e23
--- /dev/null
+++ b/thirdeye/thirdeye-frontend/app/pods/components/alert-details/component.js
@@ -0,0 +1,217 @@
+/**
+ * This component displays the alert details. It would be used in places like 
Alert Details, and Preview pages/modules.
+ * @module components/alert-details
+ * @property {Object} alertYaml - the alert yaml
+ * @property {boolean} disableYamlSave  - detect flag for yaml changes
+ * @example
+   {{#alert-details
+     alertYaml=alertYaml
+     disableYamlSave=disableYamlSave
+   }}
+     {{yield}}
+   {{/alert-details}}
+ * @exports alert-details
+ */
+
+import Component from '@ember/component';
+import { computed, observer, setProperties, set, get } from '@ember/object';
+import { inject as service } from '@ember/service';
+import { task } from 'ember-concurrency';
+import floatToPercent from 'thirdeye-frontend/utils/float-to-percent';
+import { setUpTimeRangeOptions } from 
'thirdeye-frontend/utils/manage-alert-utils';
+import moment from 'moment';
+
+const TIME_PICKER_INCREMENT = 5; // tells date picker hours field how 
granularly to display time
+const DEFAULT_ACTIVE_DURATION = '1m'; // setting this date range selection as 
default (Last 24 Hours)
+const UI_DATE_FORMAT = 'MMM D, YYYY hh:mm a'; // format for date picker to use 
(usually varies by route or metric)
+const DISPLAY_DATE_FORMAT = 'YYYY-MM-DD HH:mm'; // format used consistently 
across app to display custom date range
+const TIME_RANGE_OPTIONS = ['1m', '3m'];
+
+export default Component.extend({
+  anomaliesApiService: service('services/api/anomalies'),
+  notifications: service('toast'),
+  anomalyMapping: {},
+  analysisRange: [moment().subtract(1, 'month').startOf('hour').valueOf(), 
moment().startOf('hour').valueOf()],
+  displayRange: [moment().subtract(2, 'month').startOf('hour').valueOf(), 
moment().startOf('hour').valueOf()],
+  isPendingData: false,
+
+  alertYamlChanged: observer('alertYaml', 'analysisRange', 'disableYamlSave', 
async function() {
+    set(this, 'isPendingData', true);
+    // deal with the change
+    const alertYaml = get(this, 'alertYaml');
+    if(alertYaml) {
+      try {
+        const anomalyMapping = await 
this.get('_getAnomalyMapping').perform(alertYaml);
+        set(this, 'isPendingData', false);
+        set(this, 'anomalyMapping', anomalyMapping);
+      } catch (error) {
+        throw new Error(`Unable to retrieve anomaly data. ${error}`);
+      }
+    }
+  }),
+
+  /**
+   * Stats to display in cards
+   * @type {Object[]} - array of objects, each of which represents a stats card
+   */
+  stats: computed(
+    'anomalyMapping',
+    function() {
+      const anomalyMapping = get(this, 'anomalyMapping');
+      if (!anomalyMapping) {
+        return {};
+      }
+      let respondedAnomaliesCount = 0;
+      let truePositives = 0;
+      let falsePositives = 0;
+      let falseNegatives = 0;
+      let numberOfAnomalies = 0;
+
+      Object.keys(anomalyMapping).forEach(function (key) {
+        anomalyMapping[key].forEach(function (attr) {
+          numberOfAnomalies++;
+          if(attr.anomaly && attr.anomaly.data) {
+            const classification = attr.anomaly.data.classification;
+            if (classification != 'NONE') {
+              respondedAnomaliesCount++;
+              if (classification == 'TRUE_POSITIVE') {
+                truePositives++;
+              } else if (classification == 'FALSE_POSITIVE') {
+                falsePositives++;
+              } else if (classification == 'FALSE_NEGATIVE') {
+                falseNegatives++;
+              }
+            }
+          }
+        });
+      });
+
+      const totalAnomaliesCount = numberOfAnomalies;
+      const totalAlertsDescription = 'Total number of anomalies that occured 
over a period of time';
+      let statsArray = [];
+      if(respondedAnomaliesCount > 0) {
+        const responseRate = respondedAnomaliesCount / totalAnomaliesCount;
+        const precision = truePositives / (truePositives + falsePositives);
+        const recall = truePositives / (truePositives + falseNegatives);
+        const responseRateDescription = '% of anomalies that are reviewed';
+        const precisionDescription = '% of all anomalies detected by the 
system that are true';
+        const recallDescription = '% of all anomalies detected by the system';
+        statsArray = [
+          ['Anomalies', totalAlertsDescription, totalAnomaliesCount, 'digit'],
+          ['Response Rate', responseRateDescription, 
floatToPercent(responseRate), 'percent'],
+          ['Precision', precisionDescription, floatToPercent(precision), 
'percent'],
+          ['Recall', recallDescription, floatToPercent(recall), 'percent']
+        ];
+      } else {
+        statsArray = [
+          ['Anomalies', totalAlertsDescription, totalAnomaliesCount, 'digit']
+        ];
+      }
+      return statsArray;
+    }
+  ),
+
+  /**
+   * Date types to display in the pills
+   * @type {Object[]} - array of objects, each of which represents each date 
pill
+   */
+  pill: computed(
+    'analysisRange', 'startDate', 'endDate' ,'duration',
+    function() {
+      const analysisRange = get(this, 'analysisRange');
+      const startDate = Number(analysisRange[0]) || Number(get(this, 
'startDate'));
+      const endDate = Number(analysisRange[1]) || Number(get(this, 'endDate'));
+      const duration = get(this, 'duration') || DEFAULT_ACTIVE_DURATION;
+      const predefinedRanges = {
+        'Today': [moment().startOf('day'), moment()],
+        'Last 24 hours': [moment().subtract(1, 'day'), moment()],
+        'Yesterday': [moment().subtract(1, 'day').startOf('day'), 
moment().subtract(1, 'days').endOf('day')],
+        'Last Week': [moment().subtract(1, 'week'), moment()]
+      };
+
+      return {
+        uiDateFormat: UI_DATE_FORMAT,
+        activeRangeStart: moment(startDate).format(DISPLAY_DATE_FORMAT),
+        activeRangeEnd: moment(endDate).format(DISPLAY_DATE_FORMAT),
+        timeRangeOptions: setUpTimeRangeOptions(TIME_RANGE_OPTIONS, duration),
+        timePickerIncrement: TIME_PICKER_INCREMENT,
+        predefinedRanges
+      };
+    }
+  ),
+
+  _getAnomalyMapping: task (function * (alertYaml) {//TODO: need to add to 
anomaly util - LH
+    let anomalyMapping = {};
+    const analysisRange = get(this, 'analysisRange');
+    const postProps = {
+      method: 'POST',
+      body: alertYaml,
+      headers: { 'content-type': 'text/plain' }
+    };
+    const notifications = get(this, 'notifications');
+
+    //detection alert fetch
+    const start = analysisRange[0] || '1548489600000';
+    const end = analysisRange[1] || '1548748800000';
+    const alertUrl = 
`yaml/preview?start=${start}&end=${end}&tuningStart=0&tuningEnd=0`;
+    try {
+      const alert_result = yield fetch(alertUrl, postProps);
+      const alert_status  = get(alert_result, 'status');
+      const applicationAnomalies = yield alert_result.json();
+
+      if (alert_status !== 200 && applicationAnomalies.message) {
+        notifications.error(applicationAnomalies.message, 'Preview alert 
failed');
+      } else {
+        const anomalies = applicationAnomalies.anomalies;
+        if (anomalies && anomalies.length > 0) {
+          const humanizedObject = {
+            queryDuration: '1m',
+            queryStart: start,
+            queryEnd: end
+          };
+          this.set('applicationAnomalies', anomalies);
+
+          anomalies.forEach(anomaly => {
+            const metricName = anomaly.metric;
+            //Grouping the anomalies of the same metric name
+            if (!anomalyMapping[metricName]) {
+              anomalyMapping[metricName] = [];
+            }
+
+            // Group anomalies by metricName and function name (alertName) and 
wrap it into the Humanized cache. Each `anomaly` is the raw data from ember 
data cache.
+            
anomalyMapping[metricName].push(this.get('anomaliesApiService').getHumanizedEntity(anomaly,
 humanizedObject));
+          });
+        }
+      }
+    } catch (error) {
+      notifications.error('Preview alert failed', error);
+    }
+
+    return anomalyMapping;
+  }).drop(),
+
+  actions: {
+    /**
+     * Sets the new custom date range for anomaly coverage
+     * @method onRangeSelection
+     * @param {Object} rangeOption - the user-selected time range to load
+     */
+    onRangeSelection(timeRangeOptions) {
+      const {
+        start,
+        end,
+        value: duration
+      } = timeRangeOptions;
+
+      const startDate = moment(start).valueOf();
+      const endDate = moment(end).valueOf();
+      //Update the time range option selected
+      set(this, 'analysisRange', [startDate, endDate]);
+      set(this, 'duration', duration)
+    },
+
+    refreshPreview(){
+      set(this, 'disableYamlSave', true);
+    }
+  }
+});
diff --git 
a/thirdeye/thirdeye-frontend/app/pods/components/alert-details/template.hbs 
b/thirdeye/thirdeye-frontend/app/pods/components/alert-details/template.hbs
new file mode 100644
index 0000000..ac752de
--- /dev/null
+++ b/thirdeye/thirdeye-frontend/app/pods/components/alert-details/template.hbs
@@ -0,0 +1,46 @@
+<div class="alert-details">
+  {{#if isPendingData}}
+    <div>
+      {{ember-spinner scale=0.5 rotate=10 speed='1.1' color='#3498DB'}}Please 
wait while we compiled the data.
+    </div>
+  {{else}}
+    {{#if disableYamlSave}}
+      {{range-pill-selectors
+        title="Showing"
+        uiDateFormat=pill.uiDateFormat
+        activeRangeEnd=pill.activeRangeEnd
+        activeRangeStart=pill.activeRangeStart
+        timeRangeOptions=pill.timeRangeOptions
+        timePickerIncrement=pill.timePickerIncrement
+        predefinedRanges=pill.predefinedRanges
+        selectAction=(action "onRangeSelection")
+      }}
+      <div class="te-horizontal-cards">
+        <h4 class="te-self-serve__block-title">
+          <label for="select-dimension" class="control-label te-label">
+            Alert Performance
+            <span>
+              <i class="glyphicon glyphicon-question-sign"></i>
+              {{#tooltip-on-element class="te-tooltip"}}
+                All estimated performance numbers are based on reviewed 
anomalies.
+              {{/tooltip-on-element}}
+            </span>
+          </label>
+        </h4>
+        <div class="te-horizontal-cards__container">
+          {{!-- Alert anomaly stats cards --}}
+          {{stats-cards stats=stats}}
+        </div>
+        {{#if repRunStatus}}
+          <p class="te-self-serve__block-subtext 
te-self-serve__block-subtext--normal">Replay in progress. Please check back 
later...</p>
+        {{/if}}
+      </div>
+    {{else}}
+      <div class="yaml-editor-msg">Alert configuration has changed.</div>
+      <label class="control-label te-label">
+        <a {{action "refreshPreview"}}>Refresh Preview</a>
+      </label>
+    {{/if}}
+  {{/if}}
+  {{yield}}
+</div>
diff --git 
a/thirdeye/thirdeye-frontend/app/pods/components/stats-cards/template.hbs 
b/thirdeye/thirdeye-frontend/app/pods/components/stats-cards/template.hbs
index 1c9c004..f1d0de4 100644
--- a/thirdeye/thirdeye-frontend/app/pods/components/stats-cards/template.hbs
+++ b/thirdeye/thirdeye-frontend/app/pods/components/stats-cards/template.hbs
@@ -1,7 +1,16 @@
 {{#each statsTransformed as |card|}}
   <ul class="te-horizontal-cards__card">
-    <li class="te-horizontal-cards__card-title">{{card.title}}</li>
-    <li class="te-horizontal-cards__card-text">{{card.description}}</li>
+    <li class="te-horizontal-cards__card-title">
+      <label for="select-dimension" class="control-label te-label">
+        {{card.title}}
+        <span>
+          <i class="glyphicon glyphicon-question-sign"></i>
+          {{#tooltip-on-element class="te-tooltip"}}
+            {{card.description}}
+          {{/tooltip-on-element}}
+        </span>
+      </label>
+    </li>
     <li class="te-horizontal-cards__card-number">
       {{card.value}}{{#if (eq card.unit 'percent')}}%{{/if}}
     </li>
diff --git 
a/thirdeye/thirdeye-frontend/app/pods/components/yaml-editor/component.js 
b/thirdeye/thirdeye-frontend/app/pods/components/yaml-editor/component.js
index a5423db..1c55b0a 100644
--- a/thirdeye/thirdeye-frontend/app/pods/components/yaml-editor/component.js
+++ b/thirdeye/thirdeye-frontend/app/pods/components/yaml-editor/component.js
@@ -63,7 +63,6 @@ export default Component.extend({
 
   init() {
     this._super(...arguments);
-
     if(get(this, 'isEditMode')) {
       set(this, 'currentYamlAlertOriginal', get(this, 'alertYaml') || 
get(this, 'yamlAlertProps'));
       set(this, 'currentYamlSettingsOriginal', get(this, 
'detectionSettingsYaml') || get(this, 'yamlAlertSettings'));
@@ -118,8 +117,8 @@ export default Component.extend({
   ),
 
   _fetchSubscriptionGroups: task(function* () {
-    // /detection/subscription-groups
-    const url2 = `/detection/subscription-groups`;//dropdown of subscription 
groups
+    //dropdown of subscription groups
+    const url2 = `/detection/subscription-groups`;
     const postProps2 = {
       method: 'get',
       headers: { 'content-type': 'application/json' }
@@ -129,8 +128,6 @@ export default Component.extend({
     try {
       const response = yield fetch(url2, postProps2);
       const json = yield response.json();
-      //filter subscription groups with yaml
-      //set(this, 'subscriptionGroupNames', json.filterBy('yaml'));
       return json.filterBy('yaml');
     } catch (error) {
       notifications.error('Failed to retrieve subscription groups.', 'Error');
@@ -232,13 +229,10 @@ export default Component.extend({
   },
 
   actions: {
-    /**
-    * triggered by preview dropdown
-    */
-    showPreview() {
+    getPreview(){
+      set(this, 'alertYaml', get(this, 'currentYamlAlert')),
       this.toggleProperty('toggleCollapsed');
     },
-
     /**
      * resets given yaml field to default value for creation mode and server 
value for edit mode
      */
diff --git 
a/thirdeye/thirdeye-frontend/app/pods/components/yaml-editor/template.hbs 
b/thirdeye/thirdeye-frontend/app/pods/components/yaml-editor/template.hbs
index 10ad9d8..fcf4f1d 100644
--- a/thirdeye/thirdeye-frontend/app/pods/components/yaml-editor/template.hbs
+++ b/thirdeye/thirdeye-frontend/app/pods/components/yaml-editor/template.hbs
@@ -37,17 +37,21 @@
       mode="ace/mode/yaml"
     }}
   </div>
-
   <div class="col-xs-12">
-    {{#bs-accordion onChange=(action "showPreview") as |acc|}}
+    {{#bs-accordion onChange=(action 'getPreview') as |acc|}}
       {{#acc.item value=preview as |aitem|}}
         {{#aitem.title}}
-          <section class="dashboard-container__title">Preview alert
+          <section class="dashboard-container__title">Preview alert {{#unless 
disableYamlSave}} / Alert configuration has changed.{{/unless}}
             <span class="pull-right"><i class="glyphicon glyphicon-menu-{{if 
toggleCollapsed "down" "up"}}"></i></span>
           </section>
         {{/aitem.title}}
         {{#aitem.body}}
-          <p>Preview Alert Component Goes Here</p>
+          {{#alert-details
+            alertYaml=alertYaml
+            disableYamlSave=disableYamlSave
+          }}
+            {{yield}}
+          {{/alert-details}}
         {{/aitem.body}}
       {{/acc.item}}
     {{/bs-accordion}}
diff --git a/thirdeye/thirdeye-frontend/app/pods/home/index/route.js 
b/thirdeye/thirdeye-frontend/app/pods/home/index/route.js
index b520eb1..a62b12e 100644
--- a/thirdeye/thirdeye-frontend/app/pods/home/index/route.js
+++ b/thirdeye/thirdeye-frontend/app/pods/home/index/route.js
@@ -82,7 +82,7 @@ export default Route.extend(AuthenticatedRouteMixin, {
 
     return new RSVP.Promise(async (resolve, reject) => {
       try {
-        const anomalyMapping = appName ? await 
this.get('_getAnomalyMapping').perform(model) : [];//DEMO:
+        const anomalyMapping = appName ? await 
this.get('_getAnomalyMapping').perform(model) : [];
         const alertsByMetric = appName ? this.getAlertsByMetric() : [];
         const defaultParams = {
           anomalyMapping,
diff --git 
a/thirdeye/thirdeye-frontend/app/pods/self-serve/create-alert/template.hbs 
b/thirdeye/thirdeye-frontend/app/pods/self-serve/create-alert/template.hbs
index 467360e..9e41d86 100644
--- a/thirdeye/thirdeye-frontend/app/pods/self-serve/create-alert/template.hbs
+++ b/thirdeye/thirdeye-frontend/app/pods/self-serve/create-alert/template.hbs
@@ -420,10 +420,11 @@
       {{/if}}
     </fieldset>
   {{else}}
-    {{yaml-editor
+    {{#yaml-editor
       isEditMode=false
       showSettings=true
     }}
+    {{/yaml-editor}}
   {{/if}}
   {{outlet}}
 </main>
diff --git 
a/thirdeye/thirdeye-frontend/tests/integration/pods/components/stats-cards/component-test.js
 
b/thirdeye/thirdeye-frontend/tests/integration/pods/components/stats-cards/component-test.js
index ec0a11f..6b04b9f 100644
--- 
a/thirdeye/thirdeye-frontend/tests/integration/pods/components/stats-cards/component-test.js
+++ 
b/thirdeye/thirdeye-frontend/tests/integration/pods/components/stats-cards/component-test.js
@@ -36,20 +36,6 @@ module('Integration | Component | stats cards', 
function(hooks) {
       stats[2][0],
       'title of 3rd card is correct');
 
-    // Testing descriptions of all cards
-    assert.equal(
-      $description.get(0).innerText.trim(),
-      stats[0][1],
-      'description of 1st card is correct');
-    assert.equal(
-      $description.get(1).innerText.trim(),
-      stats[1][1],
-      'description of 2nd card is correct');
-    assert.equal(
-      $description.get(2).innerText.trim(),
-      stats[2][1],
-      'description of 3rd card is correct');
-
     // Testing values of all cards
     assert.equal(
       $number.get(0).innerText.trim(),


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to