Browse Source

Labeling for anomalies #281 (#299)

master
rozetko 6 years ago committed by GitHub
parent
commit
5a86d1723e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 24
      src/panel/graph_panel/controllers/analytic_controller.ts
  2. 5
      src/panel/graph_panel/graph_ctrl.ts
  3. 4
      src/panel/graph_panel/graph_renderer.ts
  4. 17
      src/panel/graph_panel/models/analytic_units/analytic_unit.ts
  5. 49
      src/panel/graph_panel/models/analytic_units/anomaly_analytic_unit.ts
  6. 12
      src/panel/graph_panel/models/analytic_units/pattern_analytic_unit.ts
  7. 6
      src/panel/graph_panel/models/analytic_units/threshold_analytic_unit.ts
  8. 409
      src/panel/graph_panel/partials/tab_analytics.html

24
src/panel/graph_panel/controllers/analytic_controller.ts

@ -106,7 +106,7 @@ export class AnalyticController {
async saveNew(metric: MetricExpanded, datasource: DatasourceRequest) { async saveNew(metric: MetricExpanded, datasource: DatasourceRequest) {
this._savingNewAnalyticUnit = true; this._savingNewAnalyticUnit = true;
const newAnalyticUnit = createAnalyticUnit(this._newAnalyticUnit.serverObject); const newAnalyticUnit = createAnalyticUnit(this._newAnalyticUnit.toJSON());
newAnalyticUnit.id = await this._analyticService.postNewAnalyticUnit( newAnalyticUnit.id = await this._analyticService.postNewAnalyticUnit(
newAnalyticUnit, metric, datasource, this._grafanaUrl, this._panelId newAnalyticUnit, metric, datasource, this._grafanaUrl, this._panelId
); );
@ -143,7 +143,8 @@ export class AnalyticController {
await this.disableLabeling(); await this.disableLabeling();
this._selectedAnalyticUnitId = id; this._selectedAnalyticUnitId = id;
this.labelingUnit.selected = true; this.labelingUnit.selected = true;
this.toggleLabelingMode(LabelingMode.LABELING); const labelingModes = this.labelingUnit.labelingModes;
this.toggleLabelingMode(labelingModes[0].value);
} }
async disableLabeling() { async disableLabeling() {
@ -196,9 +197,9 @@ export class AnalyticController {
this.labelingUnit.labelingMode = labelingMode; this.labelingUnit.labelingMode = labelingMode;
} }
addLabelSegment(segment: Segment, deleted = false) { addSegment(segment: Segment, deleted = false) {
const asegment = this.labelingUnit.addLabeledSegment(segment, deleted); const addedSegment = this.labelingUnit.addSegment(segment, deleted);
this._labelingDataAddedSegments.addSegment(asegment); this._labelingDataAddedSegments.addSegment(addedSegment);
} }
get analyticUnits(): AnalyticUnit[] { get analyticUnits(): AnalyticUnit[] {
@ -453,11 +454,7 @@ export class AnalyticController {
if(!this.inLabelingMode) { if(!this.inLabelingMode) {
throw new Error(`Can't enter ${labelingMode} mode when labeling mode is disabled`); throw new Error(`Can't enter ${labelingMode} mode when labeling mode is disabled`);
} }
if(this.labelingUnit.labelingMode === labelingMode) { this.labelingUnit.labelingMode = labelingMode;
this.labelingUnit.labelingMode = LabelingMode.LABELING;
} else {
this.labelingUnit.labelingMode = labelingMode;
}
} }
async removeAnalyticUnit(id: AnalyticUnitId, silent: boolean = false): Promise<void> { async removeAnalyticUnit(id: AnalyticUnitId, silent: boolean = false): Promise<void> {
@ -484,7 +481,7 @@ export class AnalyticController {
} }
analyticUnit.saving = true; analyticUnit.saving = true;
await this._analyticService.updateAnalyticUnit(analyticUnit.serverObject); await this._analyticService.updateAnalyticUnit(analyticUnit.toJSON());
analyticUnit.saving = false; analyticUnit.saving = false;
} }
@ -681,6 +678,11 @@ export class AnalyticController {
.forEach(unit => unit.inspect = false); .forEach(unit => unit.inspect = false);
} }
public async updateSeasonality(id: AnalyticUnitId) {
const analyticUnit = this._analyticUnitsSet.byId(id) as AnomalyAnalyticUnit;
await this.saveAnalyticUnit(analyticUnit);
}
public onAnalyticUnitDetectorChange(analyticUnitTypes: any) { public onAnalyticUnitDetectorChange(analyticUnitTypes: any) {
// TODO: looks bad // TODO: looks bad
this._newAnalyticUnit.type = analyticUnitTypes[this._newAnalyticUnit.detectorType][0].value; this._newAnalyticUnit.type = analyticUnitTypes[this._newAnalyticUnit.detectorType][0].value;

5
src/panel/graph_panel/graph_ctrl.ts

@ -682,6 +682,11 @@ class GraphCtrl extends MetricsPanelCtrl {
this.refresh(); this.refresh();
} }
onSeasonalityChange(id: AnalyticUnitId) {
this.analyticsController.updateSeasonality(id);
this.refresh();
}
private async _updatePanelInfo() { private async _updatePanelInfo() {
let datasource = undefined; let datasource = undefined;
if(this.panel.datasource) { if(this.panel.datasource) {

4
src/panel/graph_panel/graph_renderer.ts

@ -153,10 +153,10 @@ export class GraphRenderer {
this._analyticController.deleteLabelingAnalyticUnitSegmentsInRange( this._analyticController.deleteLabelingAnalyticUnitSegmentsInRange(
segment.from, segment.to segment.from, segment.to
); );
this._analyticController.addLabelSegment(segment, true); this._analyticController.addSegment(segment, true);
} }
if(this._analyticController.labelingMode === LabelingMode.LABELING) { if(this._analyticController.labelingMode === LabelingMode.LABELING) {
this._analyticController.addLabelSegment(segment, false); this._analyticController.addSegment(segment, false);
} }
if(this._analyticController.labelingMode === LabelingMode.UNLABELING) { if(this._analyticController.labelingMode === LabelingMode.UNLABELING) {
this._analyticController.deleteLabelingAnalyticUnitSegmentsInRange( this._analyticController.deleteLabelingAnalyticUnitSegmentsInRange(

17
src/panel/graph_panel/models/analytic_units/analytic_unit.ts

@ -33,6 +33,9 @@ export class AnalyticSegment extends Segment {
if(!_.isBoolean(this.labeled)) { if(!_.isBoolean(this.labeled)) {
throw new Error('labeled value is not boolean'); throw new Error('labeled value is not boolean');
} }
if(labeled && deleted) {
throw new Error('Segment can`t be both labeled and deleted');
}
} }
} }
@ -47,6 +50,8 @@ const DEFAULTS = {
visible: true visible: true
}; };
const LABELING_MODES = [];
export class AnalyticUnit { export class AnalyticUnit {
private _labelingMode: LabelingMode = LabelingMode.LABELING; private _labelingMode: LabelingMode = LabelingMode.LABELING;
@ -122,10 +127,10 @@ export class AnalyticUnit {
this._serverObject.visible = value; this._serverObject.visible = value;
} }
addLabeledSegment(segment: Segment, deleted: boolean): AnalyticSegment { addSegment(segment: Segment, deleted: boolean): AnalyticSegment {
const asegment = new AnalyticSegment(!deleted, segment.id, segment.from, segment.to, deleted); const addedSegment = new AnalyticSegment(!deleted, segment.id, segment.from, segment.to, deleted);
this._segmentSet.addSegment(asegment); this._segmentSet.addSegment(addedSegment);
return asegment; return addedSegment;
} }
removeSegmentsInRange(from: number, to: number): AnalyticSegment[] { removeSegmentsInRange(from: number, to: number): AnalyticSegment[] {
@ -172,4 +177,8 @@ export class AnalyticUnit {
get serverObject() { return this._serverObject; } get serverObject() { return this._serverObject; }
// TODO: make it abstract
get labelingModes() {
return LABELING_MODES;
}
} }

49
src/panel/graph_panel/models/analytic_units/anomaly_analytic_unit.ts

@ -1,14 +1,30 @@
import { AnalyticUnit, DetectorType } from './analytic_unit'; import { AnalyticUnit, DetectorType, LabelingMode } from './analytic_unit';
import _ from 'lodash'; import _ from 'lodash';
import moment from 'moment';
type TimePeriod = {
value: number,
unit: string
};
const DEFAULTS = { const DEFAULTS = {
detectorType: DetectorType.ANOMALY, detectorType: DetectorType.ANOMALY,
type: 'ANOMALY', type: 'ANOMALY',
alpha: 0.5, alpha: 0.5,
confidence: 1 confidence: 1,
seasonality: 0,
seasonalityPeriod: {
value: 0,
unit: 'seconds'
}
}; };
const LABELING_MODES = [
{ name: 'Label Negative', value: LabelingMode.DELETING },
{ name: 'Unlabel', value: LabelingMode.UNLABELING }
];
export class AnomalyAnalyticUnit extends AnalyticUnit { export class AnomalyAnalyticUnit extends AnalyticUnit {
constructor(_serverObject?: any) { constructor(_serverObject?: any) {
@ -21,7 +37,9 @@ export class AnomalyAnalyticUnit extends AnalyticUnit {
return { return {
...baseJSON, ...baseJSON,
alpha: this.alpha, alpha: this.alpha,
confidence: this.confidence confidence: this.confidence,
seasonality: this.seasonality,
seasonalityPeriod: this.seasonalityPeriod
}; };
} }
@ -30,4 +48,29 @@ export class AnomalyAnalyticUnit extends AnalyticUnit {
set confidence(val: number) { this._serverObject.confidence = val; } set confidence(val: number) { this._serverObject.confidence = val; }
get confidence(): number { return this._serverObject.confidence; } get confidence(): number { return this._serverObject.confidence; }
get seasonality(): number {
let seasonalityObj = {};
seasonalityObj[this.seasonalityPeriod.unit] = this.seasonalityPeriod.value;
return moment.duration(seasonalityObj).asMilliseconds();
}
set seasonalityPeriod(val: TimePeriod) { this._serverObject.seasonalityPeriod = val; }
get seasonalityPeriod(): TimePeriod { return this._serverObject.seasonalityPeriod; }
// TODO: merge seasonality and hasSeasonality
set hasSeasonality(val: boolean) {
if(val) {
this.seasonalityPeriod = { value: 1, unit: 'seconds' };
} else {
this.seasonalityPeriod = { value: 0, unit: 'seconds' };
}
}
get hasSeasonality(): boolean {
return this.seasonality > 0;
}
get labelingModes() {
return LABELING_MODES;
}
} }

12
src/panel/graph_panel/models/analytic_units/pattern_analytic_unit.ts

@ -1,4 +1,4 @@
import { AnalyticUnit, DetectorType } from './analytic_unit'; import { AnalyticUnit, DetectorType, LabelingMode } from './analytic_unit';
import _ from 'lodash'; import _ from 'lodash';
@ -7,6 +7,12 @@ const DEFAULTS = {
type: 'GENERAL' type: 'GENERAL'
}; };
const LABELING_MODES = [
{ name: 'Label Positive', value: LabelingMode.LABELING },
{ name: 'Label Negative', value: LabelingMode.DELETING },
{ name: 'Unlabel', value: LabelingMode.UNLABELING }
];
export class PatternAnalyticUnit extends AnalyticUnit { export class PatternAnalyticUnit extends AnalyticUnit {
constructor(_serverObject?: any) { constructor(_serverObject?: any) {
@ -20,4 +26,8 @@ export class PatternAnalyticUnit extends AnalyticUnit {
...baseJSON ...baseJSON
}; };
} }
get labelingModes() {
return LABELING_MODES;
}
} }

6
src/panel/graph_panel/models/analytic_units/threshold_analytic_unit.ts

@ -19,6 +19,8 @@ const DEFAULTS = {
condition: Condition.ABOVE_OR_EQUAL condition: Condition.ABOVE_OR_EQUAL
}; };
const LABELING_MODES = [];
export class ThresholdAnalyticUnit extends AnalyticUnit { export class ThresholdAnalyticUnit extends AnalyticUnit {
constructor(_serverObject?: any) { constructor(_serverObject?: any) {
@ -40,4 +42,8 @@ export class ThresholdAnalyticUnit extends AnalyticUnit {
set condition(val: Condition) { this._serverObject.condition = val; } set condition(val: Condition) { this._serverObject.condition = val; }
get condition(): Condition { return this._serverObject.condition; } get condition(): Condition { return this._serverObject.condition; }
get labelingModes() {
return LABELING_MODES;
}
} }

409
src/panel/graph_panel/partials/tab_analytics.html

@ -1,7 +1,7 @@
<div class="gf-form-group"> <div class="gf-form-group">
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label">Select Hastic datasource</label> <label class="gf-form-label">Select Hastic datasource</label>
<select class="gf-form-input max-width-15" <select class="gf-form-input width-15"
ng-model="ctrl.panel.hasticDatasource" ng-model="ctrl.panel.hasticDatasource"
ng-options="ds.id as ds.name for ds in ctrl.hasticDatasources" ng-options="ds.id as ds.name for ds in ctrl.hasticDatasources"
ng-change="ctrl.onHasticDatasourceChange()" ng-change="ctrl.onHasticDatasourceChange()"
@ -20,217 +20,282 @@
<div ng-if="ctrl.analyticsController.serverStatus === true && !ctrl.analyticsController.loading"> <div ng-if="ctrl.analyticsController.serverStatus === true && !ctrl.analyticsController.loading">
<h5> Analytic Units </h5> <h5> Analytic Units </h5>
<div class="editor-row"> <div ng-repeat="analyticUnit in ctrl.analyticsController.analyticUnits">
<div class="gf-form" ng-repeat="analyticUnit in ctrl.analyticsController.analyticUnits"> <div class="gf-form-inline">
<div class="gf-form">
<label class="gf-form-label width-5"> <label class="gf-form-label width-5">
<i class="fa fa-info" bs-tooltip="'Analytic unit id: ' + analyticUnit.id"></i> <i class="fa fa-info" bs-tooltip="'Analytic unit id: ' + analyticUnit.id"></i>
&nbsp; Name &nbsp; Name
</label> </label>
<input <input
type="text" class="gf-form-input max-width-15" type="text" class="gf-form-input width-15"
ng-model="analyticUnit.name" ng-model="analyticUnit.name"
ng-blur="ctrl.onAnalyticUnitChange(analyticUnit)" ng-blur="ctrl.onAnalyticUnitChange(analyticUnit)"
> >
<label class="gf-form-label width-4"> Type </label>
<div class="gf-form-select-wrapper">
<select class="gf-form-input width-10"
ng-model="analyticUnit.type"
ng-options="type.value as type.name for type in ctrl.analyticUnitTypes[analyticUnit.detectorType]"
ng-disabled="true"
/>
</div> </div>
<!-- <div class="gf-form">
<label class="gf-form-label width-6"> Confidence </label> <label class="gf-form-label width-5"> Type </label>
<input <div class="gf-form-select-wrapper">
type="number" class="gf-form-input width-5 ng-valid ng-scope ng-empty ng-dirty ng-valid-number ng-touched" <select class="gf-form-input width-10"
placeholder="auto" bs-tooltip="'Override automatic decimal precision for legend and tooltips'" ng-model="analyticUnit.type"
data-placement="right" ng-model="ctrl.panel.decimals" ng-change="ctrl.render()" ng-model-onblur="" data-original-title="" title="" ng-options="type.value as type.name for type in ctrl.analyticUnitTypes[analyticUnit.detectorType]"
/> ng-disabled="true"
--> />
</div>
</div>
<label class="gf-form-label width-8"> Positive Color </label> <div class="gf-form">
<span class="gf-form-label"> <label class="gf-form-label width-8"> Positive Color </label>
<color-picker <span class="gf-form-label">
color="analyticUnit.labeledColor" <color-picker
onChange="ctrl.onColorChange.bind(ctrl, analyticUnit.id, false)" color="analyticUnit.labeledColor"
/> onChange="ctrl.onColorChange.bind(ctrl, analyticUnit.id, false)"
</span> />
</span>
</div>
<!-- Hack to avoid Grafana's .gf-form-label + .gf-form-label css. Fix for https://github.com/hastic/hastic-grafana-app/issues/192 --> <div class="gf-form"
<div ng-if="analyticUnit.detectorType === 'pattern'"></div> ng-if="analyticUnit.detectorType === 'pattern' || analyticUnit.detectorType === 'anomaly'"
>
<label class="gf-form-label width-8"> Negative Color </label>
<span class="gf-form-label">
<color-picker
color="analyticUnit.deletedColor"
onChange="ctrl.onColorChange.bind(ctrl, analyticUnit.id, true)"
/>
</span>
</div>
<label ng-if="analyticUnit.detectorType === 'pattern'" class="gf-form-label width-8"> Negative Color </label> <div class="gf-form" ng-if="analyticUnit.visible">
<span ng-if="analyticUnit.detectorType === 'pattern'" class="gf-form-label"> <!-- TODO: Remove hack with "margin-bottom: 0" -->
<color-picker <gf-form-switch
color="analyticUnit.deletedColor" class="gf-form"
onChange="ctrl.onColorChange.bind(ctrl, analyticUnit.id, true)" style="margin-bottom: 0"
label="Inspect"
label-class="width-7"
on-change="ctrl.onToggleInspect(analyticUnit.id)"
checked="analyticUnit.inspect"
/> />
</span> </div>
<!-- TODO: move analytic-unit-specific fields rendering to class --> <div
<select class="gf-form-input width-9" class="gf-form"
ng-if="analyticUnit.detectorType === 'threshold'"
ng-model="analyticUnit.condition"
ng-options="type for type in ctrl.analyticsController.conditions"
ng-change="ctrl.onAnalyticUnitChange(analyticUnit)"
/>
<input class="gf-form-input width-5"
ng-if=" ng-if="
analyticUnit.detectorType === 'threshold' && analyticUnit.detectorType === 'pattern' ||
analyticUnit.condition !== 'NO_DATA' (analyticUnit.detectorType === 'anomaly' && analyticUnit.hasSeasonality)
" "
type="number" >
ng-model="analyticUnit.value" <label
ng-blur="ctrl.onAnalyticUnitChange(analyticUnit)" class="gf-form-label pointer"
/> ng-if="!analyticUnit.selected"
ng-style="analyticUnit.status === 'LEARNING' && { 'cursor': 'not-allowed' }"
ng-click="ctrl.onToggleLabelingMode(analyticUnit.id)"
ng-disabled="analyticUnit.status === 'LEARNING'"
>
<i class="fa fa-bar-chart" ng-if="!analyticUnit.saving"></i>
<i class="fa fa-spinner fa-spin" ng-if="analyticUnit.saving"></i>
Label
</label>
<select class="gf-form-input width-12"
ng-if="analyticUnit.selected && !analyticUnit.saving"
ng-model="ctrl.analyticsController.labelingMode"
ng-options="type.value as type.name for type in analyticUnit.labelingModes"
ng-disabled="analyticUnit.status === 'LEARNING'"
/>
</div>
<label class="gf-form-label width-6" ng-if="analyticUnit.detectorType === 'anomaly'"> Alpha </label> <div class="gf-form" ng-if="!analyticUnit.selected">
<input class="gf-form-input width-5" <label class="gf-form-label">
ng-if="analyticUnit.detectorType === 'anomaly'" <a
min="0" ng-if="analyticUnit.visible"
max="1" bs-tooltip="'Hide. It`s visible now.'"
type="number" ng-click="ctrl.onToggleVisibility(analyticUnit.id)"
ng-model="analyticUnit.alpha" class="pointer"
ng-blur="ctrl.onAnalyticUnitChange(analyticUnit)" >
/> <i class="fa fa-eye"></i>
</a>
<label class="gf-form-label width-6" ng-if="analyticUnit.detectorType === 'anomaly'"> Confidence </label> <a
<input class="gf-form-input width-5" ng-if="!analyticUnit.visible"
ng-if="analyticUnit.detectorType === 'anomaly'" bs-tooltip="'Show. It`s hidden now.'"
min="0" ng-click="ctrl.onToggleVisibility(analyticUnit.id)"
type="number" class="pointer"
ng-model="analyticUnit.confidence" >
ng-blur="ctrl.onAnalyticUnitChange(analyticUnit)" <i class="fa fa-eye-slash"></i>
/> </a>
</label>
</div>
<!-- TODO: Remove hack with "margin-bottom: 0" --> <div class="gf-form">
<gf-form-switch ng-if="analyticUnit.visible" <label class="gf-form-label">
class="gf-form" <a
style="margin-bottom: 0" ng-if="!analyticUnit.selected"
label="Inspect" ng-click="ctrl.onRemove(analyticUnit.id)"
label-class="width-5" class="pointer"
on-change="ctrl.onToggleInspect(analyticUnit.id)" >
checked="analyticUnit.inspect" <i class="fa fa-trash"></i>
/> </a>
<label class="gf-form-label" ng-hide="analyticUnit.selected"> <a
<a ng-if="analyticUnit.selected"
ng-if="analyticUnit.visible" ng-click="ctrl.onCancelLabeling(analyticUnit.id)"
bs-tooltip="'Hide. It`s visible now.'" class="pointer"
ng-click="ctrl.onToggleVisibility(analyticUnit.id)" >
class="pointer" <i class="fa fa-ban"></i>
> </a>
<i class="fa fa-eye"></i> </label>
</a> </div>
<a <div class="gf-form" ng-if="analyticUnit.selected && !analyticUnit.saving">
ng-if="!analyticUnit.visible" <!-- Standard way to add conditions to ng-style: https://stackoverflow.com/a/29470439 -->
bs-tooltip="'Show. It`s hidden now.'" <label
ng-click="ctrl.onToggleVisibility(analyticUnit.id)" class="gf-form-label"
class="pointer" ng-style="analyticUnit.status === 'LEARNING' && { 'cursor': 'not-allowed' }"
ng-disabled="analyticUnit.status === 'LEARNING'"
> >
<i class="fa fa-eye-slash"></i> <a class="pointer"
</a> ng-click="ctrl.onToggleLabelingMode(analyticUnit.id)"
</label> ng-disabled="analyticUnit.status === 'LEARNING'"
>
<i class="fa fa-spinner fa-spin" ng-if="analyticUnit.saving"></i>
<b ng-if="!analyticUnit.saving"> Detect </b>
<b ng-if="analyticUnit.saving" ng-disabled="true"> saving... </b>
</a>
</label>
</div>
<label class="gf-form-label" <div class="gf-form"
ng-if="analyticUnit.detectorType === 'threshold' || analyticUnit.detectorType === 'anomaly'" ng-if="
analyticUnit.detectorType === 'threshold' ||
(analyticUnit.detectorType === 'anomaly' && !analyticUnit.hasSeasonality)
"
> >
<a class="pointer" ng-click="ctrl.runDetectInCurrentRange(analyticUnit.id)"> <label class="gf-form-label">
<i class="fa fa-spinner fa-spin" ng-if="analyticUnit.saving"></i> <a class="pointer" ng-click="ctrl.runDetectInCurrentRange(analyticUnit.id)">
<b ng-if="!analyticUnit.saving"> Detect </b> <i class="fa fa-spinner fa-spin" ng-if="analyticUnit.saving"></i>
<b ng-if="analyticUnit.saving" ng-disabled="true"> saving... </b> <b ng-if="!analyticUnit.saving"> Detect </b>
</a> <b ng-if="analyticUnit.saving" ng-disabled="true"> saving... </b>
</label> </a>
</label>
</div>
<label <div class="gf-form">
class="gf-form-label pointer" <label>
ng-if="analyticUnit.detectorType === 'pattern' && !analyticUnit.selected" <i ng-if="analyticUnit.status === 'READY'" class="grafana-tip fa fa-check-circle ng-scope" bs-tooltip="'Ready'"></i>
ng-style="analyticUnit.status === 'LEARNING' && { 'cursor': 'not-allowed' }" <i ng-if="analyticUnit.status === 'LEARNING'" class="grafana-tip fa fa-leanpub ng-scope" bs-tooltip="'Learning'"></i>
ng-click="ctrl.onToggleLabelingMode(analyticUnit.id)" <i ng-if="analyticUnit.status === 'PENDING'" class="grafana-tip fa fa-list-ul ng-scope" bs-tooltip="'Pending'"></i>
ng-disabled="analyticUnit.status === 'LEARNING'" <i ng-if="analyticUnit.status === 'FAILED'" class="grafana-tip fa fa-exclamation-circle ng-scope" bs-tooltip="'Error: ' + analyticUnit.error"></i>
> </label>
<i class="fa fa-bar-chart" ng-if="!analyticUnit.saving"></i> </div>
<i class="fa fa-spinner fa-spin" ng-if="analyticUnit.saving"></i> </div>
Label
</label>
<select class="gf-form-input width-12" <div class="gf-form-inline">
ng-model="ctrl.analyticsController.labelingMode" <div class="gf-form width-20"/>
ng-options="type.value as type.name for type in [ <!-- TODO: move analytic-unit-specific fields rendering to class -->
{ name:'Label Positive', value: 'LABELING' }, <div class="gf-form" ng-if="analyticUnit.detectorType === 'threshold'">
{ name:'Label Negative', value: 'DELETING' }, <label class="gf-form-label width-5"> Condition </label>
{ name:'Unlabel', value: 'UNLABELING' } <select class="gf-form-input width-5"
]" ng-class="{
ng-if="analyticUnit.selected && !analyticUnit.saving" 'width-5': analyticUnit.condition !== 'NO_DATA',
ng-disabled="analyticUnit.status === 'LEARNING'" 'width-10': analyticUnit.condition === 'NO_DATA'
/> }"
ng-model="analyticUnit.condition"
ng-options="type for type in ctrl.analyticsController.conditions"
ng-change="ctrl.onAnalyticUnitChange(analyticUnit)"
/>
<input class="gf-form-input width-5"
ng-if="analyticUnit.condition !== 'NO_DATA'"
type="number"
ng-model="analyticUnit.value"
ng-blur="ctrl.onAnalyticUnitChange(analyticUnit)"
/>
</div>
<!-- Standard way to add conditions to ng-style: https://stackoverflow.com/a/29470439 --> <div class="gf-form" ng-if="analyticUnit.detectorType === 'anomaly' && !analyticUnit.selected">
<label <label class="gf-form-label width-5"> Alpha </label>
class="gf-form-label" <input class="gf-form-input width-10"
ng-style="analyticUnit.status === 'LEARNING' && { 'cursor': 'not-allowed' }" ng-hide="analyticUnit.selected"
ng-if="analyticUnit.selected && !analyticUnit.saving" min="0"
ng-disabled="analyticUnit.status === 'LEARNING'" max="1"
> type="number"
<a class="pointer" ng-model="analyticUnit.alpha"
ng-click="ctrl.onToggleLabelingMode(analyticUnit.id)" ng-blur="ctrl.onAnalyticUnitChange(analyticUnit)"
ng-disabled="analyticUnit.status === 'LEARNING'" />
> </div>
<i class="fa fa-spinner fa-spin" ng-if="analyticUnit.saving"></i>
<b ng-if="!analyticUnit.saving"> Detect </b>
<b ng-if="analyticUnit.saving" ng-disabled="true"> saving... </b>
</a>
</label>
<label class="gf-form-label"> <div class="gf-form" ng-if="analyticUnit.detectorType === 'anomaly' && !analyticUnit.selected">
<a <label class="gf-form-label width-6"> Confidence </label>
ng-if="!analyticUnit.selected" <input class="gf-form-input width-5"
ng-click="ctrl.onRemove(analyticUnit.id)" min="0"
class="pointer" type="number"
> ng-model="analyticUnit.confidence"
<i class="fa fa-trash"></i> ng-blur="ctrl.onAnalyticUnitChange(analyticUnit)"
</a> />
</div>
<a <div class="gf-form" ng-if="analyticUnit.detectorType === 'anomaly' && !analyticUnit.selected">
ng-if="analyticUnit.selected" <!-- TODO: Remove hack with "margin-bottom: 0" -->
ng-click="ctrl.onCancelLabeling(analyticUnit.id)" <gf-form-switch
class="pointer" class="gf-form"
style="margin-bottom: 0"
label="Seasonality"
label-class="width-7"
on-change="ctrl.onAnalyticUnitChange(analyticUnit)"
checked="analyticUnit.hasSeasonality"
/>
</div>
<div
class="gf-form"
ng-if="analyticUnit.detectorType === 'anomaly' && analyticUnit.hasSeasonality && !analyticUnit.selected"
>
<label class="gf-form-label width-9"> Seasonality Period </label>
<input
type="number" class="gf-form-input width-5"
ng-model="analyticUnit.seasonalityPeriod.value"
ng-blur="ctrl.onSeasonalityChange(analyticUnit.id)"
min="0"
> >
<i class="fa fa-ban"></i> </div>
</a>
</label>
<label> <div class="gf-form"
<i ng-if="analyticUnit.status === 'READY'" class="grafana-tip fa fa-check-circle ng-scope" bs-tooltip="'Ready'"></i> ng-if="
<i ng-if="analyticUnit.status === 'LEARNING'" class="grafana-tip fa fa-leanpub ng-scope" bs-tooltip="'Learning'"></i> analyticUnit.detectorType === 'anomaly' &&
<i ng-if="analyticUnit.status === 'PENDING'" class="grafana-tip fa fa-list-ul ng-scope" bs-tooltip="'Pending'"></i> analyticUnit.hasSeasonality &&
<i ng-if="analyticUnit.status === 'FAILED'" class="grafana-tip fa fa-exclamation-circle ng-scope" bs-tooltip="'Error: ' + analyticUnit.error"></i> !analyticUnit.selected
</label> "
>
<div class="gf-form-select-wrapper">
<!-- TODO: move periods from ng-options -->
<select class="gf-form-input width-8"
ng-model="analyticUnit.seasonalityPeriod.unit"
ng-change="ctrl.onSeasonalityChange(analyticUnit.id)"
ng-options="type for type in ['seconds', 'minutes', 'days', 'hours', 'days', 'years']"
/>
</div>
</div>
</div> </div>
</div> </div>
<div class="editor-row" ng-if="ctrl.analyticsController.creatingNew"> <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-5"> Name </label>
<input <input
type="text" class="gf-form-input max-width-15" type="text" class="gf-form-input width-15"
ng-model="ctrl.analyticsController.newAnalyticUnit.name" ng-model="ctrl.analyticsController.newAnalyticUnit.name"
> >
<label class="gf-form-label width-8"> Detector Type </label> <label class="gf-form-label width-8"> Detector Type </label>
<div class="gf-form-select-wrapper"> <div class="gf-form-select-wrapper">
<select class="gf-form-input width-10" <select class="gf-form-input width-7"
ng-model="ctrl.analyticsController.newAnalyticUnit.detectorType" ng-model="ctrl.analyticsController.newAnalyticUnit.detectorType"
ng-options="analyticUnitDetectorType for analyticUnitDetectorType in ctrl.analyticUnitDetectorTypes" ng-options="analyticUnitDetectorType for analyticUnitDetectorType in ctrl.analyticUnitDetectorTypes"
ng-change="ctrl.analyticsController.onAnalyticUnitDetectorChange(ctrl.analyticUnitTypes);" ng-change="ctrl.analyticsController.onAnalyticUnitDetectorChange(ctrl.analyticUnitTypes);"
/> />
</div> </div>
<label class="gf-form-label width-8"> Type </label> <label class="gf-form-label width-5"> Type </label>
<div class="gf-form-select-wrapper"> <div class="gf-form-select-wrapper">
<select class="gf-form-input width-10" <select class="gf-form-input width-10"
ng-model="ctrl.analyticsController.newAnalyticUnit.type" ng-model="ctrl.analyticsController.newAnalyticUnit.type"
@ -256,7 +321,7 @@
Add Analytic Unit Add Analytic Unit
</button> </button>
</div> </div>
<div class="gf-form-button-row"> <div class="gf-form-button-row" ng-if="ctrl.analyticsController.analyticUnits.length > 0">
<button class="gf-form-label width-12 pointer" ng-click="ctrl.redetectAll()"> <button class="gf-form-label width-12 pointer" ng-click="ctrl.redetectAll()">
Re-detect all analytic units Re-detect all analytic units
</button> </button>

Loading…
Cancel
Save