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

[incubator-pinot] branch master updated: [TE] frontend - harleyjj/yaml-editor - implements simple graph for preview (#3787)

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

jihao 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 280c192  [TE] frontend - harleyjj/yaml-editor - implements simple graph for preview (#3787)
280c192 is described below

commit 280c192a86f6f66a75a3c4884f864d6bf4be1347
Author: Harley Jackson <ha...@gmail.com>
AuthorDate: Wed Feb 6 14:08:58 2019 -0800

    [TE] frontend - harleyjj/yaml-editor - implements simple graph for preview (#3787)
    
    implements simple graph for preview
    adds graph and table, limits yaml/preview calls
---
 .../app/pods/components/alert-details/component.js | 378 +++++++++++++++++++--
 .../app/pods/components/alert-details/template.hbs | 218 +++++++++++-
 .../app/pods/components/anomaly-graph/component.js |  95 +-----
 .../app/pods/components/yaml-editor/component.js   |   9 +-
 .../app/pods/components/yaml-editor/template.hbs   |   4 +-
 5 files changed, 582 insertions(+), 122 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
index 95d7e23..34259fa 100644
--- a/thirdeye/thirdeye-frontend/app/pods/components/alert-details/component.js
+++ b/thirdeye/thirdeye-frontend/app/pods/components/alert-details/component.js
@@ -14,13 +14,19 @@
  */
 
 import Component from '@ember/component';
-import { computed, observer, setProperties, set, get } from '@ember/object';
+import { computed, observer, set, get, getProperties } from '@ember/object';
+import { later } from '@ember/runloop';
+import { checkStatus, humanizeFloat } from 'thirdeye-frontend/utils/utils';
+import { colorMapping, toColor, makeTime } from 'thirdeye-frontend/utils/rca-utils';
 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';
+import _ from 'lodash';
+import d3 from 'd3';
 
+const TABLE_DATE_FORMAT = 'MMM DD, hh:mm A'; // format for anomaly table
 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)
@@ -31,24 +37,203 @@ export default Component.extend({
   anomaliesApiService: service('services/api/anomalies'),
   notifications: service('toast'),
   anomalyMapping: {},
+  timeseries: null,
+  isLoading: false,
   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,
+  colorMapping: colorMapping,
+  zoom: {
+    enabled: true,
+    rescale: true
+  },
 
-  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}`);
+  legend: {
+    show: true,
+    position: 'right'
+  },
+  errorTimeseries: null,
+  metricUrn: null,
+  errorBaseline: null,
+  compareMode: 'wo1w',
+  baseline: null,
+  errorAnomalies: null,
+  showPreview: false,
+  componentId: 'timeseries-chart',
+  anomalies: null,
+  baselineOptions: [
+    { name: 'wo1w', isActive: true},
+    { name: 'wo2w', isActive: false},
+    { name: 'wo3w', isActive: false},
+    { name: 'wo4w', isActive: false},
+    { name: 'mean4w', isActive: false},
+    { name: 'median4w', isActive: false},
+    { name: 'min4w', isActive: false},
+    { name: 'max4w', isActive: false},
+    { name: 'none', isActive: false}
+  ],
+  sortColumnStartUp: false,
+  sortColumnScoreUp: false,
+  sortColumnChangeUp: false,
+  sortColumnNumberUp: true,
+  sortColumnResolutionUp: false,
+  selectedSortMode: '',
+  selectedBaseline: 'wo1w',
+
+  // 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);
+  //       debugger;
+  //     } catch (error) {
+  //       throw new Error(`Unable to retrieve anomaly data. ${error}`);
+  //     }
+  //   }
+  // }),
+
+  disablePreviewButton: computed(
+    'alertYaml',
+    'isLoading',
+    function() {
+      return (get(this, 'alertYaml') === null || get(this, 'isLoading') === true);
+    }
+  ),
+
+  axis: computed(
+    'displayRange',
+    function () {
+      const displayRange = getProperties(this, 'displayRange');
+
+      return {
+        y: {
+          show: true,
+          tick: {
+            format: function(d){return humanizeFloat(d);}
+          }
+        },
+        y2: {
+          show: false,
+          min: 0,
+          max: 1
+        },
+        x: {
+          type: 'timeseries',
+          show: true,
+          min: displayRange[0],
+          max: displayRange[1],
+          tick: {
+            fit: false,
+            format: (d) => {
+              const t = makeTime(d);
+              if (t.valueOf() === t.clone().startOf('day').valueOf()) {
+                return t.format('MMM D (ddd)');
+              }
+              return t.format('h:mm a');
+            }
+          }
+        }
+      };
+    }
+  ),
+
+  series: computed(
+    'anomalies',
+    'timeseries',
+    'baseline',
+    'analysisRange',
+    'displayRange',
+    function () {
+      const {
+        metricUrn, anomalies, timeseries, baseline, analysisRange, displayRange
+      } = getProperties(this, 'metricUrn', 'anomalies', 'timeseries',
+        'baseline', 'analysisRange', 'displayRange');
+
+      const series = {};
+
+      if (!_.isEmpty(anomalies)) {
+
+          anomalies
+            .filter(anomaly => anomaly.metricUrn === metricUrn)
+            .forEach(anomaly => {
+              const key = this._formatAnomaly(anomaly);
+              series[key] = {
+                timestamps: [anomaly.startTime, anomaly.endTime],
+                values: [1, 1],
+                type: 'line',
+                color: 'teal',
+                axis: 'y2'
+              };
+              series[key + '-region'] = Object.assign({}, series[key], {
+                type: 'region',
+                color: 'orange'
+              });
+            });
       }
+
+      if (timeseries && !_.isEmpty(timeseries.value)) {
+        series['current'] = {
+          timestamps: timeseries.timestamp,
+          values: timeseries.value,
+          type: 'line',
+          color: toColor(metricUrn)
+        };
+      }
+
+      if (baseline && !_.isEmpty(baseline.value)) {
+        series['baseline'] = {
+          timestamps: baseline.timestamp,
+          values: baseline.value,
+          type: 'line',
+          color: 'light-' + toColor(metricUrn)
+        };
+      }
+
+      // detection range
+      if (timeseries && !_.isEmpty(timeseries.value)) {
+        series['pre-detection-region'] = {
+          timestamps: [displayRange[0], analysisRange[0]],
+          values: [1, 1],
+          type: 'region',
+          color: 'grey'
+        };
+      }
+
+      return series;
+    }
+  ),
+
+  /**
+   * formats anomalies for table
+   * @method tableAnomalies
+   * @return {Array}
+   */
+  tableAnomalies: computed(
+    'anomalies',
+    function() {
+      const anomalies = get(this, 'anomalies');
+      let tableData = [];
+      let i = 1;
+
+      anomalies.forEach(a => {
+        let tableRow = {
+          index: i,
+          startDateStr: this._formatAnomaly(a),
+          severityScore: a.score,
+          shownCurrent: humanizeFloat(a.avgCurrentVal),
+          shownBaseline: humanizeFloat(a.avgBaselineVal),
+          shownChangeRate: humanizeFloat(((a.avgCurrentVal/a.avgBaselineVal - 1.0) * 100.0))
+        };
+        tableData.push(tableRow);
+        i++;
+      });
+      return tableData;
     }
-  }),
+  ),
 
   /**
    * Stats to display in cards
@@ -116,7 +301,7 @@ export default Component.extend({
    * @type {Object[]} - array of objects, each of which represents each date pill
    */
   pill: computed(
-    'analysisRange', 'startDate', 'endDate' ,'duration',
+    'analysisRange', 'startDate', 'endDate', 'duration',
     function() {
       const analysisRange = get(this, 'analysisRange');
       const startDate = Number(analysisRange[0]) || Number(get(this, 'startDate'));
@@ -151,9 +336,10 @@ export default Component.extend({
     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`;
+    const start = analysisRange[0];
+    const end = analysisRange[1];
+    const alertUrl = `/yaml/preview?start=${start}&end=${end}&tuningStart=0&tuningEnd=0`;
+    let anomalies;
     try {
       const alert_result = yield fetch(alertUrl, postProps);
       const alert_status  = get(alert_result, 'status');
@@ -162,7 +348,9 @@ export default Component.extend({
       if (alert_status !== 200 && applicationAnomalies.message) {
         notifications.error(applicationAnomalies.message, 'Preview alert failed');
       } else {
-        const anomalies = applicationAnomalies.anomalies;
+        anomalies = applicationAnomalies.anomalies;
+        set(this, 'metricUrn', Object.keys(applicationAnomalies.diagnostics['0'])[0]);
+
         if (anomalies && anomalies.length > 0) {
           const humanizedObject = {
             queryDuration: '1m',
@@ -187,9 +375,109 @@ export default Component.extend({
       notifications.error('Preview alert failed', error);
     }
 
-    return anomalyMapping;
+    return {
+      anomalyMapping,
+      anomalies
+    };
   }).drop(),
 
+  didRender(){
+    this._super(...arguments);
+
+    later(() => {
+      this._buildSliderButton();
+    });
+  },
+
+  // Helper function that builds the subchart region buttons
+  _buildSliderButton() {
+    const componentId = this.get('componentId');
+    const resizeButtons = d3.select(`.${componentId}`).selectAll('.resize');
+
+    resizeButtons.append('circle')
+      .attr('cx', 0)
+      .attr('cy', 30)
+      .attr('r', 10)
+      .attr('fill', '#0091CA');
+    resizeButtons.append('line')
+      .attr('class', 'anomaly-graph__slider-line')
+      .attr("x1", 0)
+      .attr("y1", 27)
+      .attr("x2", 0)
+      .attr("y2", 33);
+
+    resizeButtons.append('line')
+      .attr('class', 'anomaly-graph__slider-line')
+      .attr("x1", -5)
+      .attr("y1", 27)
+      .attr("x2", -5)
+      .attr("y2", 33);
+
+    resizeButtons.append('line')
+      .attr('class', 'anomaly-graph__slider-line')
+      .attr("x1", 5)
+      .attr("y1", 27)
+      .attr("x2", 5)
+      .attr("y2", 33);
+  },
+
+  _formatAnomaly(anomaly) {
+    return `${moment(anomaly.startTime).format(TABLE_DATE_FORMAT)}`;
+  },
+
+  _filterAnomalies(rows) {
+    return rows.filter(row => (row.startTime && row.endTime && !row.child));
+  },
+
+  _fetchTimeseries() {
+    const {
+      metricUrn,
+      displayRange,
+      selectedBaseline
+    } = this.getProperties('metricUrn', 'displayRange', 'selectedBaseline');
+    const granularity = '15_MINUTES';
+    const timezone = moment.tz.guess();
+
+    set(this, 'errorTimeseries', null);
+
+    const urlCurrent = `/rootcause/metric/timeseries?urn=${metricUrn}&start=${displayRange[0]}&end=${displayRange[1]}&offset=current&granularity=${granularity}&timezone=${timezone}`;
+    fetch(urlCurrent)
+      .then(checkStatus)
+      .then(res => {
+        this.setProperties({
+          timeseries: res,
+          isLoading: false
+        });
+      });
+
+    set(this, 'errorBaseline', null);
+
+    const urlBaseline = `/rootcause/metric/timeseries?urn=${metricUrn}&start=${displayRange[0]}&end=${displayRange[1]}&offset=${selectedBaseline}&granularity=${granularity}&timezone=${timezone}`;
+    fetch(urlBaseline)
+      .then(checkStatus)
+      .then(res => set(this, 'baseline', res));
+  },
+
+  _fetchAnomalies() {
+    set(this, 'errorAnomalies', null);
+
+    try {
+      const content = get(this, 'alertYaml');
+      this.get('_getAnomalyMapping').perform(content)
+      .then(results => {
+        this.setProperties({
+          anomalyMapping: results.anomalyMapping,
+          anomalies: results.anomalies,
+          isLoading: false
+        });
+        this._fetchTimeseries();
+      });
+    } catch (error) {
+      set(this, 'isLoading', false);
+      throw new Error(`Unable to retrieve anomaly data. ${error}`);
+    }
+  },
+
   actions: {
     /**
      * Sets the new custom date range for anomaly coverage
@@ -207,7 +495,57 @@ export default Component.extend({
       const endDate = moment(end).valueOf();
       //Update the time range option selected
       set(this, 'analysisRange', [startDate, endDate]);
-      set(this, 'duration', duration)
+      set(this, 'duration', duration);
+    },
+
+    /**
+    * triggered by preview button
+    */
+    getPreview() {
+      this.setProperties({
+        isLoading: true,
+        showPreview: true
+      });
+      this._fetchAnomalies();
+    },
+
+    /**
+     * Handle display of selected baseline options
+     * @param {Object} clicked - the baseline selection
+     */
+    onBaselineOptionClick(clicked) {
+      const baselineOptions = get(this, 'baselineOptions');
+      const isValidSelection = !clicked.isActive;
+      let newOptions = baselineOptions.map((val) => {
+        return { name: val.name, isActive: false };
+      });
+
+      // Set active option
+      newOptions.find((val) => val.name === clicked.name).isActive = true;
+      this.set('baselineOptions', newOptions);
+
+      if(isValidSelection) {
+        set(this, 'selectedBaseline', clicked.name);
+        this._fetchTimeseries();
+      }
+    },
+
+    /**
+     * Handle sorting for each sortable table column
+     * @param {String} sortKey  - stringified start date
+     */
+    toggleSortDirection(sortKey) {
+      const propName = 'sortColumn' + sortKey.capitalize() + 'Up' || '';
+
+      this.toggleProperty(propName);
+      if (this.get(propName)) {
+        this.set('selectedSortMode', sortKey + ':up');
+      } else {
+        this.set('selectedSortMode', sortKey + ':down');
+      }
+
+      //On sort, set table to first pagination page
+      this.set('currentPage', 1);
     },
 
     refreshPreview(){
diff --git a/thirdeye/thirdeye-frontend/app/pods/components/alert-details/template.hbs b/thirdeye/thirdeye-frontend/app/pods/components/alert-details/template.hbs
index ac752de..b5533d9 100644
--- a/thirdeye/thirdeye-frontend/app/pods/components/alert-details/template.hbs
+++ b/thirdeye/thirdeye-frontend/app/pods/components/alert-details/template.hbs
@@ -1,5 +1,218 @@
 <div class="alert-details">
-  {{#if isPendingData}}
+
+  {{#unless errorAnomalies}}
+    {{#if showPreview}}
+      {{#if isLoading}}
+        {{ember-spinner scale=0.5 rotate=10 speed='1.1' color='#3498DB'}}Please wait while we compile the data.
+      {{/if}}
+      {{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>
+      {{timeseries-chart
+        series=series
+        colorMapping=colorMapping
+        axis=axis
+        zoom=zoom
+        legend=legend
+      }}
+      {{#if anomalies}}
+        {{!-- Baseline type selector --}}
+        {{range-pill-selectors
+          title="Baseline"
+          timeRangeOptions=baselineOptions
+          selectAction=(action "onBaselineOptionClick")
+        }}
+        {{!-- Alert anomaly table --}}
+        <div class="te-block-container">
+          <table class="te-anomaly-table">
+            {{#if anomalies}}
+              <thead>
+                <tr class="te-anomaly-table__row te-anomaly-table__head">
+                  <th class="te-anomaly-table__cell-head te-anomaly-table__cell-head--left">
+                    <a class="te-anomaly-table__cell-link" {{action "toggleSortDirection" "number"}}>#
+                      <i class="te-anomaly-table__icon glyphicon {{if sortColumnNumberUp "glyphicon-menu-down" "glyphicon-menu-up"}}"></i>
+                    </a>
+                  </th>
+                  <th class="te-anomaly-table__cell-head te-anomaly-table__cell-head--left">
+                    <a class="te-anomaly-table__cell-link" {{action "toggleSortDirection" "start"}}>
+                      Start/Duration (PDT)
+                      <i class="te-anomaly-table__icon glyphicon {{if sortColumnStartUp "glyphicon-menu-up" "glyphicon-menu-down"}}"></i>
+                    </a>
+                  </th>
+                  {{#if alertHasDimensions}}
+                    <th class="te-anomaly-table__cell-head te-anomaly-table__cell-head--fixed">Dimensions</th>
+                  {{/if}}
+                  <th class="te-anomaly-table__cell-head">
+                    <a class="te-anomaly-table__cell-link" {{action "toggleSortDirection" "score"}}>
+                      Severity Score
+                      <i class="te-anomaly-table__icon glyphicon {{if sortColumnScoreUp "glyphicon-menu-up" "glyphicon-menu-down"}}"></i>
+                    </a>
+                  </th>
+                  <th class="te-anomaly-table__cell-head">
+                    <a class="te-anomaly-table__cell-link" {{action "toggleSortDirection" "change"}}>
+                      Average Current / Average Predicted
+                      <i class="te-anomaly-table__icon glyphicon {{if sortColumnChangeUp "glyphicon-menu-up" "glyphicon-menu-down"}}"></i>
+                    </a>
+                  </th>
+                  {{#if notPreview}}
+                    <th class="te-anomaly-table__cell-head">
+                      <a class="te-anomaly-table__cell-link" {{action "toggleSortDirection" "resolution"}}>
+                        Resolution
+                        <i class="te-anomaly-table__icon glyphicon {{if sortColumnResolutionUp "glyphicon-menu-up" "glyphicon-menu-down"}}"></i>
+                      </a>
+                    </th>
+                    <th class="te-anomaly-table__cell-head"></th>
+                  {{/if}}
+                </tr>
+              </thead>
+            {{/if}}
+            <tbody>
+              {{#each tableAnomalies as |anomaly|}}
+                <tr class="te-anomaly-table__row">
+                   <td class="te-anomaly-table__cell te-anomaly-table__cell--index">{{anomaly.index}}</td>
+                   <td class="te-anomaly-table__cell">
+                    <ul class="te-anomaly-table__list te-anomaly-table__list--left">
+                      <li class="te-anomaly-table__list-item te-anomaly-table__list-item--stronger">
+                        {{#if notPreview}}
+                          <a target="_blank" class="te-anomaly-table__link" href="/app/#/rootcause?anomalyId={{anomaly.anomalyId}}">
+                            {{anomaly.startDateStr}}
+                          </a>
+                        {{else}}
+                          {{anomaly.startDateStr}}
+                        {{/if}}
+                      </li>
+                      <li class="te-anomaly-table__list-item te-anomaly-table__list-item--lighter">{{anomaly.durationStr}}</li>
+                    </ul>
+                   </td>
+                   {{#if alertHasDimensions}}
+                     <td class="te-anomaly-table__cell">
+                      <ul class="te-anomaly-table__list">
+                       {{#each anomaly.dimensionList as |dimension|}}
+                          <li class="te-anomaly-table__list-item te-anomaly-table__list-item--smaller" title="{{dimension.dimensionVal}}">
+                            {{dimension.dimensionKey}}: <span class="stronger">{{dimension.dimensionVal}}</span>
+                          </li>
+                       {{else}}
+                          -
+                       {{/each}}
+                      </ul>
+                     </td>
+                   {{/if}}
+                   <td class="te-anomaly-table__cell">{{anomaly.severityScore}}</td>
+                   <td class="te-anomaly-table__cell">
+                    <ul class="te-anomaly-table__list">
+                      <li>{{anomaly.shownCurrent}} / {{anomaly.shownBaseline}}</li>
+                      <li class="te-anomaly-table__value-label te-anomaly-table__value-label--{{calculate-direction anomaly.shownChangeRate}}">
+                        {{#if (not anomaly.isNullChangeRate)}}
+                          ({{anomaly.shownChangeRate}}%)
+                        {{else}}
+                          (N/A)
+                        {{/if}}
+                      </li>
+                    </ul>
+                   </td>
+                   {{#if notPreview}}
+                     <td class="te-anomaly-table__cell">
+                        {{#if renderStatusIcon}}
+                          {{#if anomaly.showResponseSaved}}
+                            <i class="te-anomaly-table__icon--status glyphicon glyphicon-ok-circle"></i>
+                          {{/if}}
+                          {{#if anomaly.showResponseFailed}}
+                            <i class="te-anomaly-table__icon--status te-anomaly-table__icon--error glyphicon glyphicon-remove-circle"></i>
+                          {{/if}}
+                        {{/if}}
+
+                        {{#if anomaly.isUserReported}}
+                          <div class="te-anomaly-table__text te-anomaly-table__text--explore">User Reported</div>
+                          <div class="te-anomaly-table__comment">
+                            <i class="glyphicon glyphicon-th-list"></i>
+                            {{#tooltip-on-element class="te-anomaly-table__tooltip"}}
+                              {{anomaly.anomalyFeedbackComments}}
+                            {{/tooltip-on-element}}
+                          </div>
+                        {{else}}
+                          {{#power-select
+                            triggerId=anomaly.anomalyId
+                            triggerClass="te-anomaly-table__select"
+                            options=responseOptions
+                            searchEnabled=false
+                            selected=(get labelMap anomaly.anomalyFeedback)
+                            onchange=(action "onChangeAnomalyResponse" anomaly)
+                            as |response|
+                          }}
+                            {{response}}
+                          {{/power-select}}
+                        {{/if}}
+                     </td>
+                     <td class="te-anomaly-table__cell te-anomaly-table__cell--feedback">
+                        <div class="te-anomaly-table__link-wrapper">
+                          {{#link-to 'rootcause' (query-params anomalyId=anomaly.anomalyId) target="_blank" class="te-anomaly-table__link"}}
+                            Investigate
+                          {{/link-to}}
+                        </div>
+                     </td>
+                   {{/if}}
+                </tr>
+              {{/each}}
+            </tbody>
+          </table>
+        </div>
+
+      {{/if}}
+      {{else}}
+        {{#if disablePreviewButton}}
+          <p>Please define anomaly detection in YAML to enable preview.</p>
+        {{/if}}
+    {{/if}}
+  {{/unless}}
+  <p>{{errorAnomalies}}</p>
+  <div class="pull-right">
+    {{bs-button
+      defaultText=(if showPreview "Refresh Preview" "Get Preview")
+      disabled=disablePreviewButton
+      type="outline-primary"
+      buttonType="refresh"
+      onClick=(action "getPreview")
+      class="te-button te-button--cancel"
+    }}
+  </div>
+  {{yield}}
+
+
+
+
+
+
+
+
+  <!-- {{#if isPendingData}}
     <div>
       {{ember-spinner scale=0.5 rotate=10 speed='1.1' color='#3498DB'}}Please wait while we compiled the data.
     </div>
@@ -31,6 +244,7 @@
           {{!-- 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}}
@@ -42,5 +256,5 @@
       </label>
     {{/if}}
   {{/if}}
-  {{yield}}
+  {{yield}} -->
 </div>
diff --git a/thirdeye/thirdeye-frontend/app/pods/components/anomaly-graph/component.js b/thirdeye/thirdeye-frontend/app/pods/components/anomaly-graph/component.js
index 396d001..3ac6d93 100644
--- a/thirdeye/thirdeye-frontend/app/pods/components/anomaly-graph/component.js
+++ b/thirdeye/thirdeye-frontend/app/pods/components/anomaly-graph/component.js
@@ -26,7 +26,8 @@ const COLOR_MAPPING = {
 export default Component.extend({
   init() {
     this._super(...arguments);
-
+    const subChartStart = this.get('subChartStart');
+    const subChartEnd = this.get('subChartEnd');
     this.setProperties({
       _subchartStart: Number(this.get('subchartStart')),
       _subchartEnd: Number(this.get('subchartEnd'))
@@ -65,94 +66,6 @@ export default Component.extend({
       .attr("y2", 33);
   },
 
-  buildAnomalyRegionSlider(start, end) {
-    const {
-      componentId,
-      regionStart,
-      regionEnd,
-      _subchartStart: subchartStart,
-      _subchartEnd: subchartEnd
-    } = this.getProperties(
-      'componentId',
-      'regionStart',
-      'regionEnd',
-      '_subchartStart',
-      '_subchartEnd');
-
-    start = start || subchartStart;
-    end = end || subchartEnd;
-
-    d3.select(`#${componentId} .anomaly-graph__region-slider`).remove();
-    if (componentId !== 'main-graph') {return;}
-
-    const focus = d3.select(`#${componentId}.c3-chart-component .c3-chart`);
-    const { height, width } = d3.select(`#${componentId} .c3-chart .c3-event-rects`).node().getBoundingClientRect();
-    const dates = this.get('primaryMetric.timeBucketsCurrent');
-    const min = start ? moment(start).valueOf() : d3.min(dates);
-    const max = end ? moment(end).valueOf() : d3.max(dates);
-
-    const x = d3.time.scale()
-      .domain([min, max])
-      .range([0, width]);
-
-    const brush = d3.svg.brush()
-      .on("brushend", brushed.bind(this))
-      .x(x)
-      .extent([+regionStart, +regionEnd]);
-
-    function brushed() {
-      const e = brush.extent();
-      const [ start, end ] = e;
-
-      const regionStart = moment(start).valueOf();
-      const regionEnd = moment(end).valueOf();
-      const subchartStart = this.get('_subchartStart');
-      const subchartEnd = this.get('_subchartEnd');
-
-      this.setProperties({
-        regionStart,
-        regionEnd,
-        subchartStart,
-        subchartEnd
-      });
-    }
-
-    focus.append('g')
-      .attr('class', 'anomaly-graph__region-slider x brush')
-      .call(brush)
-      .selectAll('rect')
-      .attr('y', 0)
-      .attr('height', height);
-
-    const resizeButton = focus.selectAll('.resize');
-    const sliderHeight = height/2;
-    resizeButton.append('circle')
-      .attr('cx', 0)
-      .attr('cy', sliderHeight)
-      .attr('r', 10)
-      .attr('fill', '#E55800');
-    resizeButton.append('line')
-      .attr('class', 'anomaly-graph__slider-line')
-      .attr("x1", 0)
-      .attr("y1", sliderHeight - 3)
-      .attr("x2", 0)
-      .attr("y2", sliderHeight + 3);
-
-    resizeButton.append('line')
-      .attr('class', 'anomaly-graph__slider-line')
-      .attr("x1", -5)
-      .attr("y1", sliderHeight - 3)
-      .attr("x2", -5)
-      .attr("y2", sliderHeight + 3);
-
-    resizeButton.append('line')
-      .attr('class', 'anomaly-graph__slider-line')
-      .attr("x1", 5)
-      .attr("y1", sliderHeight - 3)
-      .attr("x2", 5)
-      .attr("y2", sliderHeight + 3);
-  },
-
   // Builds the Current/Expected legend for the graph
   buildCustomLegend() {
     const componentId = this.get('componentId');
@@ -213,8 +126,6 @@ export default Component.extend({
 
     later(() => {
       this.buildSliderButton();
-      // hiding this feature until fully fleshed out
-      // this.buildAnomalyRegionSlider();
       this.buildCustomLegend().then(() => {
         this.notifyPhantomJS();
       });
@@ -529,8 +440,6 @@ export default Component.extend({
 
       onSubchartBrush && onSubchartBrush(dates);
     }
-    // hiding this feature until fully fleshed out
-    // this.buildAnomalyRegionSlider(start, end);
 
   },
 
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 1ef8267..fe6e51b 100644
--- a/thirdeye/thirdeye-frontend/app/pods/components/yaml-editor/component.js
+++ b/thirdeye/thirdeye-frontend/app/pods/components/yaml-editor/component.js
@@ -35,6 +35,7 @@ import {
 import { inject as service } from '@ember/service';
 import { task } from 'ember-concurrency';
 
+
 export default Component.extend({
   classNames: ['yaml-editor'],
   notifications: service('toast'),
@@ -58,7 +59,8 @@ export default Component.extend({
   YAMLField: '',
   currentYamlAlertOriginal: '',
   currentYamlSettingsOriginal: '',
-  toggleCollapsed: false,
+  toggleCollapsed: true,
+
 
 
   init() {
@@ -68,6 +70,7 @@ export default Component.extend({
       set(this, 'currentYamlSettingsOriginal', get(this, 'detectionSettingsYaml') || get(this, 'yamlAlertSettings'));
     }
   },
+
   /**
    * sets Yaml value displayed to contents of alertYaml or yamlAlertProps
    * @method currentYamlAlert
@@ -239,10 +242,6 @@ export default Component.extend({
   },
 
   actions: {
-    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 fcf4f1d..56bb47f 100644
--- a/thirdeye/thirdeye-frontend/app/pods/components/yaml-editor/template.hbs
+++ b/thirdeye/thirdeye-frontend/app/pods/components/yaml-editor/template.hbs
@@ -38,10 +38,10 @@
     }}
   </div>
   <div class="col-xs-12">
-    {{#bs-accordion onChange=(action 'getPreview') as |acc|}}
+    {{#bs-accordion onChange=(action (mut toggleCollapsed)) as |acc|}}
       {{#acc.item value=preview as |aitem|}}
         {{#aitem.title}}
-          <section class="dashboard-container__title">Preview alert {{#unless disableYamlSave}} / Alert configuration has changed.{{/unless}}
+          <section class="dashboard-container__title">Preview alert
             <span class="pull-right"><i class="glyphicon glyphicon-menu-{{if toggleCollapsed "down" "up"}}"></i></span>
           </section>
         {{/aitem.title}}


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