Browse Source

renaming anomalyType to analyticUnit

master
Alexey Velikiy 6 years ago
parent
commit
7cc7a2f5c1
  1. 14763
      dist/module.js
  2. 85
      dist/partials/tab_analytics.html
  3. 155
      src/controllers/analytic_controller.ts
  4. 37
      src/graph_renderer.ts
  5. 4
      src/graph_tooltip.ts
  6. 69
      src/model/analytic_unit.ts
  7. 68
      src/module.ts
  8. 85
      src/partials/tab_analytics.html
  9. 18
      src/services/analytic_service.ts

14763
dist/module.js vendored

File diff suppressed because one or more lines are too long

85
dist/partials/tab_analytics.html vendored

@ -1,18 +1,18 @@
<h5> Anomaly Types </h5> <h5> Analytic Units </h5>
<div class="editor-row"> <div class="editor-row">
<div class="gf-form" ng-repeat="anomalyType in ctrl.anomalyController.anomalyTypes"> <div class="gf-form" ng-repeat="analyticUnit in ctrl.analyticsController.analyticUnits">
<label class="gf-form-label width-4"> Name </label> <label class="gf-form-label width-4"> Name </label>
<input <input
type="text" class="gf-form-input max-width-15" type="text" class="gf-form-input max-width-15"
ng-model="anomalyType.name" ng-model="analyticUnit.name"
ng-disabled="true" ng-disabled="true"
> >
<label class="gf-form-label width-8"> Pattern type </label> <label class="gf-form-label width-8"> Type </label>
<div class="gf-form-select-wrapper"> <div class="gf-form-select-wrapper">
<select class="gf-form-input width-12" <select class="gf-form-input width-12"
ng-model="anomalyType.pattern" ng-model="analyticUnit.pattern"
ng-options="pattern as pattern for pattern in ctrl.patterns" ng-options="pattern as pattern for pattern in ctrl.patterns"
ng-disabled="true" ng-disabled="true"
/> />
@ -30,21 +30,21 @@
<label class="gf-form-label width-6"> Color </label> <label class="gf-form-label width-6"> Color </label>
<span class="gf-form-label"> <span class="gf-form-label">
<color-picker <color-picker
color="anomalyType.color" color="analyticUnit.color"
onChange="ctrl.onAnomalyColorChange.bind(ctrl, anomalyType.key)" onChange="ctrl.onAnomalyColorChange.bind(ctrl, analyticUnit.key)"
/> />
</span> </span>
<label class="gf-form-label" ng-style="anomalyType.status === 'learning' && { 'cursor': 'not-allowed' }"> <label class="gf-form-label" ng-style="analyticUnit.status === 'learning' && { 'cursor': 'not-allowed' }">
<a class="pointer" tabindex="1" <a class="pointer" tabindex="1"
ng-click="ctrl.onToggleAnomalyTypeLabelingMode(anomalyType.key)" ng-click="ctrl.onToggleLabelingMode(analyticUnit.key)"
ng-disabled="anomalyType.status === 'learning'" ng-disabled="analyticUnit.status === 'learning'"
> >
<i class="fa fa-bar-chart" ng-if="!anomalyType.saving"></i> <i class="fa fa-bar-chart" ng-if="!analyticUnit.saving"></i>
<i class="fa fa-spinner fa-spin" ng-if="anomalyType.saving"></i> <i class="fa fa-spinner fa-spin" ng-if="analyticUnit.saving"></i>
<b ng-if="anomalyType.selected && !anomalyType.deleteMode && !anomalyType.saving"> labeling </b> <b ng-if="analyticUnit.selected && !analyticUnit.deleteMode && !analyticUnit.saving"> labeling </b>
<b ng-if="anomalyType.selected && anomalyType.deleteMode && !anomalyType.saving"> deleting </b> <b ng-if="analyticUnit.selected && analyticUnit.deleteMode && !analyticUnit.saving"> deleting </b>
<b ng-if="anomalyType.saving" ng-disabled="true"> saving... </b> <b ng-if="analyticUnit.saving" ng-disabled="true"> saving... </b>
</a> </a>
</label> </label>
@ -53,35 +53,35 @@
<label <label
class="gf-form-label text-center" class="gf-form-label text-center"
style="width: 4rem" style="width: 4rem"
ng-if="anomalyType.alertEnabled === undefined" ng-if="analyticUnit.alertEnabled === undefined"
bs-tooltip="'Alarting status isn`t available. Wait please.'" bs-tooltip="'Alarting status isn`t available. Wait please.'"
> >
<i class="fa fa-spinner fa-spin"></i> <i class="fa fa-spinner fa-spin"></i>
</label> </label>
<gf-form-switch <gf-form-switch
ng-if="anomalyType.alertEnabled !== undefined" ng-if="analyticUnit.alertEnabled !== undefined"
on-change="ctrl.onAnomalyAlertChange(anomalyType)" on-change="ctrl.onAnomalyAlertChange(analyticUnit)"
checked="anomalyType.alertEnabled" checked="analyticUnit.alertEnabled"
style="height: 36px;" style="height: 36px;"
/> />
<label class="gf-form-label"> <label class="gf-form-label">
<a <a
ng-if="anomalyType.visible" ng-if="analyticUnit.visible"
ng-disabled="anomalyType.selected" ng-disabled="analyticUnit.selected"
bs-tooltip="'Hide. It`s visible now.'" bs-tooltip="'Hide. It`s visible now.'"
ng-click="ctrl.onAnomalyToggleVisibility(anomalyType.key)" ng-click="ctrl.onToggleVisibility(analyticUnit.key)"
class="pointer" class="pointer"
> >
<i class="fa fa-eye"></i> <i class="fa fa-eye"></i>
</a> </a>
<a <a
ng-if="!anomalyType.visible" ng-if="!analyticUnit.visible"
ng-disabled="anomalyType.selected" ng-disabled="analyticUnit.selected"
bs-tooltip="'Show. It`s hidden now.'" bs-tooltip="'Show. It`s hidden now.'"
ng-click="ctrl.onAnomalyToggleVisibility(anomalyType.key)" ng-click="ctrl.onToggleVisibility(analyticUnit.key)"
class="pointer" class="pointer"
> >
<i class="fa fa-eye-slash"></i> <i class="fa fa-eye-slash"></i>
@ -90,16 +90,16 @@
<label class="gf-form-label"> <label class="gf-form-label">
<a <a
ng-if="!anomalyType.selected" ng-if="!analyticUnit.selected"
ng-click="ctrl.onAnomalyRemove(anomalyType.key)" ng-click="ctrl.onRemove(analyticUnit.key)"
class="pointer" class="pointer"
> >
<i class="fa fa-trash"></i> <i class="fa fa-trash"></i>
</a> </a>
<a <a
ng-if="anomalyType.selected" ng-if="analyticUnit.selected"
ng-click="ctrl.onAnomalyCancelLabeling(anomalyType.key)" ng-click="ctrl.onCancelLabeling(analyticUnit.key)"
class="pointer" class="pointer"
> >
<i class="fa fa-ban"></i> <i class="fa fa-ban"></i>
@ -107,43 +107,42 @@
</label> </label>
<label> <label>
<i ng-if="anomalyType.status === 'learning'" class="grafana-tip fa fa-leanpub ng-scope" bs-tooltip="'Learning'"></i> <i ng-if="analyticUnit.status === 'learning'" class="grafana-tip fa fa-leanpub ng-scope" bs-tooltip="'Learning'"></i>
<i ng-if="anomalyType.status === 'pending'" class="grafana-tip fa fa-list-ul ng-scope" bs-tooltip="'Pending'"></i> <i ng-if="analyticUnit.status === 'pending'" class="grafana-tip fa fa-list-ul ng-scope" bs-tooltip="'Pending'"></i>
<i ng-if="anomalyType.status === 'failed'" class="grafana-tip fa fa-exclamation-circle ng-scope" bs-tooltip="'Error: ' + anomalyType.error"></i> <i ng-if="analyticUnit.status === 'failed'" class="grafana-tip fa fa-exclamation-circle ng-scope" bs-tooltip="'Error: ' + analyticUnit.error"></i>
</label> </label>
</div> </div>
</div> </div>
<div class="editor-row" ng-if="ctrl.anomalyController.creatingAnomalyType"> <div class="editor-row" ng-if="ctrl.analyticsController.creatingNew">
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label width-4"> Name </label> <label class="gf-form-label width-4"> Name </label>
<input <input
type="text" class="gf-form-input max-width-15" type="text" class="gf-form-input max-width-15"
ng-model="ctrl.anomalyController.newAnomalyType.name" ng-model="ctrl.analyticsController.newAnalyticUnit.name"
ng-change="ctrl.onAnomalyNameChange()"
> >
<label class="gf-form-label width-8"> Pattern type </label> <label class="gf-form-label width-8"> Type </label>
<div class="gf-form-select-wrapper"> <div class="gf-form-select-wrapper">
<select class="gf-form-input width-12" <select class="gf-form-input width-12"
ng-model="ctrl.anomalyController.newAnomalyType.pattern" ng-model="ctrl.analyticsController.newAnalyticUnit.pattern"
ng-options="pattern as pattern disable when (pattern !== 'General' && pattern !== 'Drops' && pattern !== 'Peaks') for pattern in ctrl.patterns" ng-options="pattern as pattern disable when (pattern !== 'General' && pattern !== 'Drops' && pattern !== 'Peaks') for pattern in ctrl.patterns"
/> />
</div> </div>
<label class="gf-form-label"> <label class="gf-form-label">
<a class="pointer" tabindex="1" ng-click="ctrl.saveAnomalyType()"> <a class="pointer" tabindex="1" ng-click="ctrl.saveNew()">
<b ng-if="!ctrl.anomalyController.savingAnomalyType"> create </b> <b ng-if="!ctrl.analyticsController.saving"> create </b>
<b ng-if="ctrl.anomalyController.savingAnomalyType" ng-disabled="true"> saving... </b> <b ng-if="ctrl.analyticsController.saving" ng-disabled="true"> saving... </b>
</a> </a>
</label> </label>
</div> </div>
</div> </div>
<div class="gf-form-button-row" ng-if="!ctrl.anomalyController.creatingAnomalyType"> <div class="gf-form-button-row" ng-if="!ctrl.analyticsController.creatingAnalyticUnit">
<button class="btn btn-inverse" ng-click="ctrl.createNewAnomalyType()"> <button class="btn btn-inverse" ng-click="ctrl.createNew()">
<i class="fa fa-plus"></i> <i class="fa fa-plus"></i>
Add an Anomaly Type Add Analytic Unit
</button> </button>
</div> </div>

155
src/controllers/anomaly_controller.ts → src/controllers/analytic_controller.ts

@ -1,9 +1,9 @@
import { AnomalyService } from '../services/anomaly_service' import { AnalyticService } from '../services/analytic_service'
import { import {
AnomalyKey, AnomalyType, AnalyticUnitKey, AnalyticUnit,
AnomalyTypesSet, AnomalySegment, AnomalySegmentsSearcher, AnomalySermentPair AnalyticUnitsSet, AnalyticSegment, AnalyticSegmentsSearcher, AnalyticSegmentPair
} from '../model/anomaly'; } from '../model/analytic_unit';
import { MetricExpanded } from '../model/metric'; import { MetricExpanded } from '../model/metric';
import { DatasourceRequest } from '../model/datasource'; import { DatasourceRequest } from '../model/datasource';
import { Segment, SegmentKey } from '../model/segment'; import { Segment, SegmentKey } from '../model/segment';
@ -13,45 +13,46 @@ import { Emitter } from 'grafana/app/core/utils/emitter'
import _ from 'lodash'; import _ from 'lodash';
export const REGION_FILL_ALPHA = 0.7; export const REGION_FILL_ALPHA = 0.7;
export const REGION_STROKE_ALPHA = 0.9; export const REGION_STROKE_ALPHA = 0.9;
export const REGION_DELETE_COLOR_LIGHT = '#d1d1d1'; export const REGION_DELETE_COLOR_LIGHT = '#d1d1d1';
export const REGION_DELETE_COLOR_DARK = 'white'; export const REGION_DELETE_COLOR_DARK = 'white';
export class AnomalyController { export class AnalyticController {
private _anomalyTypesSet: AnomalyTypesSet; private _analyticUnitsSet: AnalyticUnitsSet;
private _selectedAnomalyKey: AnomalyKey = null; private _selectedAnalyticUnitKey: AnalyticUnitKey = null;
private _labelingDataAddedSegments: SegmentsSet<AnomalySegment>; private _labelingDataAddedSegments: SegmentsSet<AnalyticSegment>;
private _labelingDataDeletedSegments: SegmentsSet<AnomalySegment>; private _labelingDataDeletedSegments: SegmentsSet<AnalyticSegment>;
private _newAnomalyType: AnomalyType = null; private _newAnalyticUnitType: AnalyticUnit = null;
private _creatingNewAnomalyType: boolean = false; private _creatingNewAnalyticType: boolean = false;
private _savingNewAnomalyType: boolean = false; private _savingNewAnalyticUnit: boolean = false;
private _tempIdCounted = -1; private _tempIdCounted = -1;
private _graphLocked = false; private _graphLocked = false;
private _statusRunners: Set<AnomalyKey> = new Set<AnomalyKey>(); private _statusRunners: Set<AnalyticUnitKey> = new Set<AnalyticUnitKey>();
constructor(private _panelObject: any, private _anomalyService: AnomalyService, private _emitter: Emitter) { constructor(private _panelObject: any, private _analyticService: AnalyticService, private _emitter: Emitter) {
if(_panelObject.anomalyTypes === undefined) { if(_panelObject.anomalyTypes === undefined) {
_panelObject.anomalyTypes = []; _panelObject.anomalyTypes = [];
} }
this._labelingDataAddedSegments = new SegmentArray<AnomalySegment>(); this._labelingDataAddedSegments = new SegmentArray<AnalyticSegment>();
this._labelingDataDeletedSegments = new SegmentArray<AnomalySegment>(); this._labelingDataDeletedSegments = new SegmentArray<AnalyticSegment>();
this._anomalyTypesSet = new AnomalyTypesSet(this._panelObject.anomalyTypes); this._analyticUnitsSet = new AnalyticUnitsSet(this._panelObject.anomalyTypes);
this.anomalyTypes.forEach(a => this.runAnomalyTypeAlertEnabledWaiter(a)); this.analyticUnits.forEach(a => this.runEnabledWaiter(a));
} }
getAnomalySegmentsSearcher(): AnomalySegmentsSearcher { getSegmentsSearcher(): AnalyticSegmentsSearcher {
return this._anomalySegmentsSearcher.bind(this); return this._segmentsSearcher.bind(this);
} }
private _anomalySegmentsSearcher(point: number, rangeDist: number): AnomalySermentPair[] { private _segmentsSearcher(point: number, rangeDist: number): AnalyticSegmentPair[] {
var result: AnomalySermentPair[] = []; var result: AnalyticSegmentPair[] = [];
this._anomalyTypesSet.anomalyTypes.forEach(at => { this._analyticUnitsSet.items.forEach(at => {
var segs = at.segments.findSegments(point, rangeDist); var segs = at.segments.findSegments(point, rangeDist);
segs.forEach(s => { segs.forEach(s => {
result.push({ anomalyType: at, segment: s }); result.push({ anomalyType: at, segment: s });
@ -60,53 +61,51 @@ export class AnomalyController {
return result; return result;
} }
createAnomalyType() { createNew() {
this._newAnomalyType = new AnomalyType(); this._newAnalyticUnitType = new AnalyticUnit();
this._creatingNewAnomalyType = true; this._creatingNewAnalyticType = true;
this._savingNewAnomalyType = false; this._savingNewAnalyticUnit = false;
} }
async saveNewAnomalyType(metricExpanded: MetricExpanded, datasourceRequest: DatasourceRequest, panelId: number) { async saveNew(metricExpanded: MetricExpanded, datasourceRequest: DatasourceRequest, panelId: number) {
this._savingNewAnomalyType = true; this._savingNewAnalyticUnit = true;
await this._anomalyService.postNewAnomalyType(metricExpanded, datasourceRequest, this._newAnomalyType, panelId); await this._analyticService.postNewAnalyticUnit(metricExpanded, datasourceRequest, this._newAnalyticUnitType, panelId);
this._anomalyTypesSet.addAnomalyType(this._newAnomalyType); this._analyticUnitsSet.addAnomalyType(this._newAnalyticUnitType);
this._creatingNewAnomalyType = false; this._creatingNewAnalyticType = false;
this._savingNewAnomalyType = false; this._savingNewAnalyticUnit = false;
this.runAnomalyTypeAlertEnabledWaiter(this._newAnomalyType); this.runEnabledWaiter(this._newAnalyticUnitType);
this._runAnomalyTypeStatusWaiter(this._newAnomalyType); this._runStatusWaiter(this._newAnalyticUnitType);
} }
get creatingAnomalyType() { return this._creatingNewAnomalyType; } get creatingNew() { return this._creatingNewAnalyticType; }
get savingAnomalyType() { return this._savingNewAnomalyType; } get saving() { return this._savingNewAnalyticUnit; }
get newAnomalyType(): AnomalyType { return this._newAnomalyType; } get newAnalyticUnit(): AnalyticUnit { return this._newAnalyticUnitType; }
get graphLocked() { return this._graphLocked; } get graphLocked() { return this._graphLocked; }
set graphLocked(value) { set graphLocked(value) { this._graphLocked = value; }
this._graphLocked = value;
}
get labelingAnomaly(): AnomalyType { get labelingAnomaly(): AnalyticUnit {
if(this._selectedAnomalyKey === null) { if(this._selectedAnalyticUnitKey === null) {
return null; return null;
} }
return this._anomalyTypesSet.byKey(this._selectedAnomalyKey); return this._analyticUnitsSet.byKey(this._selectedAnalyticUnitKey);
} }
async toggleAnomalyTypeLabelingMode(key: AnomalyKey) { async toggleAnomalyTypeLabelingMode(key: AnalyticUnitKey) {
if(this.labelingAnomaly && this.labelingAnomaly.saving) { if(this.labelingAnomaly && this.labelingAnomaly.saving) {
throw new Error('Can`t toggel during saving'); throw new Error('Can`t toggel during saving');
} }
if(this._selectedAnomalyKey === key) { if(this._selectedAnalyticUnitKey === key) {
return this.disableAnomalyLabeling(); return this.disableLabeling();
} }
await this.disableAnomalyLabeling(); await this.disableLabeling();
this._selectedAnomalyKey = key; this._selectedAnalyticUnitKey = key;
this.labelingAnomaly.selected = true; this.labelingAnomaly.selected = true;
this.toggleAnomalyVisibility(key, true); this.toggleVisibility(key, true);
} }
async disableAnomalyLabeling() { async disableLabeling() {
if(this._selectedAnomalyKey === null) { if(this._selectedAnalyticUnitKey === null) {
return; return;
} }
this.labelingAnomaly.saving = true; this.labelingAnomaly.saving = true;
@ -118,7 +117,7 @@ export class AnomalyController {
var anomaly = this.labelingAnomaly; var anomaly = this.labelingAnomaly;
this.dropLabeling(); this.dropLabeling();
this._runAnomalyTypeStatusWaiter(anomaly); this._runStatusWaiter(anomaly);
} }
undoLabeling() { undoLabeling() {
@ -135,12 +134,12 @@ export class AnomalyController {
this._labelingDataAddedSegments.clear(); this._labelingDataAddedSegments.clear();
this._labelingDataDeletedSegments.clear(); this._labelingDataDeletedSegments.clear();
this.labelingAnomaly.selected = false; this.labelingAnomaly.selected = false;
this._selectedAnomalyKey = null; this._selectedAnalyticUnitKey = null;
this._tempIdCounted = -1; this._tempIdCounted = -1;
} }
get labelingMode(): boolean { get labelingMode(): boolean {
return this._selectedAnomalyKey !== null; return this._selectedAnalyticUnitKey !== null;
} }
get labelingDeleteMode(): boolean { get labelingDeleteMode(): boolean {
@ -155,37 +154,37 @@ export class AnomalyController {
this._labelingDataAddedSegments.addSegment(asegment); this._labelingDataAddedSegments.addSegment(asegment);
} }
get anomalyTypes(): AnomalyType[] { get analyticUnits(): AnalyticUnit[] {
return this._anomalyTypesSet.anomalyTypes; return this._analyticUnitsSet.items;
} }
onAnomalyColorChange(key: AnomalyKey, value) { onAnomalyColorChange(key: AnalyticUnitKey, value) {
this._anomalyTypesSet.byKey(key).color = value; this._analyticUnitsSet.byKey(key).color = value;
} }
fetchAnomalyTypesStatuses() { fetchAnomalyTypesStatuses() {
this.anomalyTypes.forEach(a => this._runAnomalyTypeStatusWaiter(a)); this.analyticUnits.forEach(a => this._runStatusWaiter(a));
} }
async fetchAnomalyTypesSegments(from: number, to: number) { async fetchAnomalyTypesSegments(from: number, to: number) {
if(!_.isNumber(from)) { if(!_.isNumber(+from)) {
throw new Error('from isn`t number'); throw new Error('from isn`t number');
} }
if(!_.isNumber(+to)) { if(!_.isNumber(+to)) {
throw new Error('to isn`t number'); throw new Error('to isn`t number');
} }
var tasks = this.anomalyTypes.map(a => this.fetchSegments(a, from, to)); var tasks = this.analyticUnits.map(a => this.fetchSegments(a, from, to));
return Promise.all(tasks); return Promise.all(tasks);
} }
async fetchSegments(anomalyType: AnomalyType, from: number, to: number): Promise<void> { async fetchSegments(anomalyType: AnalyticUnit, from: number, to: number): Promise<void> {
if(!_.isNumber(from)) { if(!_.isNumber(+from)) {
throw new Error('from isn`t number'); throw new Error('from isn`t number');
} }
if(!_.isNumber(+to)) { if(!_.isNumber(+to)) {
throw new Error('to isn`t number'); throw new Error('to isn`t number');
} }
var allSegmentsList = await this._anomalyService.getSegments(anomalyType.key, from, to); var allSegmentsList = await this._analyticService.getSegments(anomalyType.key, from, to);
var allSegmentsSet = new SegmentArray(allSegmentsList); var allSegmentsSet = new SegmentArray(allSegmentsList);
if(anomalyType.selected) { if(anomalyType.selected) {
this._labelingDataAddedSegments.getSegments().forEach(s => allSegmentsSet.addSegment(s)); this._labelingDataAddedSegments.getSegments().forEach(s => allSegmentsSet.addSegment(s));
@ -207,7 +206,7 @@ export class AnomalyController {
return []; return [];
} }
return this._anomalyService.updateSegments( return this._analyticService.updateSegments(
anomaly.key, this._labelingDataAddedSegments, this._labelingDataDeletedSegments anomaly.key, this._labelingDataAddedSegments, this._labelingDataDeletedSegments
); );
} }
@ -218,8 +217,8 @@ export class AnomalyController {
options.markings = []; options.markings = [];
} }
for(var i = 0; i < this.anomalyTypes.length; i++) { for(var i = 0; i < this.analyticUnits.length; i++) {
var anomalyType = this.anomalyTypes[i]; var anomalyType = this.analyticUnits[i];
var borderColor = addAlphaToRGB(anomalyType.color, REGION_STROKE_ALPHA); var borderColor = addAlphaToRGB(anomalyType.color, REGION_STROKE_ALPHA);
var fillColor = addAlphaToRGB(anomalyType.color, REGION_FILL_ALPHA); var fillColor = addAlphaToRGB(anomalyType.color, REGION_FILL_ALPHA);
var segments = anomalyType.segments.getSegments(); var segments = anomalyType.segments.getSegments();
@ -273,13 +272,13 @@ export class AnomalyController {
} }
removeAnomalyType(key) { removeAnomalyType(key) {
if(key === this._selectedAnomalyKey) { if(key === this._selectedAnalyticUnitKey) {
this.dropLabeling(); this.dropLabeling();
} }
this._anomalyTypesSet.removeAnomalyType(key); this._analyticUnitsSet.removeAnomalyType(key);
} }
private async _runAnomalyTypeStatusWaiter(anomalyType: AnomalyType) { private async _runStatusWaiter(anomalyType: AnalyticUnit) {
if(anomalyType === undefined || anomalyType === null) { if(anomalyType === undefined || anomalyType === null) {
throw new Error('anomalyType not defined'); throw new Error('anomalyType not defined');
} }
@ -290,7 +289,7 @@ export class AnomalyController {
this._statusRunners.add(anomalyType.key); this._statusRunners.add(anomalyType.key);
var statusGenerator = this._anomalyService.getAnomalyTypeStatusGenerator( var statusGenerator = this._analyticService.getAnomalyTypeStatusGenerator(
anomalyType.key, 1000 anomalyType.key, 1000
); );
@ -312,18 +311,18 @@ export class AnomalyController {
this._statusRunners.delete(anomalyType.key); this._statusRunners.delete(anomalyType.key);
} }
async runAnomalyTypeAlertEnabledWaiter(anomalyType: AnomalyType) { async runEnabledWaiter(anomalyType: AnalyticUnit) {
var enabled = await this._anomalyService.getAlertEnabled(anomalyType.key); var enabled = await this._analyticService.getAlertEnabled(anomalyType.key);
if(anomalyType.alertEnabled !== enabled) { if(anomalyType.alertEnabled !== enabled) {
anomalyType.alertEnabled = enabled; anomalyType.alertEnabled = enabled;
this._emitter.emit('anomaly-type-alert-change', anomalyType); this._emitter.emit('anomaly-type-alert-change', anomalyType);
} }
} }
async toggleAnomalyTypeAlertEnabled(anomalyType: AnomalyType) { async toggleAnomalyTypeAlertEnabled(anomalyType: AnalyticUnit) {
var enabled = anomalyType.alertEnabled; var enabled = anomalyType.alertEnabled;
anomalyType.alertEnabled = undefined; anomalyType.alertEnabled = undefined;
await this._anomalyService.setAlertEnabled(anomalyType.key, enabled); await this._analyticService.setAlertEnabled(anomalyType.key, enabled);
anomalyType.alertEnabled = enabled; anomalyType.alertEnabled = enabled;
this._emitter.emit('anomaly-type-alert-change', anomalyType); this._emitter.emit('anomaly-type-alert-change', anomalyType);
} }
@ -333,8 +332,8 @@ export class AnomalyController {
return this._tempIdCounted; return this._tempIdCounted;
} }
public toggleAnomalyVisibility(key: AnomalyKey, value?: boolean) { public toggleVisibility(key: AnalyticUnitKey, value?: boolean) {
var anomaly = this._anomalyTypesSet.byKey(key); var anomaly = this._analyticUnitsSet.byKey(key);
if(value !== undefined) { if(value !== undefined) {
anomaly.visible = value; anomaly.visible = value;
} else { } else {

37
src/graph_renderer.ts

@ -3,13 +3,14 @@ import { GraphTooltip } from './graph_tooltip';
import { ThresholdManager } from './threshold_manager'; import { ThresholdManager } from './threshold_manager';
import { convertValuesToHistogram, getSeriesValues } from './histogram'; import { convertValuesToHistogram, getSeriesValues } from './histogram';
import { import {
AnomalyController, AnalyticController,
REGION_FILL_ALPHA as ANOMALY_REGION_FILL_ALPHA, REGION_FILL_ALPHA as ANOMALY_REGION_FILL_ALPHA,
REGION_STROKE_ALPHA as ANOMALY_REGION_STROKE_ALPHA, REGION_STROKE_ALPHA as ANOMALY_REGION_STROKE_ALPHA,
REGION_DELETE_COLOR_LIGHT as ANOMALY_REGION_DELETE_COLOR_LIGHT, REGION_DELETE_COLOR_LIGHT as ANOMALY_REGION_DELETE_COLOR_LIGHT,
REGION_DELETE_COLOR_DARK as ANOMALY_REGION_DELETE_COLOR_DARK REGION_DELETE_COLOR_DARK as ANOMALY_REGION_DELETE_COLOR_DARK
} from './controllers/anomaly_controller'; } from './controllers/analytic_controller';
import { GraphCtrl } from './module';
import './vendor/flot/jquery.flot'; import './vendor/flot/jquery.flot';
import './vendor/flot/jquery.flot.time'; import './vendor/flot/jquery.flot.time';
@ -38,14 +39,14 @@ const COLOR_SELECTION = '#666';
export class GraphRenderer { export class GraphRenderer {
private _anomalyController: AnomalyController; private _ananlyticController: AnalyticController;
private data: any; private data: any;
private tooltip: GraphTooltip; private tooltip: GraphTooltip;
private thresholdManager: ThresholdManager; private thresholdManager: ThresholdManager;
private panelWidth: number; private panelWidth: number;
private plot: any; private plot: any;
private sortedSeries: any; private sortedSeries: any;
private ctrl: any; private ctrl: GraphCtrl;
private dashboard: any; private dashboard: any;
private panel: any; private panel: any;
// private eventManager; // private eventManager;
@ -71,9 +72,9 @@ export class GraphRenderer {
this.contextSrv = contextSrv; this.contextSrv = contextSrv;
this.scope = scope; this.scope = scope;
this._anomalyController = this.ctrl.anomalyController; this._ananlyticController = this.ctrl.analyticsController;
if(this._anomalyController === undefined) { if(this._ananlyticController === undefined) {
throw new Error('anomalyController is undefined'); throw new Error('ananlyticController is undefined');
} }
@ -85,7 +86,7 @@ export class GraphRenderer {
this.thresholdManager = new ThresholdManager(this.ctrl); this.thresholdManager = new ThresholdManager(this.ctrl);
this.tooltip = new GraphTooltip( this.tooltip = new GraphTooltip(
$elem, this.dashboard, scope, () => this.sortedSeries, $elem, this.dashboard, scope, () => this.sortedSeries,
this._anomalyController.getAnomalySegmentsSearcher() this._ananlyticController.getSegmentsSearcher()
); );
// panel events // panel events
@ -111,18 +112,18 @@ export class GraphRenderer {
if(this._isAnomalyEvent(selectionEvent)) { if(this._isAnomalyEvent(selectionEvent)) {
this.plot.clearSelection(); this.plot.clearSelection();
var id = this._anomalyController.getIdForNewLabelSegment() var id = this._ananlyticController.getIdForNewLabelSegment()
var segment = new Segment( var segment = new Segment(
id, id,
Math.round(selectionEvent.xaxis.from), Math.round(selectionEvent.xaxis.from),
Math.round(selectionEvent.xaxis.to) Math.round(selectionEvent.xaxis.to)
); );
if(this._anomalyController.labelingDeleteMode) { if(this._ananlyticController.labelingDeleteMode) {
this._anomalyController.deleteLabelingAnomalySegmentsInRange( this._ananlyticController.deleteLabelingAnomalySegmentsInRange(
segment.from, segment.to segment.from, segment.to
); );
} else { } else {
this._anomalyController.addLabelSegment(segment); this._ananlyticController.addLabelSegment(segment);
} }
this._renderPanel(); this._renderPanel();
return; return;
@ -186,12 +187,12 @@ export class GraphRenderer {
}); });
$elem.mousedown(e => { $elem.mousedown(e => {
this._anomalyController.graphLocked = true; this._ananlyticController.graphLocked = true;
this._chooseSelectionColor(e); this._chooseSelectionColor(e);
}); });
$(document).mouseup(e => { $(document).mouseup(e => {
this._anomalyController.graphLocked = false; this._ananlyticController.graphLocked = false;
}) })
} }
@ -336,7 +337,7 @@ export class GraphRenderer {
this._configureYAxisOptions(this.data); this._configureYAxisOptions(this.data);
this.thresholdManager.addFlotOptions(this.flotOptions, this.panel); this.thresholdManager.addFlotOptions(this.flotOptions, this.panel);
// this.eventManager.addFlotEvents(this.annotations, this.flotOptions); // this.eventManager.addFlotEvents(this.annotations, this.flotOptions);
this._anomalyController.updateFlotEvents(this.contextSrv.isEditor, this.flotOptions); this._ananlyticController.updateFlotEvents(this.contextSrv.isEditor, this.flotOptions);
this.sortedSeries = this._sortSeries(this.data, this.panel); this.sortedSeries = this._sortSeries(this.data, this.panel);
this._callPlot(true); this._callPlot(true);
@ -347,12 +348,12 @@ export class GraphRenderer {
var fillAlpha = 0.4; var fillAlpha = 0.4;
var strokeAlpha = 0.4; var strokeAlpha = 0.4;
if(this._isAnomalyEvent(e)) { if(this._isAnomalyEvent(e)) {
if(this._anomalyController.labelingDeleteMode) { if(this._ananlyticController.labelingDeleteMode) {
color = this.contextSrv.user.lightTheme ? color = this.contextSrv.user.lightTheme ?
ANOMALY_REGION_DELETE_COLOR_LIGHT : ANOMALY_REGION_DELETE_COLOR_LIGHT :
ANOMALY_REGION_DELETE_COLOR_DARK; ANOMALY_REGION_DELETE_COLOR_DARK;
} else { } else {
color = this._anomalyController.labelingAnomaly.color; color = this._ananlyticController.labelingAnomaly.color;
} }
fillAlpha = ANOMALY_REGION_FILL_ALPHA; fillAlpha = ANOMALY_REGION_FILL_ALPHA;
strokeAlpha = ANOMALY_REGION_STROKE_ALPHA; strokeAlpha = ANOMALY_REGION_STROKE_ALPHA;
@ -814,7 +815,7 @@ export class GraphRenderer {
private _isAnomalyEvent(obj: any) { private _isAnomalyEvent(obj: any) {
return (obj.ctrlKey || obj.metaKey) && return (obj.ctrlKey || obj.metaKey) &&
this.contextSrv.isEditor && this.contextSrv.isEditor &&
this._anomalyController.labelingMode; this._ananlyticController.labelingMode;
} }
} }

4
src/graph_tooltip.ts

@ -1,4 +1,4 @@
import { AnomalyType, AnomalySegment, AnomalySegmentsSearcher } from "model/anomaly"; import { AnalyticSegmentsSearcher } from "model/analytic_unit";
export class GraphTooltip { export class GraphTooltip {
@ -11,7 +11,7 @@ export class GraphTooltip {
constructor( constructor(
private $elem: JQuery<HTMLElement>, private dashboard, private $elem: JQuery<HTMLElement>, private dashboard,
private scope, private getSeriesFn, private scope, private getSeriesFn,
private _anomalySegmentsSearcher: AnomalySegmentsSearcher private _anomalySegmentsSearcher: AnalyticSegmentsSearcher
) { ) {
this.ctrl = scope.ctrl; this.ctrl = scope.ctrl;
this.panel = this.ctrl.panel; this.panel = this.ctrl.panel;

69
src/model/anomaly.ts → src/model/analytic_unit.ts

@ -5,12 +5,12 @@ import { Metric } from './metric';
import _ from 'lodash'; import _ from 'lodash';
export type AnomalySermentPair = { anomalyType: AnomalyType, segment: AnomalySegment }; export type AnalyticSegmentPair = { anomalyType: AnalyticUnit, segment: AnalyticSegment };
export type AnomalySegmentsSearcher = (point: number, rangeDist: number) => AnomalySermentPair[]; export type AnalyticSegmentsSearcher = (point: number, rangeDist: number) => AnalyticSegmentPair[];
export type AnomalyKey = string; export type AnalyticUnitKey = string;
export class AnomalySegment extends Segment { export class AnalyticSegment extends Segment {
constructor(public labeled: boolean, key: SegmentKey, from: number, to: number) { constructor(public labeled: boolean, key: SegmentKey, from: number, to: number) {
super(key, from, to); super(key, from, to);
if(!_.isBoolean(labeled)) { if(!_.isBoolean(labeled)) {
@ -19,12 +19,12 @@ export class AnomalySegment extends Segment {
} }
} }
export class AnomalyType { export class AnalyticUnit {
private _selected: boolean = false; private _selected: boolean = false;
private _deleteMode: boolean = false; private _deleteMode: boolean = false;
private _saving: boolean = false; private _saving: boolean = false;
private _segmentSet = new SegmentArray<AnomalySegment>(); private _segmentSet = new SegmentArray<AnalyticSegment>();
private _status: string; private _status: string;
private _error: string; private _error: string;
private _metric: Metric; private _metric: Metric;
@ -42,7 +42,7 @@ export class AnomalyType {
//this._metric = new Metric(_panelObject.metric); //this._metric = new Metric(_panelObject.metric);
} }
get key(): AnomalyKey { return this.name; } get key(): AnalyticUnitKey { return this.name; }
set name(value: string) { this._panelObject.name = value; } set name(value: string) { this._panelObject.name = value; }
get name(): string { return this._panelObject.name; } get name(): string { return this._panelObject.name; }
@ -74,18 +74,18 @@ export class AnomalyType {
get metric() { return this._metric; } get metric() { return this._metric; }
addLabeledSegment(segment: Segment): AnomalySegment { addLabeledSegment(segment: Segment): AnalyticSegment {
var asegment = new AnomalySegment(true, segment.key, segment.from, segment.to); var asegment = new AnalyticSegment(true, segment.key, segment.from, segment.to);
this._segmentSet.addSegment(asegment); this._segmentSet.addSegment(asegment);
return asegment; return asegment;
} }
removeSegmentsInRange(from: number, to: number): AnomalySegment[] { removeSegmentsInRange(from: number, to: number): AnalyticSegment[] {
return this._segmentSet.removeInRange(from, to); return this._segmentSet.removeInRange(from, to);
} }
get segments(): SegmentsSet<AnomalySegment> { return this._segmentSet; } get segments(): SegmentsSet<AnalyticSegment> { return this._segmentSet; }
set segments(value: SegmentsSet<AnomalySegment>) { set segments(value: SegmentsSet<AnalyticSegment>) {
this._segmentSet.setSegments(value.getSegments()); this._segmentSet.setSegments(value.getSegments());
} }
@ -111,56 +111,51 @@ export class AnomalyType {
get panelObject() { return this._panelObject; } get panelObject() { return this._panelObject; }
get alertEnabled(): boolean { get alertEnabled(): boolean { return this._alertEnabled; }
return this._alertEnabled; set alertEnabled(value) { this._alertEnabled = value;}
}
set alertEnabled(value) {
this._alertEnabled = value;
}
} }
export class AnomalyTypesSet { export class AnalyticUnitsSet {
private _mapAnomalyKeyIndex: Map<AnomalyKey, number>; private _mapKeyIndex: Map<AnalyticUnitKey, number>;
private _anomalyTypes: AnomalyType[]; private _items: AnalyticUnit[];
constructor(private _panelObject: any[]) { constructor(private _panelObject: any[]) {
if(_panelObject === undefined) { if(_panelObject === undefined) {
throw new Error('panel object can`t be undefined'); throw new Error('panel object can`t be undefined');
} }
this._mapAnomalyKeyIndex = new Map<AnomalyKey, number>(); this._mapKeyIndex = new Map<AnalyticUnitKey, number>();
this._anomalyTypes = _panelObject.map(p => new AnomalyType(p)); this._items = _panelObject.map(p => new AnalyticUnit(p));
this._rebuildIndex(); this._rebuildIndex();
} }
get anomalyTypes() { return this._anomalyTypes; } get items() { return this._items; }
addAnomalyType(anomalyType: AnomalyType) { addAnomalyType(anomalyType: AnalyticUnit) {
this._panelObject.push(anomalyType.panelObject); this._panelObject.push(anomalyType.panelObject);
this._mapAnomalyKeyIndex[anomalyType.name] = this._anomalyTypes.length; this._mapKeyIndex[anomalyType.name] = this._items.length;
this._anomalyTypes.push(anomalyType); this._items.push(anomalyType);
} }
removeAnomalyType(key: AnomalyKey) { removeAnomalyType(key: AnalyticUnitKey) {
var index = this._mapAnomalyKeyIndex[key]; var index = this._mapKeyIndex[key];
this._panelObject.splice(index, 1); this._panelObject.splice(index, 1);
this._anomalyTypes.splice(index, 1); this._items.splice(index, 1);
this._rebuildIndex(); this._rebuildIndex();
} }
_rebuildIndex() { _rebuildIndex() {
this._anomalyTypes.forEach((a, i) => { this._items.forEach((a, i) => {
this._mapAnomalyKeyIndex[a.key] = i; this._mapKeyIndex[a.key] = i;
}); });
} }
byKey(key: AnomalyKey): AnomalyType { byKey(key: AnalyticUnitKey): AnalyticUnit {
return this._anomalyTypes[this._mapAnomalyKeyIndex[key]]; return this._items[this._mapKeyIndex[key]];
} }
byIndex(index: number): AnomalyType { byIndex(index: number): AnalyticUnit {
return this._anomalyTypes[index]; return this._items[index];
} }
} }

68
src/module.ts

@ -6,11 +6,11 @@ import template from './template';
import { GraphRenderer } from './graph_renderer'; import { GraphRenderer } from './graph_renderer';
import { GraphLegend } from './graph_legend'; import { GraphLegend } from './graph_legend';
import { DataProcessor } from './data_processor'; import { DataProcessor } from './data_processor';
import { Metric, MetricExpanded } from './model/metric'; import { MetricExpanded } from './model/metric';
import { DatasourceRequest } from './model/datasource'; import { DatasourceRequest } from './model/datasource';
import { AnomalyKey, AnomalyType } from './model/anomaly'; import { AnalyticUnitKey, AnalyticUnit } from './model/analytic_unit';
import { AnomalyService } from './services/anomaly_service'; import { AnalyticService } from './services/analytic_service';
import { AnomalyController } from './controllers/anomaly_controller'; import { AnalyticController } from './controllers/analytic_controller';
import { axesEditorComponent } from './axes_editor'; import { axesEditorComponent } from './axes_editor';
@ -34,6 +34,7 @@ class GraphCtrl extends MetricsPanelCtrl {
alertState: any; alertState: any;
_panelPath: any; _panelPath: any;
_renderError: boolean = false;
annotationsPromise: any; annotationsPromise: any;
dataWarning: any; dataWarning: any;
@ -44,7 +45,7 @@ class GraphCtrl extends MetricsPanelCtrl {
datasourceRequest: DatasourceRequest; datasourceRequest: DatasourceRequest;
patterns: Array<String> = ['General', 'Drops', 'Peaks', 'Jumps']; patterns: Array<String> = ['General', 'Drops', 'Peaks', 'Jumps'];
anomalyTypes = []; // TODO: remove it later. Only for alert tab anomalyTypes = []; // TODO: remove it later. Only for alert tab
anomalyController: AnomalyController; analyticsController: AnalyticController;
_graphRenderer: GraphRenderer; _graphRenderer: GraphRenderer;
_graphLegend: GraphLegend; _graphLegend: GraphLegend;
@ -151,11 +152,11 @@ class GraphCtrl extends MetricsPanelCtrl {
this.processor = new DataProcessor(this.panel); this.processor = new DataProcessor(this.panel);
var anomalyService = new AnomalyService(this.backendURL, backendSrv as BackendSrv); var anomalyService = new AnalyticService(this.backendURL, backendSrv as BackendSrv);
this.runBackendConnectivityCheck(); this.runBackendConnectivityCheck();
this.anomalyController = new AnomalyController(this.panel, anomalyService, this.events); this.analyticsController = new AnalyticController(this.panel, anomalyService, this.events);
this.anomalyTypes = this.panel.anomalyTypes; this.anomalyTypes = this.panel.anomalyTypes;
keybindingSrv.bind('d', this.onDKey.bind(this)); keybindingSrv.bind('d', this.onDKey.bind(this));
@ -168,12 +169,12 @@ class GraphCtrl extends MetricsPanelCtrl {
this.events.on('anomaly-type-alert-change', () => { this.events.on('anomaly-type-alert-change', () => {
this.$scope.$digest() this.$scope.$digest()
}); });
this.events.on('anomaly-type-status-change', async (anomalyType: AnomalyType) => { this.events.on('anomaly-type-status-change', async (anomalyType: AnalyticUnit) => {
if(anomalyType === undefined) { if(anomalyType === undefined) {
throw new Error('anomalyType is undefined'); throw new Error('anomalyType is undefined');
} }
if(anomalyType.status === 'ready') { if(anomalyType.status === 'ready') {
await this.anomalyController.fetchSegments(anomalyType, +this.range.from, +this.range.to); await this.analyticsController.fetchSegments(anomalyType, +this.range.from, +this.range.to);
} }
this.render(this.seriesList); this.render(this.seriesList);
this.$scope.$digest(); this.$scope.$digest();
@ -189,7 +190,7 @@ class GraphCtrl extends MetricsPanelCtrl {
}; };
}); });
this.anomalyController.fetchAnomalyTypesStatuses(); this.analyticsController.fetchAnomalyTypesStatuses();
} }
@ -210,7 +211,7 @@ class GraphCtrl extends MetricsPanelCtrl {
return; return;
} }
var as = new AnomalyService(this.backendURL, this.backendSrv); var as = new AnalyticService(this.backendURL, this.backendSrv);
var isOK = await as.isBackendOk(); var isOK = await as.isBackendOk();
if(!isOK) { if(!isOK) {
this.alertSrv.set( this.alertSrv.set(
@ -307,7 +308,7 @@ class GraphCtrl extends MetricsPanelCtrl {
var loadTasks = [ var loadTasks = [
this.annotationsPromise, this.annotationsPromise,
this.anomalyController.fetchAnomalyTypesSegments(+this.range.from, +this.range.to) this.analyticsController.fetchAnomalyTypesSegments(+this.range.from, +this.range.to)
]; ];
var results = await Promise.all(loadTasks); var results = await Promise.all(loadTasks);
@ -382,7 +383,7 @@ class GraphCtrl extends MetricsPanelCtrl {
} }
} }
if(!this.anomalyController.graphLocked) { if(!this.analyticsController.graphLocked) {
this._graphLegend.render(); this._graphLegend.render();
this._graphRenderer.render(data); this._graphRenderer.render(data);
} }
@ -509,13 +510,13 @@ class GraphCtrl extends MetricsPanelCtrl {
return this._panelPath; return this._panelPath;
} }
createNewAnomalyType() { createNew() {
this.anomalyController.createAnomalyType(); this.analyticsController.createNew();
} }
async saveAnomalyType() { async saveNew() {
this.refresh(); this.refresh();
await this.anomalyController.saveNewAnomalyType( await this.analyticsController.saveNew(
new MetricExpanded(this.panel.datasource, this.panel.targets), new MetricExpanded(this.panel.datasource, this.panel.targets),
this.datasourceRequest, this.datasourceRequest,
this.panel.id this.panel.id
@ -524,17 +525,17 @@ class GraphCtrl extends MetricsPanelCtrl {
this.render(this.seriesList); this.render(this.seriesList);
} }
onAnomalyColorChange(key: AnomalyKey, value) { onColorChange(key: AnalyticUnitKey, value) {
this.anomalyController.onAnomalyColorChange(key, value); this.analyticsController.onAnomalyColorChange(key, value);
this.render(); this.render();
} }
onAnomalyRemove(key) { onRemove(key) {
this.anomalyController.removeAnomalyType(key as string); this.analyticsController.removeAnomalyType(key as string);
this.render(); this.render();
} }
onAnomalyCancelLabeling(key) { onCancelLabeling(key) {
this.$scope.$root.appEvent('confirm-modal', { this.$scope.$root.appEvent('confirm-modal', {
title: 'Clear anomaly labeling', title: 'Clear anomaly labeling',
text2: 'Your changes will be lost.', text2: 'Your changes will be lost.',
@ -542,36 +543,39 @@ class GraphCtrl extends MetricsPanelCtrl {
icon: 'fa-warning', icon: 'fa-warning',
altActionText: 'Save', altActionText: 'Save',
onAltAction: () => { onAltAction: () => {
this.onToggleAnomalyTypeLabelingMode(key); this.onToggleLabelingMode(key);
}, },
onConfirm: () => { onConfirm: () => {
this.anomalyController.undoLabeling(); this.analyticsController.undoLabeling();
this.render(); this.render();
}, },
}); });
} }
async onToggleAnomalyTypeLabelingMode(key) { async onToggleLabelingMode(key) {
await this.anomalyController.toggleAnomalyTypeLabelingMode(key as AnomalyKey); await this.analyticsController.toggleAnomalyTypeLabelingMode(key as AnalyticUnitKey);
this.$scope.$digest(); this.$scope.$digest();
this.render(); this.render();
} }
onDKey() { onDKey() {
if(!this.anomalyController.labelingMode) { if(!this.analyticsController.labelingMode) {
return; return;
} }
this.anomalyController.toggleDeleteMode(); this.analyticsController.toggleDeleteMode();
} }
onAnomalyAlertChange(anomalyType: AnomalyType) { onAnomalyAlertChange(anomalyType: AnalyticUnit) {
this.anomalyController.toggleAnomalyTypeAlertEnabled(anomalyType); this.analyticsController.toggleAnomalyTypeAlertEnabled(anomalyType);
} }
onAnomalyToggleVisibility(key: AnomalyKey) { onToggleVisibility(key: AnalyticUnitKey) {
this.anomalyController.toggleAnomalyVisibility(key); this.analyticsController.toggleVisibility(key);
this.render(); this.render();
} }
get renderError(): boolean { return this._renderError; }
set renderError(value: boolean) { this._renderError = value; }
} }
export { GraphCtrl, GraphCtrl as PanelCtrl }; export { GraphCtrl, GraphCtrl as PanelCtrl };

85
src/partials/tab_analytics.html

@ -1,18 +1,18 @@
<h5> Anomaly Types </h5> <h5> Analytic Units </h5>
<div class="editor-row"> <div class="editor-row">
<div class="gf-form" ng-repeat="anomalyType in ctrl.anomalyController.anomalyTypes"> <div class="gf-form" ng-repeat="analyticUnit in ctrl.analyticsController.analyticUnits">
<label class="gf-form-label width-4"> Name </label> <label class="gf-form-label width-4"> Name </label>
<input <input
type="text" class="gf-form-input max-width-15" type="text" class="gf-form-input max-width-15"
ng-model="anomalyType.name" ng-model="analyticUnit.name"
ng-disabled="true" ng-disabled="true"
> >
<label class="gf-form-label width-8"> Pattern type </label> <label class="gf-form-label width-8"> Type </label>
<div class="gf-form-select-wrapper"> <div class="gf-form-select-wrapper">
<select class="gf-form-input width-12" <select class="gf-form-input width-12"
ng-model="anomalyType.pattern" ng-model="analyticUnit.pattern"
ng-options="pattern as pattern for pattern in ctrl.patterns" ng-options="pattern as pattern for pattern in ctrl.patterns"
ng-disabled="true" ng-disabled="true"
/> />
@ -30,21 +30,21 @@
<label class="gf-form-label width-6"> Color </label> <label class="gf-form-label width-6"> Color </label>
<span class="gf-form-label"> <span class="gf-form-label">
<color-picker <color-picker
color="anomalyType.color" color="analyticUnit.color"
onChange="ctrl.onAnomalyColorChange.bind(ctrl, anomalyType.key)" onChange="ctrl.onAnomalyColorChange.bind(ctrl, analyticUnit.key)"
/> />
</span> </span>
<label class="gf-form-label" ng-style="anomalyType.status === 'learning' && { 'cursor': 'not-allowed' }"> <label class="gf-form-label" ng-style="analyticUnit.status === 'learning' && { 'cursor': 'not-allowed' }">
<a class="pointer" tabindex="1" <a class="pointer" tabindex="1"
ng-click="ctrl.onToggleAnomalyTypeLabelingMode(anomalyType.key)" ng-click="ctrl.onToggleLabelingMode(analyticUnit.key)"
ng-disabled="anomalyType.status === 'learning'" ng-disabled="analyticUnit.status === 'learning'"
> >
<i class="fa fa-bar-chart" ng-if="!anomalyType.saving"></i> <i class="fa fa-bar-chart" ng-if="!analyticUnit.saving"></i>
<i class="fa fa-spinner fa-spin" ng-if="anomalyType.saving"></i> <i class="fa fa-spinner fa-spin" ng-if="analyticUnit.saving"></i>
<b ng-if="anomalyType.selected && !anomalyType.deleteMode && !anomalyType.saving"> labeling </b> <b ng-if="analyticUnit.selected && !analyticUnit.deleteMode && !analyticUnit.saving"> labeling </b>
<b ng-if="anomalyType.selected && anomalyType.deleteMode && !anomalyType.saving"> deleting </b> <b ng-if="analyticUnit.selected && analyticUnit.deleteMode && !analyticUnit.saving"> deleting </b>
<b ng-if="anomalyType.saving" ng-disabled="true"> saving... </b> <b ng-if="analyticUnit.saving" ng-disabled="true"> saving... </b>
</a> </a>
</label> </label>
@ -53,35 +53,35 @@
<label <label
class="gf-form-label text-center" class="gf-form-label text-center"
style="width: 4rem" style="width: 4rem"
ng-if="anomalyType.alertEnabled === undefined" ng-if="analyticUnit.alertEnabled === undefined"
bs-tooltip="'Alarting status isn`t available. Wait please.'" bs-tooltip="'Alarting status isn`t available. Wait please.'"
> >
<i class="fa fa-spinner fa-spin"></i> <i class="fa fa-spinner fa-spin"></i>
</label> </label>
<gf-form-switch <gf-form-switch
ng-if="anomalyType.alertEnabled !== undefined" ng-if="analyticUnit.alertEnabled !== undefined"
on-change="ctrl.onAnomalyAlertChange(anomalyType)" on-change="ctrl.onAnomalyAlertChange(analyticUnit)"
checked="anomalyType.alertEnabled" checked="analyticUnit.alertEnabled"
style="height: 36px;" style="height: 36px;"
/> />
<label class="gf-form-label"> <label class="gf-form-label">
<a <a
ng-if="anomalyType.visible" ng-if="analyticUnit.visible"
ng-disabled="anomalyType.selected" ng-disabled="analyticUnit.selected"
bs-tooltip="'Hide. It`s visible now.'" bs-tooltip="'Hide. It`s visible now.'"
ng-click="ctrl.onAnomalyToggleVisibility(anomalyType.key)" ng-click="ctrl.onToggleVisibility(analyticUnit.key)"
class="pointer" class="pointer"
> >
<i class="fa fa-eye"></i> <i class="fa fa-eye"></i>
</a> </a>
<a <a
ng-if="!anomalyType.visible" ng-if="!analyticUnit.visible"
ng-disabled="anomalyType.selected" ng-disabled="analyticUnit.selected"
bs-tooltip="'Show. It`s hidden now.'" bs-tooltip="'Show. It`s hidden now.'"
ng-click="ctrl.onAnomalyToggleVisibility(anomalyType.key)" ng-click="ctrl.onToggleVisibility(analyticUnit.key)"
class="pointer" class="pointer"
> >
<i class="fa fa-eye-slash"></i> <i class="fa fa-eye-slash"></i>
@ -90,16 +90,16 @@
<label class="gf-form-label"> <label class="gf-form-label">
<a <a
ng-if="!anomalyType.selected" ng-if="!analyticUnit.selected"
ng-click="ctrl.onAnomalyRemove(anomalyType.key)" ng-click="ctrl.onRemove(analyticUnit.key)"
class="pointer" class="pointer"
> >
<i class="fa fa-trash"></i> <i class="fa fa-trash"></i>
</a> </a>
<a <a
ng-if="anomalyType.selected" ng-if="analyticUnit.selected"
ng-click="ctrl.onAnomalyCancelLabeling(anomalyType.key)" ng-click="ctrl.onCancelLabeling(analyticUnit.key)"
class="pointer" class="pointer"
> >
<i class="fa fa-ban"></i> <i class="fa fa-ban"></i>
@ -107,43 +107,42 @@
</label> </label>
<label> <label>
<i ng-if="anomalyType.status === 'learning'" class="grafana-tip fa fa-leanpub ng-scope" bs-tooltip="'Learning'"></i> <i ng-if="analyticUnit.status === 'learning'" class="grafana-tip fa fa-leanpub ng-scope" bs-tooltip="'Learning'"></i>
<i ng-if="anomalyType.status === 'pending'" class="grafana-tip fa fa-list-ul ng-scope" bs-tooltip="'Pending'"></i> <i ng-if="analyticUnit.status === 'pending'" class="grafana-tip fa fa-list-ul ng-scope" bs-tooltip="'Pending'"></i>
<i ng-if="anomalyType.status === 'failed'" class="grafana-tip fa fa-exclamation-circle ng-scope" bs-tooltip="'Error: ' + anomalyType.error"></i> <i ng-if="analyticUnit.status === 'failed'" class="grafana-tip fa fa-exclamation-circle ng-scope" bs-tooltip="'Error: ' + analyticUnit.error"></i>
</label> </label>
</div> </div>
</div> </div>
<div class="editor-row" ng-if="ctrl.anomalyController.creatingAnomalyType"> <div class="editor-row" ng-if="ctrl.analyticsController.creatingNew">
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label width-4"> Name </label> <label class="gf-form-label width-4"> Name </label>
<input <input
type="text" class="gf-form-input max-width-15" type="text" class="gf-form-input max-width-15"
ng-model="ctrl.anomalyController.newAnomalyType.name" ng-model="ctrl.analyticsController.newAnalyticUnit.name"
ng-change="ctrl.onAnomalyNameChange()"
> >
<label class="gf-form-label width-8"> Pattern type </label> <label class="gf-form-label width-8"> Type </label>
<div class="gf-form-select-wrapper"> <div class="gf-form-select-wrapper">
<select class="gf-form-input width-12" <select class="gf-form-input width-12"
ng-model="ctrl.anomalyController.newAnomalyType.pattern" ng-model="ctrl.analyticsController.newAnalyticUnit.pattern"
ng-options="pattern as pattern disable when (pattern !== 'General' && pattern !== 'Drops' && pattern !== 'Peaks') for pattern in ctrl.patterns" ng-options="pattern as pattern disable when (pattern !== 'General' && pattern !== 'Drops' && pattern !== 'Peaks') for pattern in ctrl.patterns"
/> />
</div> </div>
<label class="gf-form-label"> <label class="gf-form-label">
<a class="pointer" tabindex="1" ng-click="ctrl.saveAnomalyType()"> <a class="pointer" tabindex="1" ng-click="ctrl.saveNew()">
<b ng-if="!ctrl.anomalyController.savingAnomalyType"> create </b> <b ng-if="!ctrl.analyticsController.saving"> create </b>
<b ng-if="ctrl.anomalyController.savingAnomalyType" ng-disabled="true"> saving... </b> <b ng-if="ctrl.analyticsController.saving" ng-disabled="true"> saving... </b>
</a> </a>
</label> </label>
</div> </div>
</div> </div>
<div class="gf-form-button-row" ng-if="!ctrl.anomalyController.creatingAnomalyType"> <div class="gf-form-button-row" ng-if="!ctrl.analyticsController.creatingAnalyticUnit">
<button class="btn btn-inverse" ng-click="ctrl.createNewAnomalyType()"> <button class="btn btn-inverse" ng-click="ctrl.createNew()">
<i class="fa fa-plus"></i> <i class="fa fa-plus"></i>
Add an Anomaly Type Add Analytic Unit
</button> </button>
</div> </div>

18
src/services/anomaly_service.ts → src/services/analytic_service.ts

@ -2,17 +2,17 @@ import { Segment, SegmentKey } from '../model/segment';
import { MetricExpanded } from '../model/metric'; import { MetricExpanded } from '../model/metric';
import { DatasourceRequest } from '../model/datasource'; import { DatasourceRequest } from '../model/datasource';
import { SegmentsSet } from '../model/segment_set'; import { SegmentsSet } from '../model/segment_set';
import { AnomalyKey, AnomalyType, AnomalySegment } from '../model/anomaly'; import { AnalyticUnitKey, AnalyticUnit, AnalyticSegment } from '../model/analytic_unit';
import { BackendSrv } from 'grafana/app/core/services/backend_srv'; import { BackendSrv } from 'grafana/app/core/services/backend_srv';
export class AnomalyService { export class AnalyticService {
constructor(private _backendURL: string, private _backendSrv: BackendSrv) { constructor(private _backendURL: string, private _backendSrv: BackendSrv) {
} }
async postNewAnomalyType(metric: MetricExpanded, datasourceRequest: DatasourceRequest, newAnomalyType: AnomalyType, panelId: number) { async postNewAnalyticUnit(metric: MetricExpanded, datasourceRequest: DatasourceRequest, newAnomalyType: AnalyticUnit, panelId: number) {
return this._backendSrv.post( return this._backendSrv.post(
this._backendURL + '/anomalies', this._backendURL + '/anomalies',
{ {
@ -35,7 +35,7 @@ export class AnomalyService {
} }
async updateSegments( async updateSegments(
key: AnomalyKey, addedSegments: SegmentsSet<Segment>, removedSegments: SegmentsSet<Segment> key: AnalyticUnitKey, addedSegments: SegmentsSet<Segment>, removedSegments: SegmentsSet<Segment>
): Promise<SegmentKey[]> { ): Promise<SegmentKey[]> {
const getJSONs = (segs: SegmentsSet<Segment>) => segs.getSegments().map(segment => ({ const getJSONs = (segs: SegmentsSet<Segment>) => segs.getSegments().map(segment => ({
@ -57,7 +57,7 @@ export class AnomalyService {
return data.added_ids as SegmentKey[]; return data.added_ids as SegmentKey[];
} }
async getSegments(key: AnomalyKey, from?: number, to?: number): Promise<AnomalySegment[]> { async getSegments(key: AnalyticUnitKey, from?: number, to?: number): Promise<AnalyticSegment[]> {
var payload: any = { predictor_id: key }; var payload: any = { predictor_id: key };
if(from !== undefined) { if(from !== undefined) {
payload['from'] = from; payload['from'] = from;
@ -73,10 +73,10 @@ export class AnomalyService {
throw new Error('Server didn`t return segments array'); throw new Error('Server didn`t return segments array');
} }
var segments = data.segments as { id: number, start: number, finish: number, labeled: boolean }[]; var segments = data.segments as { id: number, start: number, finish: number, labeled: boolean }[];
return segments.map(s => new AnomalySegment(s.labeled, s.id, s.start, s.finish)); return segments.map(s => new AnalyticSegment(s.labeled, s.id, s.start, s.finish));
} }
async * getAnomalyTypeStatusGenerator(key: AnomalyKey, duration: number) { async * getAnomalyTypeStatusGenerator(key: AnalyticUnitKey, duration: number) {
let statusCheck = async () => { let statusCheck = async () => {
var data = await this._backendSrv.get( var data = await this._backendSrv.get(
this._backendURL + '/anomalies/status', { name: key } this._backendURL + '/anomalies/status', { name: key }
@ -95,7 +95,7 @@ export class AnomalyService {
} }
async getAlertEnabled(key: AnomalyKey): Promise<boolean> { async getAlertEnabled(key: AnalyticUnitKey): Promise<boolean> {
var data = await this._backendSrv.get( var data = await this._backendSrv.get(
this._backendURL + '/alerts', { predictor_id: key } this._backendURL + '/alerts', { predictor_id: key }
); );
@ -103,7 +103,7 @@ export class AnomalyService {
} }
async setAlertEnabled(key: AnomalyKey, value: boolean): Promise<void> { async setAlertEnabled(key: AnalyticUnitKey, value: boolean): Promise<void> {
return this._backendSrv.post( return this._backendSrv.post(
this._backendURL + '/alerts', { predictor_id: key, enable: value } this._backendURL + '/alerts', { predictor_id: key, enable: value }
); );
Loading…
Cancel
Save