diff --git a/src/panel/graph_panel/controllers/analytic_controller.ts b/src/panel/graph_panel/controllers/analytic_controller.ts index 93e2c97..705bd30 100644 --- a/src/panel/graph_panel/controllers/analytic_controller.ts +++ b/src/panel/graph_panel/controllers/analytic_controller.ts @@ -106,7 +106,7 @@ export class AnalyticController { async saveNew(metric: MetricExpanded, datasource: DatasourceRequest) { this._savingNewAnalyticUnit = true; - const newAnalyticUnit = createAnalyticUnit(this._newAnalyticUnit.serverObject); + const newAnalyticUnit = createAnalyticUnit(this._newAnalyticUnit.toJSON()); newAnalyticUnit.id = await this._analyticService.postNewAnalyticUnit( newAnalyticUnit, metric, datasource, this._grafanaUrl, this._panelId ); @@ -143,7 +143,8 @@ export class AnalyticController { await this.disableLabeling(); this._selectedAnalyticUnitId = id; this.labelingUnit.selected = true; - this.toggleLabelingMode(LabelingMode.LABELING); + const labelingModes = this.labelingUnit.labelingModes; + this.toggleLabelingMode(labelingModes[0].value); } async disableLabeling() { @@ -196,9 +197,9 @@ export class AnalyticController { this.labelingUnit.labelingMode = labelingMode; } - addLabelSegment(segment: Segment, deleted = false) { - const asegment = this.labelingUnit.addLabeledSegment(segment, deleted); - this._labelingDataAddedSegments.addSegment(asegment); + addSegment(segment: Segment, deleted = false) { + const addedSegment = this.labelingUnit.addSegment(segment, deleted); + this._labelingDataAddedSegments.addSegment(addedSegment); } get analyticUnits(): AnalyticUnit[] { @@ -453,11 +454,7 @@ export class AnalyticController { if(!this.inLabelingMode) { throw new Error(`Can't enter ${labelingMode} mode when labeling mode is disabled`); } - if(this.labelingUnit.labelingMode === labelingMode) { - this.labelingUnit.labelingMode = LabelingMode.LABELING; - } else { - this.labelingUnit.labelingMode = labelingMode; - } + this.labelingUnit.labelingMode = labelingMode; } async removeAnalyticUnit(id: AnalyticUnitId, silent: boolean = false): Promise { @@ -484,7 +481,7 @@ export class AnalyticController { } analyticUnit.saving = true; - await this._analyticService.updateAnalyticUnit(analyticUnit.serverObject); + await this._analyticService.updateAnalyticUnit(analyticUnit.toJSON()); analyticUnit.saving = false; } @@ -681,6 +678,11 @@ export class AnalyticController { .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) { // TODO: looks bad this._newAnalyticUnit.type = analyticUnitTypes[this._newAnalyticUnit.detectorType][0].value; diff --git a/src/panel/graph_panel/graph_ctrl.ts b/src/panel/graph_panel/graph_ctrl.ts index 4b7fc16..60dc7ef 100644 --- a/src/panel/graph_panel/graph_ctrl.ts +++ b/src/panel/graph_panel/graph_ctrl.ts @@ -682,6 +682,11 @@ class GraphCtrl extends MetricsPanelCtrl { this.refresh(); } + onSeasonalityChange(id: AnalyticUnitId) { + this.analyticsController.updateSeasonality(id); + this.refresh(); + } + private async _updatePanelInfo() { let datasource = undefined; if(this.panel.datasource) { diff --git a/src/panel/graph_panel/graph_renderer.ts b/src/panel/graph_panel/graph_renderer.ts index 231695a..c4000fc 100644 --- a/src/panel/graph_panel/graph_renderer.ts +++ b/src/panel/graph_panel/graph_renderer.ts @@ -153,10 +153,10 @@ export class GraphRenderer { this._analyticController.deleteLabelingAnalyticUnitSegmentsInRange( segment.from, segment.to ); - this._analyticController.addLabelSegment(segment, true); + this._analyticController.addSegment(segment, true); } if(this._analyticController.labelingMode === LabelingMode.LABELING) { - this._analyticController.addLabelSegment(segment, false); + this._analyticController.addSegment(segment, false); } if(this._analyticController.labelingMode === LabelingMode.UNLABELING) { this._analyticController.deleteLabelingAnalyticUnitSegmentsInRange( diff --git a/src/panel/graph_panel/models/analytic_units/analytic_unit.ts b/src/panel/graph_panel/models/analytic_units/analytic_unit.ts index b2fddee..1d39cc1 100644 --- a/src/panel/graph_panel/models/analytic_units/analytic_unit.ts +++ b/src/panel/graph_panel/models/analytic_units/analytic_unit.ts @@ -33,6 +33,9 @@ export class AnalyticSegment extends Segment { if(!_.isBoolean(this.labeled)) { 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 }; +const LABELING_MODES = []; + export class AnalyticUnit { private _labelingMode: LabelingMode = LabelingMode.LABELING; @@ -122,10 +127,10 @@ export class AnalyticUnit { this._serverObject.visible = value; } - addLabeledSegment(segment: Segment, deleted: boolean): AnalyticSegment { - const asegment = new AnalyticSegment(!deleted, segment.id, segment.from, segment.to, deleted); - this._segmentSet.addSegment(asegment); - return asegment; + addSegment(segment: Segment, deleted: boolean): AnalyticSegment { + const addedSegment = new AnalyticSegment(!deleted, segment.id, segment.from, segment.to, deleted); + this._segmentSet.addSegment(addedSegment); + return addedSegment; } removeSegmentsInRange(from: number, to: number): AnalyticSegment[] { @@ -172,4 +177,8 @@ export class AnalyticUnit { get serverObject() { return this._serverObject; } + // TODO: make it abstract + get labelingModes() { + return LABELING_MODES; + } } diff --git a/src/panel/graph_panel/models/analytic_units/anomaly_analytic_unit.ts b/src/panel/graph_panel/models/analytic_units/anomaly_analytic_unit.ts index 2d185d6..cacfa1e 100644 --- a/src/panel/graph_panel/models/analytic_units/anomaly_analytic_unit.ts +++ b/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 moment from 'moment'; + +type TimePeriod = { + value: number, + unit: string +}; const DEFAULTS = { detectorType: DetectorType.ANOMALY, type: 'ANOMALY', 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 { constructor(_serverObject?: any) { @@ -21,7 +37,9 @@ export class AnomalyAnalyticUnit extends AnalyticUnit { return { ...baseJSON, 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; } 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; + } } diff --git a/src/panel/graph_panel/models/analytic_units/pattern_analytic_unit.ts b/src/panel/graph_panel/models/analytic_units/pattern_analytic_unit.ts index c0f6787..5222b20 100644 --- a/src/panel/graph_panel/models/analytic_units/pattern_analytic_unit.ts +++ b/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'; @@ -7,6 +7,12 @@ const DEFAULTS = { 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 { constructor(_serverObject?: any) { @@ -20,4 +26,8 @@ export class PatternAnalyticUnit extends AnalyticUnit { ...baseJSON }; } + + get labelingModes() { + return LABELING_MODES; + } } diff --git a/src/panel/graph_panel/models/analytic_units/threshold_analytic_unit.ts b/src/panel/graph_panel/models/analytic_units/threshold_analytic_unit.ts index 3af8a5c..73d8f3a 100644 --- a/src/panel/graph_panel/models/analytic_units/threshold_analytic_unit.ts +++ b/src/panel/graph_panel/models/analytic_units/threshold_analytic_unit.ts @@ -19,6 +19,8 @@ const DEFAULTS = { condition: Condition.ABOVE_OR_EQUAL }; +const LABELING_MODES = []; + export class ThresholdAnalyticUnit extends AnalyticUnit { constructor(_serverObject?: any) { @@ -40,4 +42,8 @@ export class ThresholdAnalyticUnit extends AnalyticUnit { set condition(val: Condition) { this._serverObject.condition = val; } get condition(): Condition { return this._serverObject.condition; } + + get labelingModes() { + return LABELING_MODES; + } } diff --git a/src/panel/graph_panel/partials/tab_analytics.html b/src/panel/graph_panel/partials/tab_analytics.html index b0a927b..b4f0792 100644 --- a/src/panel/graph_panel/partials/tab_analytics.html +++ b/src/panel/graph_panel/partials/tab_analytics.html @@ -1,7 +1,7 @@
- - - -
-
- +
+ +
+ - + > + + +
+ +
- - +
+
- + + - + + + Detect + saving... + + +
- + +
- +
+ +
+
- + +
- - +
+ + +
-