You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pinot.apache.org by jl...@apache.org on 2019/03/19 23:04:58 UTC

[incubator-pinot] 01/01: [TE] frontend - harleyjj/report-anomaly - adds back report-anomaly modal to alert overview (#3985)

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

jlli pushed a commit to branch add-doc-experiment-with-pinot
in repository https://gitbox.apache.org/repos/asf/incubator-pinot.git

commit f456b46186b719c2dbfcb43c77e924cfff36517e
Author: Harley Jackson <ha...@gmail.com>
AuthorDate: Tue Mar 19 10:24:01 2019 -0700

    [TE] frontend - harleyjj/report-anomaly - adds back report-anomaly modal to alert overview (#3985)
---
 docs/getting_started.rst                           | 138 +++++++++++++++++++
 .../app/pods/components/alert-details/component.js | 146 +++++++++++++++++++--
 .../app/pods/components/alert-details/template.hbs |  50 +++++++
 .../components/alert-report-modal/component.js     |  55 ++++----
 .../components/alert-report-modal/template.hbs     |  61 ++++-----
 .../app/pods/components/yaml-editor/component.js   |   8 +-
 .../app/pods/manage/explore/template.hbs           |   1 +
 thirdeye/thirdeye-frontend/app/utils/anomaly.js    |   4 +-
 .../thirdeye-frontend/app/utils/api/anomaly.js     |   4 +-
 .../tests/unit/utils/api/anomaly-test.js           |   2 +-
 10 files changed, 391 insertions(+), 78 deletions(-)

diff --git a/docs/getting_started.rst b/docs/getting_started.rst
index e3d2416..78a9250 100644
--- a/docs/getting_started.rst
+++ b/docs/getting_started.rst
@@ -94,3 +94,141 @@ show up in Pinot.
 To show new events appearing, one can run :sql:`SELECT * FROM meetupRsvp ORDER BY mtime DESC LIMIT 50` repeatedly, which shows the
 last events that were ingested by Pinot.
 
+Experimenting with Pinot
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Now we have a quick start Pinot cluster running locally. The below shows a step-by-step instruction on
+how to add a simple table to the Pinot system, how to upload segments, and how to query it.
+
+Suppose we have a transcript in CSV format containing students' basic info and their scores of each subject.
+
++------------+------------+-----------+-----------+-----------+-----------+
+| StudentID  | firstName  | lastName  |   gender  |  subject  |   score   |
++============+============+===========+===========+===========+===========+
+|     200    |     Lucy   |   Smith   |   Female  |   Maths   |    3.8    |
++------------+------------+-----------+-----------+-----------+-----------+
+|     200    |     Lucy   |   Smith   |   Female  |  English  |    3.5    |
++------------+------------+-----------+-----------+-----------+-----------+
+|     201    |     Bob    |    King   |    Male   |   Maths   |    3.2    |
++------------+------------+-----------+-----------+-----------+-----------+
+|     202    |     Nick   |   Young   |    Male   |  Physics  |    3.6    |
++------------+------------+-----------+-----------+-----------+-----------+
+
+Firstly in order to set up a table, we need to specify the schema of this transcript.
+
+.. code-block:: none
+
+  {
+    "schemaName": "transcript",
+    "dimensionFieldSpecs": [
+      {
+        "name": "studentID",
+        "dataType": "STRING"
+      },
+      {
+        "name": "firstName",
+        "dataType": "STRING"
+      },
+      {
+        "name": "lastName",
+        "dataType": "STRING"
+      },
+      {
+        "name": "gender",
+        "dataType": "STRING"
+      },
+      {
+        "name": "subject",
+        "dataType": "STRING"
+      }
+    ],
+    "metricFieldSpecs": [
+      {
+        "name": "score",
+        "dataType": "INT"
+      }
+    ]
+  }
+
+To upload the schema, we can use the command below:
+
+.. code-block:: none
+
+  $ ./pinot-distribution/target/apache-pinot-incubating-0.1.0-SNAPSHOT-bin/apache-pinot-incubating-0.1.0-SNAPSHOT-bin/bin/pinot-admin.sh AddSchema -schemaFile /Users/jlli/transcript-schema.json -exec
+  Executing command: AddSchema -controllerHost 172.25.119.20 -controllerPort 9000 -schemaFilePath /Users/jlli/transcript-schema.json -exec
+  Sending request: http://172.25.119.20:9000/schemas to controller: jlli-mn2.linkedin.biz, version: 0.1.0-SNAPSHOT-2c5d42a908213122ab0ad8b7ac9524fcf390e4cb
+
+Then, we need to specify the table config which links the schema to this table:
+
+.. code-block:: none
+
+  {
+    "tableName": "transcript",
+    "segmentsConfig" : {
+      "replication" : "1",
+      "schemaName" : "transcript",
+      "segmentAssignmentStrategy" : "BalanceNumSegmentAssignmentStrategy"
+    },
+    "tenants" : {
+      "broker":"DefaultTenant",
+      "server":"DefaultTenant"
+    },
+    "tableIndexConfig" : {
+      "invertedIndexColumns" : [],
+      "loadMode"  : "HEAP",
+      "lazyLoad"  : "false"
+    },
+    "tableType":"OFFLINE",
+    "metadata": {}
+  }
+
+And upload the table config to Pinot cluster:
+
+.. code-block:: none
+
+  $ ./pinot-distribution/target/apache-pinot-incubating-0.1.0-SNAPSHOT-bin/apache-pinot-incubating-0.1.0-SNAPSHOT-bin/bin/pinot-admin.sh AddTable -filePath /Users/jlli/transcript-table-config.json -exec
+  Executing command: AddTable -filePath /Users/jlli/transcript-table-config.json -controllerHost 172.25.119.20 -controllerPort 9000 -exec
+  {"status":"Table transcript_OFFLINE successfully added"}
+
+In order to upload our data to Pinot cluster, we need to convert our CSV file to Pinot Segment:
+
+.. code-block:: none
+
+  $ ./pinot-distribution/target/apache-pinot-incubating-0.1.0-SNAPSHOT-bin/apache-pinot-incubating-0.1.0-SNAPSHOT-bin/bin/pinot-admin.sh CreateSegment -dataDir /Users/jlli/Desktop/test/ -format CSV -outDir /Users/jlli/Desktop/test2/ -tableName transcript -segmentName transcript_0 -overwrite -schemaFile /Users/jlli/transcript-schema.json
+  Executing command: CreateSegment  -generatorConfigFile null -dataDir /Users/jlli/Desktop/test/ -format CSV -outDir /Users/jlli/Desktop/test2/ -overwrite true -tableName transcript -segmentName transcript_0 -timeColumnName null -schemaFile /Users/jlli/transcript-schema.json -readerConfigFile null -enableStarTreeIndex false -starTreeIndexSpecFile null -hllSize 9 -hllColumns null -hllSuffix _hll -numThreads 1
+  Accepted files: [/Users/jlli/Desktop/test/Transcript.csv]
+  Finished building StatsCollector!
+  Collected stats for 4 documents
+  Created dictionary for STRING column: studentID with cardinality: 1, max length in bytes: 4, range: null to null
+  Created dictionary for STRING column: firstName with cardinality: 3, max length in bytes: 4, range: Bob to Nick
+  Created dictionary for STRING column: lastName with cardinality: 3, max length in bytes: 5, range: King to Young
+  Created dictionary for INT column: score with cardinality: 1, range: 0 to 0
+  Created dictionary for STRING column: gender with cardinality: 1, max length in bytes: 4, range: null to null
+  Created dictionary for STRING column: subject with cardinality: 1, max length in bytes: 4, range: null to null
+  Start building IndexCreator!
+  Finished records indexing in IndexCreator!
+  Finished segment seal!
+  Converting segment: /Users/jlli/Desktop/test2/transcript_0_0 to v3 format
+  v3 segment location for segment: transcript_0_0 is /Users/jlli/Desktop/test2/transcript_0_0/v3
+  Deleting files in v1 segment directory: /Users/jlli/Desktop/test2/transcript_0_0
+  Driver, record read time : 0
+  Driver, stats collector time : 0
+  Driver, indexing time : 1
+
+Once we have the Pinot segment, we can upload this segment to our cluster:
+
+.. code-block:: none
+
+  $ ./pinot-distribution/target/apache-pinot-incubating-0.1.0-SNAPSHOT-bin/apache-pinot-incubating-0.1.0-SNAPSHOT-bin/bin/pinot-admin.sh UploadSegment -segmentDir /Users/jlli/Desktop/test2/
+  Executing command: UploadSegment -controllerHost 172.25.119.20 -controllerPort 9000 -segmentDir /Users/jlli/Desktop/test2/
+  Compressing segment transcript_0_0
+  Uploading segment transcript_0_0.tar.gz
+  Sending request: http://172.25.119.20:9000/v2/segments to controller: jlli-mn2.linkedin.biz, version: 0.1.0-SNAPSHOT-2c5d42a908213122ab0ad8b7ac9524fcf390e4cb
+
+Now we can query the data we just uploaded to Pinot:
+
+.. code-block:: none
+
+  $ ./pinot-distribution/target/apache-pinot-incubating-0.1.0-SNAPSHOT-bin/apache-pinot-incubating-0.1.0-SNAPSHOT-bin/bin/pinot-admin.sh PostQuery -brokerPort 8000 -query "select count(*) from transcript"
+  Executing command: PostQuery -brokerHost 172.25.119.20 -brokerPort 8000 -query select count(*) from transcript
+  Result: {"aggregationResults":[{"function":"count_star","value":"4"}],"exceptions":[],"numServersQueried":1,"numServersResponded":1,"numSegmentsQueried":1,"numSegmentsProcessed":1,"numSegmentsMatched":1,"numDocsScanned":4,"numEntriesScannedInFilter":0,"numEntriesScannedPostFilter":0,"numGroupsLimitReached":false,"totalDocs":4,"timeUsedMs":7,"segmentStatistics":[],"traceInfo":{}}
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 b70b5d5..f93e432 100644
--- a/thirdeye/thirdeye-frontend/app/pods/components/alert-details/component.js
+++ b/thirdeye/thirdeye-frontend/app/pods/components/alert-details/component.js
@@ -14,9 +14,9 @@
  */
 
 import Component from '@ember/component';
-import { computed, observer, set, get, getProperties, getWithDefault } 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 { checkStatus, humanizeFloat, postProps } from 'thirdeye-frontend/utils/utils';
 import { colorMapping, toColor, makeTime, toMetricLabel, extractTail } from 'thirdeye-frontend/utils/rca-utils';
 import { getYamlPreviewAnomalies,
   getAnomaliesByAlertId,
@@ -90,10 +90,16 @@ export default Component.extend({
   currentPage: 1,
   isPreviewMode: false,
   alertId: null,
+  alertData: null,
   feedbackOptions: ['Not reviewed yet', 'Yes - unexpected', 'Expected temporary change', 'Expected permanent change', 'No change observed'],
   labelMap: anomalyResponseMapNew,
   labelResponse: {},
   selectedDimension: null,
+  isReportSuccess: false,
+  isReportFailure: false,
+  openReportModal: false,
+  missingAnomalyProps: {},
+
 
 
   updateVisuals: observer(
@@ -170,6 +176,24 @@ export default Component.extend({
   ),
 
   /**
+   * date-time-picker: indicates the date format to be used based on granularity
+   * @type {String}
+   */
+  uiDateFormat: computed('alertData.windowUnit', function() {
+    const rawGranularity = this.get('alertData.bucketUnit');
+    const granularity = rawGranularity ? rawGranularity.toLowerCase() : '';
+
+    switch(granularity) {
+      case 'days':
+        return 'MMM D, YYYY';
+      case 'hours':
+        return 'MMM D, YYYY h a';
+      default:
+        return 'MMM D, YYYY hh:mm a';
+    }
+  }),
+
+  /**
    * Table pagination: creates the page Array for view
    * @type {Array}
    */
@@ -645,6 +669,54 @@ export default Component.extend({
     }
   },
 
+  /**
+   * Send a POST request to the report anomaly API (2-step process)
+   * http://go/te-ss-alert-flow-api
+   * @method reportAnomaly
+   * @param {String} id - The alert id
+   * @param {Object} data - The input values from 'report new anomaly' modal
+   * @return {Promise}
+   */
+  _reportAnomaly(id, metricUrn, data) {
+    const reportUrl = `/detection/report-anomaly/${id}?metricUrn=${metricUrn}`;
+    const requiredProps = ['startTime', 'endTime', 'feedbackType'];
+    let missingData = false;
+    requiredProps.forEach(prop => {
+      if (!data[prop]) {
+        missingData = true;
+      }
+    });
+    let queryStringUrl = reportUrl;
+
+    if (missingData) {
+      return Promise.reject(new Error('missing data'));
+    } else {
+      Object.entries(data).forEach(([key, value]) => {
+        queryStringUrl += `&${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
+      });
+      // Step 1: Report the anomaly
+      return fetch(queryStringUrl, postProps('')).then((res) => checkStatus(res, 'post'));
+    }
+  },
+
+  /**
+   * Modal opener for "report missing anomaly".
+   * @method _triggerOpenReportModal
+   * @return {undefined}
+   */
+  _triggerOpenReportModal() {
+    this.setProperties({
+      isReportSuccess: false,
+      isReportFailure: false,
+      openReportModal: true
+    });
+    // We need the C3/D3 graph to render after its containing parent elements are rendered
+    // in order to avoid strange overflow effects.
+    later(() => {
+      this.set('renderModalContent', true);
+    });
+  },
+
   actions: {
     /**
      * Handle dynamically saving anomaly feedback responses
@@ -664,11 +736,9 @@ export default Component.extend({
         // Save anomaly feedback
         await updateAnomalyFeedback(anomalyRecord.anomalyId, responseObj.value);
         // We make a call to ensure our new response got saved
-        const anomaly = await verifyAnomalyFeedback(anomalyRecord.anomalyId, responseObj.status);
-        const filterMap = getWithDefault(anomaly, 'searchFilters.statusFilterMap', null);
-        // This verifies that the status change got saved as key in the anomaly statusFilterMap property
-        const keyPresent = filterMap && Object.keys(filterMap).find(key => responseObj.status.includes(key));
-        if (keyPresent) {
+        const anomaly = await verifyAnomalyFeedback(anomalyRecord.anomalyId);
+
+        if (anomaly.feedback && responseObj.value === anomaly.feedback.feedbackType) {
           this.set('labelResponse', {
             anomalyId: anomalyRecord.anomalyId,
             showResponseSaved: true,
@@ -680,7 +750,13 @@ export default Component.extend({
           let found = false;
           while (i < anomalies.length && !found) {
             if (anomalies[i].id === anomalyRecord.anomalyId) {
-              anomalies[i].feedback.feedbackType = newFeedbackValue;
+              if (anomalies[i].feedback) {
+                anomalies[i].feedback.feedbackType = newFeedbackValue;
+              } else {
+                anomalies[i].feedback = {
+                  feedbackType: newFeedbackValue
+                };
+              }
               found = true;
             }
             i++;
@@ -700,6 +776,60 @@ export default Component.extend({
       set(this, 'renderStatusIcon', true);
     },
 
+    /**
+     * Handle missing anomaly modal cancel
+     */
+    onCancel() {
+      this.setProperties({
+        isReportSuccess: false,
+        isReportFailure: false,
+        openReportModal: false,
+        renderModalContent: false
+      });
+    },
+
+    /**
+     * Open modal for missing anomalies
+     */
+    onClickReportAnomaly() {
+      this._triggerOpenReportModal();
+    },
+
+    /**
+     * Received bubbled-up action from modal
+     * @param {Object} all input field values
+     */
+    onInputMissingAnomaly(inputObj) {
+      this.set('missingAnomalyProps', inputObj);
+    },
+
+    /**
+     * Handle submission of missing anomaly form from alert-report-modal
+     */
+    onSave() {
+      const { alertId, missingAnomalyProps, metricUrn } = this.getProperties('alertId', 'missingAnomalyProps', 'metricUrn');
+      this._reportAnomaly(alertId, metricUrn, missingAnomalyProps)
+        .then(() => {
+          const rangeFormat = 'YYYY-MM-DD HH:mm';
+          const startStr = moment(missingAnomalyProps.startTime).format(rangeFormat);
+          const endStr = moment(missingAnomalyProps.endTime).format(rangeFormat);
+          this.setProperties({
+            isReportSuccess: true,
+            isReportFailure: false,
+            openReportModal: false,
+            reportedRange: `${startStr} - ${endStr}`
+          });
+        })
+        // If failure, leave modal open and report
+        .catch(() => {
+          this.setProperties({
+            missingAnomalyProps: {},
+            isReportFailure: true,
+            isReportSuccess: false
+          });
+        });
+    },
+
     onSelectDimension(selected) {
       const metricUrnList = get(this, 'metricUrnList');
       const newMetricUrn = metricUrnList.find(urn => {
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 287fc98..c0f479d 100644
--- a/thirdeye/thirdeye-frontend/app/pods/components/alert-details/template.hbs
+++ b/thirdeye/thirdeye-frontend/app/pods/components/alert-details/template.hbs
@@ -49,12 +49,30 @@
           {{/if}}
         </div>
 
+        {{#if isReportSuccess}}
+          {{#bs-alert type="success" class="te-form__banner te-form__banner--success"}}
+            <strong>Success:</strong> Anomaly reported for dates <strong>{{reportedRange}}</strong>. Refresh page to see new anomalies...
+          {{/bs-alert}}
+        {{/if}}
+
+        {{#if isReportFailure}}
+          {{#bs-alert type="danger" class="te-form__banner te-form__banner--failure"}}
+            <strong>Error:</strong> Failed to save reported anomaly.
+          {{/bs-alert}}
+        {{/if}}
+
         <div class="te-content-block">
           {{#if alertHasDimensions}}
             <h4 class="te-self-serve__block-title">Anomalies over time for dimension {{selectedDimension}}</h4>
           {{else}}
             <h4 class="te-self-serve__block-title">Anomalies over time </h4>
           {{/if}}
+
+          {{#unless isPreviewMode}}
+            <a class="te-self-serve__side-link te-self-serve__side-link--high" {{action "onClickReportAnomaly" this}}>Report missing anomaly</a>
+          {{/unless}}
+
+          {{!-- Dimension selector --}}
           {{#if alertHasDimensions}}
             <div class="te-form__select te-form__select--wider col-md-3">
               {{#power-select
@@ -73,6 +91,38 @@
               {{/power-select}}
             </div>
           {{/if}}
+
+          {{!-- Missing anomaly modal --}}
+          {{#te-modal
+            cancelButtonText="Cancel"
+            submitButtonText="Report"
+            submitAction=(action "onSave")
+            cancelAction=(action "onCancel")
+            isShowingModal=openReportModal
+            headerText="Report Undetected Anomaly"
+          }}
+            {{#if renderModalContent}}
+              {{alert-report-modal
+                series=series
+                colorMapping=colorMapping
+                axis=axis
+                zoom=zoom
+                legend=legend
+                metricName=alertData.metric
+                alertName=alertData.detectionName
+                predefinedRanges=predefinedRanges
+                dimensionOptions=dimensionOptions
+                selectedDimension=selectedDimension
+                alertHasDimensions=alertHasDimensions
+                isMetricDataLoading=isMetricDataLoading
+                isMetricDataInvalid=isMetricDataInvalid
+                inputAction=(action "onInputMissingAnomaly")
+              }}
+            {{else}}
+              {{ember-spinner}}
+            {{/if}}
+          {{/te-modal}}
+
           <div class="col-xs-12 te-graph-container">
             {{timeseries-chart
               series=series
diff --git a/thirdeye/thirdeye-frontend/app/pods/components/alert-report-modal/component.js b/thirdeye/thirdeye-frontend/app/pods/components/alert-report-modal/component.js
index 8ca639a..df87958 100644
--- a/thirdeye/thirdeye-frontend/app/pods/components/alert-report-modal/component.js
+++ b/thirdeye/thirdeye-frontend/app/pods/components/alert-report-modal/component.js
@@ -3,26 +3,22 @@
  * @module components/alert-report-modal
  * @property {String} metricName - text for read-only metric field
  * @property {String} alertName  - text for read-only alert field
- * @property {Array} dimensionOptions - options for dimension select field
  * @property {Number} timePickerIncrement - config for time-range-picker
- * @property {String} maxTime - timestamp for loading anomaly graph
  * @property {String} viewRegionStart - range start timestamp
  * @property {String} viewRegionEnd - range end timestamp
  * @property {Object} predefinedRanges - needed for time-range-picker
  * @property {String} uiDateFormat - date format desired for time-range-picker
- * @property {String} graphMessageText - text for graph in loading state
  * @example
   {{alert-report-modal
     metricName="mobile_notification_errors"
     alertName="notification_sessions_mobile"
-    dimensionOptions=['dimension 1', 'dimension 2']
+    selectedDimension='dimension'
+    alertHasDimensions=true
     timePickerIncrement=200
-    maxTime="1513137100914"
     viewRegionStart="2017-10-12 23:59"
     viewRegionEnd="2017-12-11 23:59"
     predefinedRanges=predefinedRanges
     uiDateFormat="MMM D, YYYY hh:mm a"
-    graphMessageText="Loading graph"
     inputAction=(action "onInputMissingAnomaly")
   }}
  * @exports alert-report-modal
@@ -37,19 +33,7 @@ export default Component.extend({
   containerClassNames: 'alert-report-modal',
   isNewTrend: false,
   showTimePicker: true,
-  timePickerIncrement: 30,
-
-  /**
-   * Pre-select all dimensions option if none else available
-   * TODO: use this.get('dimensionOptions.firstObject')
-   */
-  init() {
-    this._super(...arguments);
-    const dimensionOptions = this.get('dimensionOptions');
-    if (dimensionOptions && dimensionOptions.length === 1 && dimensionOptions[0] === 'All Dimensions') {
-      this.set('selectedDimension', dimensionOptions[0]);
-    }
-  },
+  timePickerIncrement: 5,
 
   /**
    * Collects all input data for the post request
@@ -68,7 +52,7 @@ export default Component.extend({
         startTime: moment(this.get('viewAnomalyStart')).utc().valueOf(),
         endTime: moment(this.get('viewAnomalyEnd')).utc().valueOf(),
         feedbackType: this.get('isNewTrend') ? 'ANOMALY_NEW_TREND' : 'ANOMALY',
-        dimension: this.get('selectedDimension') || null,
+        dimension: this.get('showDimension') ? this.get('selectedDimension') || null : null,
         comment: this.get('anomalyComments') || null,
         externalURL: this.get('anomalyLinks') || null
       };
@@ -78,6 +62,24 @@ export default Component.extend({
   ),
 
   /**
+   * Collects all input data for the post request
+   * @method reportAnomalyPayload
+   * @return {Object} Post data
+   */
+  showDimension: computed(
+    'alertHasDimensions',
+    'selectedDimension',
+    function() {
+      const {
+        alertHasDimensions,
+        selectedDimension
+      } = this.getProperties('alertHasDimensions', 'selectedDimension');
+
+      return (alertHasDimensions && selectedDimension !== 'Choose a dimension');
+    }
+  ),
+
+  /**
    * Sends post object as is to parent
    * @method bubbleModalInput
    */
@@ -98,9 +100,9 @@ export default Component.extend({
     },
 
     /**
-     * Handle selected dimension filter
-     * @method onSelectDimension
-     * @param {Object} selectedObj - the user-selected dimension to filter by
+     * Handle selected feedback type
+     * @method onFeedbackTypeSelection
+     * @param {Object} selectedObj - the user-selected feedback type
      */
     onFeedbackTypeSelection(trendSelection) {
       this.set('isNewTrend', trendSelection);
@@ -108,11 +110,10 @@ export default Component.extend({
     },
 
     /**
-     * Handle selected dimension filter
-     * @method onFeedbackComments
-     * @param {String} comment field value
+     * Handle changes to anomaly input
+     * @method onAnomalyInput
      */
-    onAnomalyInput(value) {
+    onAnomalyInput() {
       this.bubbleModalInput();
     }
 
diff --git a/thirdeye/thirdeye-frontend/app/pods/components/alert-report-modal/template.hbs b/thirdeye/thirdeye-frontend/app/pods/components/alert-report-modal/template.hbs
index 76c12c6..4520f01 100644
--- a/thirdeye/thirdeye-frontend/app/pods/components/alert-report-modal/template.hbs
+++ b/thirdeye/thirdeye-frontend/app/pods/components/alert-report-modal/template.hbs
@@ -1,9 +1,15 @@
 <main class="te-form alert-report-modal__body">
 
   <fieldset class="te-form__section te-form__section--first row" id="select-target1">
-    <div class="col-xs-12">
-      <legend class="te-report-title">Specify Metric, Alert, and Dimensions</legend>
-    </div>
+    {{#if showDimension}}
+      <div class="col-xs-12">
+        <legend class="te-report-title">Specify Metric, Alert, and Dimensions</legend>
+      </div>
+    {{else}}
+      <div class="col-xs-12">
+        <legend class="te-report-title">Specify Metric and Alert</legend>
+      </div>
+    {{/if}}
 
     <div class="form-group col-xs-5">
       <label for="select-metric" class="control-label te-label required">Metric</label>
@@ -27,23 +33,18 @@
       }}
     </div>
 
-    <div class="form-group col-xs-7">
-      <label for="report-select-dimension" class="control-label te-label">Dimensions</label>
-      {{#power-select
-        triggerId="report-select-dimension"
-        triggerClass="te-form__select"
-        verticalPosition="below"
-        placeholder="Select a dimension"
-        renderInPlace=true
-        options=dimensionOptions
-        searchEnabled=false
-        selected=selectedDimension
-        onchange=(action "onSelectDimension")
-        as |dimension|
-      }}
-        {{dimension}}
-      {{/power-select}}
-    </div>
+    {{#if showDimension}}
+      <div class="form-group col-xs-7">
+        <label for="report-select-dimension" class="control-label te-label">Dimensions</label>
+        {{input
+          type="text"
+          id="report-select-dimension"
+          class="form-control te-input"
+          value=selectedDimension
+          disabled=true
+        }}
+      </div>
+    {{/if}}
   </fieldset>
 
   <fieldset class="te-form__section row">
@@ -69,20 +70,12 @@
   </fieldset>
 
   <fieldset class="te-form__section te-form__section--graph">
-    {{self-serve-graph
-      isMetricDataLoading=isMetricDataLoading
-      isMetricDataInvalid=isMetricDataInvalid
-      isFetchingDimensions=isFetchingDimensions
-      isDimensionFetchDone=isDimensionFetchDone
-      availableDimensions=availableDimensions
-      selectedDimension=alertDimension
-      graphMailtoLink=graphMailtoLink
-      isTopDimensionsAllowed=false
-      isDimensionError=false
-      isMetricSelected=true
-      metricData=metricData
-      topDimensions=topDimensions
-      componentId='report-anomaly'
+    {{timeseries-chart
+      series=series
+      colorMapping=colorMapping
+      axis=axis
+      zoom=zoom
+      legend=legend
     }}
   </fieldset>
 
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 07f384a..1777b90 100644
--- a/thirdeye/thirdeye-frontend/app/pods/components/yaml-editor/component.js
+++ b/thirdeye/thirdeye-frontend/app/pods/components/yaml-editor/component.js
@@ -305,6 +305,7 @@ export default Component.extend({
       if(value.yaml) {
         set(this, 'currentYamlSettings', value.yaml);
         set(this, 'groupName', value);
+        set(this, 'subscriptionGroupId', value.id);
       }
     },
 
@@ -419,12 +420,11 @@ export default Component.extend({
     async saveEditYamlAction() {
       const {
         alertYaml,
-        detectionSettingsYaml,
+        currentYamlSettings,
         notifications,
         alertId,
         subscriptionGroupId
-      } = getProperties(this, 'alertYaml', 'detectionSettingsYaml', 'notifications', 'alertId', 'subscriptionGroupId');
-
+      } = getProperties(this, 'alertYaml', 'currentYamlSettings', 'notifications', 'alertId', 'subscriptionGroupId');
       //PUT alert
       const alert_url = `/yaml/${alertId}`;
       const alertPostProps = {
@@ -450,7 +450,7 @@ export default Component.extend({
       const setting_url = `/yaml/subscription/${subscriptionGroupId}`;
       const settingsPostProps = {
         method: 'PUT',
-        body: detectionSettingsYaml,
+        body: currentYamlSettings,
         headers: { 'content-type': 'text/plain' }
       };
       try {
diff --git a/thirdeye/thirdeye-frontend/app/pods/manage/explore/template.hbs b/thirdeye/thirdeye-frontend/app/pods/manage/explore/template.hbs
index d0f178b..c5698d8 100644
--- a/thirdeye/thirdeye-frontend/app/pods/manage/explore/template.hbs
+++ b/thirdeye/thirdeye-frontend/app/pods/manage/explore/template.hbs
@@ -29,6 +29,7 @@
         showDetails=true
         dataIsCurrent=true
         alertId=model.alertId
+        alertData=model.alertData
         metricUrn=model.metricUrn
         metricUrnList=model.metricUrnList
       }}
diff --git a/thirdeye/thirdeye-frontend/app/utils/anomaly.js b/thirdeye/thirdeye-frontend/app/utils/anomaly.js
index 84c44a4..ed74925 100644
--- a/thirdeye/thirdeye-frontend/app/utils/anomaly.js
+++ b/thirdeye/thirdeye-frontend/app/utils/anomaly.js
@@ -89,7 +89,7 @@ anomalyResponseObjNew.forEach((obj) => {
  * @return {Ember.RSVP.Promise}
  */
 export function updateAnomalyFeedback(anomalyId, feedbackType) {
-  const url = `/anomalies/updateFeedback/${anomalyId}`;
+  const url = `/dashboard/anomaly-merged-result/feedback/${anomalyId}`;
   const data = { feedbackType, comment: '' };
   return fetch(url, postProps(data)).then((res) => checkStatus(res, 'post'));
 }
@@ -127,7 +127,7 @@ export function getAnomaliesByAlertId(alertId, startTime, endTime) {
  * @return {Ember.RSVP.Promise}
  */
 export function verifyAnomalyFeedback(anomalyId) {
-  const url = `${anomalyApiUrls.getAnomalyDataUrl()}${anomalyId}`;
+  const url = `${anomalyApiUrls.getAnomalyDataUrl(anomalyId)}`;
   return fetch(url).then(checkStatus);
 }
 
diff --git a/thirdeye/thirdeye-frontend/app/utils/api/anomaly.js b/thirdeye/thirdeye-frontend/app/utils/api/anomaly.js
index 77e7905..de4aa5a 100644
--- a/thirdeye/thirdeye-frontend/app/utils/api/anomaly.js
+++ b/thirdeye/thirdeye-frontend/app/utils/api/anomaly.js
@@ -5,8 +5,8 @@
  * @returns {String} the complete anomaly data url
  * @example getAnomalyDataUrl(1491804013000, 1491890413000) // yields => /anomalies/search/anomalyIds/1491804013000/1491890413000/1?anomalyIds=
  */
-export function getAnomalyDataUrl(startStamp = 0, endStamp = 0) {
-  return `/anomalies/search/anomalyIds/${startStamp}/${endStamp}/1?anomalyIds=`;
+export function getAnomalyDataUrl(anomalyId) {
+  return `/dashboard/anomalies/view/${anomalyId}`;
 }
 
 /**
diff --git a/thirdeye/thirdeye-frontend/tests/unit/utils/api/anomaly-test.js b/thirdeye/thirdeye-frontend/tests/unit/utils/api/anomaly-test.js
index c841f24..2e7e57c 100644
--- a/thirdeye/thirdeye-frontend/tests/unit/utils/api/anomaly-test.js
+++ b/thirdeye/thirdeye-frontend/tests/unit/utils/api/anomaly-test.js
@@ -4,5 +4,5 @@ import { getAnomalyDataUrl } from 'thirdeye-frontend/utils/api/anomaly';
 module('Unit | Utility | api/anomaly');
 
 test('it returns anomaly data url correctly', function(assert) {
-  assert.equal(getAnomalyDataUrl(0, 0), '/anomalies/search/anomalyIds/0/0/1?anomalyIds=', 'it returns anomaly data url duration ok');
+  assert.equal(getAnomalyDataUrl(0), '/dashboard/anomalies/view/0', 'it returns anomaly data url duration ok');
 });


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