diff --git a/src/panel/graph_panel/controllers/analytic_controller.ts b/src/panel/graph_panel/controllers/analytic_controller.ts index 1e4949d..e11ee5f 100644 --- a/src/panel/graph_panel/controllers/analytic_controller.ts +++ b/src/panel/graph_panel/controllers/analytic_controller.ts @@ -1,21 +1,23 @@ -// Corresponds to https://github.com/hastic/hastic-server/blob/master/server/src/models/analytic_unit.ts +// Corresponds to https://github.com/hastic/hastic-server/blob/master/server/src/models/analytic_units/analytic_unit.ts -import { AnalyticService } from '../services/analytic_service' +import { AnalyticService } from '../services/analytic_service'; import { AnalyticUnitId, AnalyticUnit, - AnalyticUnitsSet, AnalyticSegment, AnalyticSegmentsSearcher, AnalyticSegmentPair, + AnalyticSegment, AnalyticSegmentsSearcher, AnalyticSegmentPair, LabelingMode -} from '../models/analytic_unit'; +} from '../models/analytic_units/analytic_unit'; +import { AnalyticUnitsSet } from '../models/analytic_units/analytic_units_set'; import { MetricExpanded } from '../models/metric'; import { DatasourceRequest } from '../models/datasource'; import { Segment, SegmentId } from '../models/segment'; import { SegmentsSet } from '../models/segment_set'; import { SegmentArray } from '../models/segment_array'; import { HasticServerInfo, HasticServerInfoUnknown } from '../models/hastic_server_info'; -import { Threshold, Condition } from '../models/threshold'; +import { Condition } from '../models/analytic_units/threshold_analytic_unit'; import { DetectionStatus, DETECTION_STATUS_TEXT, DetectionSpan } from '../models/detection'; -import text from '../partials/help_section.html'; +import { createAnalyticUnit } from '../models/analytic_units/utils'; +import helpSectionText from '../partials/help_section.html'; import { ANALYTIC_UNIT_COLORS, @@ -42,7 +44,7 @@ export class AnalyticController { private _labelingDataAddedSegments: SegmentsSet; private _labelingDataRemovedSegments: SegmentsSet; private _newAnalyticUnit: AnalyticUnit = null; - private _creatingNewAnalyticType: boolean = false; + private _creatingNewAnalyticUnit: boolean = false; private _savingNewAnalyticUnit: boolean = false; private _tempIdCounted: number = -1; private _graphLocked: boolean = false; @@ -51,7 +53,6 @@ export class AnalyticController { private _serverInfo: HasticServerInfo; private _currentMetric: MetricExpanded; private _currentDatasource: DatasourceRequest; - private _thresholds: Threshold[]; private _loading = true; constructor( @@ -65,11 +66,9 @@ export class AnalyticController { this._labelingDataRemovedSegments = new SegmentArray(); this._analyticUnitsSet = new AnalyticUnitsSet([]); this.fetchAnalyticUnits(); - this._thresholds = []; - this.updateThresholds(); } - get helpSectionText() { return text; } + get helpSectionText() { return helpSectionText; } get loading() { return this._loading; @@ -90,15 +89,9 @@ export class AnalyticController { return result; } - async sendThresholdParamsToServer(id: AnalyticUnitId) { - await this.saveThreshold(id); - await this._analyticService.runDetect(id); - await this._runStatusWaiter(this._analyticUnitsSet.byId(id)); - } - createNew() { this._newAnalyticUnit = new AnalyticUnit(); - this._creatingNewAnalyticType = true; + this._creatingNewAnalyticUnit = true; this._savingNewAnalyticUnit = false; if(this.analyticUnits.length === 0) { this._newAnalyticUnit.labeledColor = ANALYTIC_UNIT_COLORS[0]; @@ -111,18 +104,17 @@ export class AnalyticController { async saveNew(metric: MetricExpanded, datasource: DatasourceRequest) { this._savingNewAnalyticUnit = true; - this._newAnalyticUnit.id = await this._analyticService.postNewAnalyticUnit( - this._newAnalyticUnit, metric, datasource, this._grafanaUrl, this._panelId + const newAnalyticUnit = createAnalyticUnit(this._newAnalyticUnit.serverObject); + newAnalyticUnit.id = await this._analyticService.postNewAnalyticUnit( + newAnalyticUnit, metric, datasource, this._grafanaUrl, this._panelId ); - if(this._newAnalyticUnit.detectorType === 'threshold') { - await this.saveThreshold(this._newAnalyticUnit.id); - } - this._analyticUnitsSet.addItem(this._newAnalyticUnit); - this._creatingNewAnalyticType = false; + this._analyticUnitsSet.addItem(newAnalyticUnit); + this._creatingNewAnalyticUnit = false; this._savingNewAnalyticUnit = false; + delete this._newAnalyticUnit; } - get creatingNew() { return this._creatingNewAnalyticType; } + get creatingNew() { return this._creatingNewAnalyticUnit; } get saving() { return this._savingNewAnalyticUnit; } get newAnalyticUnit(): AnalyticUnit { return this._newAnalyticUnit; } @@ -326,6 +318,15 @@ export class AnalyticController { _.each(this.analyticUnits, analyticUnit => this._runStatusWaiter(analyticUnit)); } + async runDetect(analyticUnitId: AnalyticUnitId) { + const analyticUnit = this._analyticUnitsSet.byId(analyticUnitId); + analyticUnit.segments.clear(); + analyticUnit.status = null; + await this.saveAnalyticUnit(analyticUnit); + await this._analyticService.runDetect(analyticUnitId); + this._runStatusWaiter(analyticUnit); + } + // TODO: move to renderer updateFlotEvents(isEditMode: boolean, plot: any): void { // We get a reference to flot options so we can change it and it'll be rendered @@ -536,39 +537,6 @@ export class AnalyticController { return null; } - async updateThresholds(): Promise { - if(this._analyticService === undefined) { - return; - } - const ids = _.map(this._panelObject.analyticUnits, (analyticUnit: any) => analyticUnit.id); - const thresholds = await this._analyticService.getThresholds(ids); - this._thresholds = thresholds; - } - - getThreshold(id: AnalyticUnitId): Threshold { - let threshold = _.find(this._thresholds, { id }); - if(threshold === undefined) { - threshold = { - id, - value: 0, - condition: Condition.ABOVE - }; - this._thresholds.push(threshold); - } - return threshold; - } - - async saveThreshold(id: AnalyticUnitId) { - const threshold = this.getThreshold(id); - if(threshold.value === undefined) { - throw new Error('Cannot save threshold with undefined value'); - } - if(threshold.condition === undefined) { - throw new Error('Cannot save threshold with undefined condition'); - } - return this._analyticService.updateThreshold(threshold); - } - public get conditions() { return _.values(Condition); } @@ -687,7 +655,8 @@ export class AnalyticController { } public onAnalyticUnitDetectorChange(analyticUnitTypes: any) { - this.newAnalyticUnit.type = analyticUnitTypes[this.newAnalyticUnit.detectorType][0].value; + // TODO: looks bad + this._newAnalyticUnit.type = analyticUnitTypes[this._newAnalyticUnit.detectorType][0].value; } public async updateServerInfo() { diff --git a/src/panel/graph_panel/graph_ctrl.ts b/src/panel/graph_panel/graph_ctrl.ts index 7550b35..fd20df1 100644 --- a/src/panel/graph_panel/graph_ctrl.ts +++ b/src/panel/graph_panel/graph_ctrl.ts @@ -7,7 +7,7 @@ import { GraphLegend } from './graph_legend'; import { DataProcessor } from './data_processor'; import { MetricExpanded } from './models/metric'; import { DatasourceRequest } from './models/datasource'; -import { AnalyticUnitId, AnalyticUnit, LabelingMode } from './models/analytic_unit'; +import { AnalyticUnitId, AnalyticUnit, LabelingMode } from './models/analytic_units/analytic_unit'; import { AnalyticService } from './services/analytic_service'; import { AnalyticController } from './controllers/analytic_controller'; import { HasticPanelInfo } from './models/hastic_panel_info'; @@ -144,8 +144,7 @@ class GraphCtrl extends MetricsPanelCtrl { // series color overrides aliasColors: {}, // other style overrides - seriesOverrides: [], - thresholds: [] + seriesOverrides: [] }; /** @ngInject */ @@ -581,6 +580,10 @@ class GraphCtrl extends MetricsPanelCtrl { this.analyticsController.redetectAll(); } + async runDetect(analyticUnitId: AnalyticUnitId) { + this.analyticsController.runDetect(analyticUnitId); + } + async saveNew() { try { const datasource = await this._getDatasourceRequest(); @@ -606,7 +609,7 @@ class GraphCtrl extends MetricsPanelCtrl { await this.analyticsController.toggleAnalyticUnitAlert(analyticUnit); } - async onAnalyticUnitNameChange(analyticUnit: AnalyticUnit) { + async onAnalyticUnitChange(analyticUnit: AnalyticUnit) { await this.analyticsController.saveAnalyticUnit(analyticUnit); this.refresh(); } diff --git a/src/panel/graph_panel/graph_renderer.ts b/src/panel/graph_panel/graph_renderer.ts index 0a6501c..231695a 100644 --- a/src/panel/graph_panel/graph_renderer.ts +++ b/src/panel/graph_panel/graph_renderer.ts @@ -1,5 +1,5 @@ import { Segment } from './models/segment'; -import { LabelingMode } from './models/analytic_unit'; +import { LabelingMode } from './models/analytic_units/analytic_unit'; import { GraphTooltip } from './graph_tooltip'; import { convertValuesToHistogram, getSeriesValues } from './histogram'; diff --git a/src/panel/graph_panel/graph_tooltip.ts b/src/panel/graph_panel/graph_tooltip.ts index a7a95f9..1a4c2cb 100644 --- a/src/panel/graph_panel/graph_tooltip.ts +++ b/src/panel/graph_panel/graph_tooltip.ts @@ -1,4 +1,4 @@ -import { AnalyticSegmentsSearcher } from './models/analytic_unit'; +import { AnalyticSegmentsSearcher } from './models/analytic_units/analytic_unit'; export class GraphTooltip { diff --git a/src/panel/graph_panel/models/analytic_unit.ts b/src/panel/graph_panel/models/analytic_units/analytic_unit.ts similarity index 71% rename from src/panel/graph_panel/models/analytic_unit.ts rename to src/panel/graph_panel/models/analytic_units/analytic_unit.ts index a64b4e5..6d36f40 100644 --- a/src/panel/graph_panel/models/analytic_unit.ts +++ b/src/panel/graph_panel/models/analytic_units/analytic_unit.ts @@ -1,13 +1,14 @@ -import { SegmentsSet } from './segment_set'; -import { SegmentArray } from './segment_array'; -import { Segment, SegmentId } from './segment'; -import { DetectionSpan } from './detection'; +import { SegmentsSet } from '../segment_set'; +import { SegmentArray } from '../segment_array'; +import { Segment, SegmentId } from '../segment'; +import { DetectionSpan } from '../detection'; -import { ANALYTIC_UNIT_COLORS, DEFAULT_DELETED_SEGMENT_COLOR } from '../colors'; +import { ANALYTIC_UNIT_COLORS, DEFAULT_DELETED_SEGMENT_COLOR } from '../../colors'; import _ from 'lodash'; +// TODO: move types to ./types export enum DetectorType { PATTERN = 'pattern', THRESHOLD = 'threshold' @@ -34,6 +35,17 @@ export class AnalyticSegment extends Segment { } } +const DEFAULTS = { + id: null, + name: 'AnalyticUnitName', + type: 'GENERAL', + detectorType: DetectorType.PATTERN, + labeledColor: ANALYTIC_UNIT_COLORS[0], + deletedColor: DEFAULT_DELETED_SEGMENT_COLOR, + alert: false, + visible: true +}; + export class AnalyticUnit { private _labelingMode: LabelingMode = LabelingMode.LABELING; @@ -45,22 +57,28 @@ export class AnalyticUnit { private _status: string; private _error: string; - constructor(private _serverObject?: any) { - const defaults = { - name: 'AnalyticUnitName', - labeledColor: ANALYTIC_UNIT_COLORS[0], - deletedColor: DEFAULT_DELETED_SEGMENT_COLOR, - detectorType: DetectorType.PATTERN, - type: 'GENERAL', - alert: false, - id: null, - visible: true - }; - + // TODO: serverObject -> fields + constructor(protected _serverObject?: any) { if(_serverObject === undefined) { - this._serverObject = defaults; + this._serverObject = _.clone(DEFAULTS); } - _.defaults(this._serverObject, defaults); + _.defaults(this._serverObject, DEFAULTS); + } + + toJSON() { + return { + id: this.id, + name: this.name, + // TODO: enum type + // TODO: type -> subType + type: this.type, + // TODO: detectorType -> type + detectorType: this.detectorType, + labeledColor: this.labeledColor, + deletedColor: this.deletedColor, + alert: this.alert, + visible: this.visible, + }; } get id(): AnalyticUnitId { return this._serverObject.id; } @@ -154,47 +172,3 @@ export class AnalyticUnit { get serverObject() { return this._serverObject; } } - -export class AnalyticUnitsSet { - - private _mapIdIndex: Map; - private _items: AnalyticUnit[]; - - constructor(private _serverObject: any[]) { - if(_serverObject === undefined) { - throw new Error('server object can`t be undefined'); - } - this._mapIdIndex = new Map(); - this._items = _serverObject.map(p => new AnalyticUnit(p)); - this._rebuildIndex(); - } - - get items() { return this._items; } - - addItem(item: AnalyticUnit) { - this._serverObject.push(item.serverObject); - this._mapIdIndex[item.id] = this._items.length; - this._items.push(item); - } - - removeItem(id: AnalyticUnitId) { - var index = this._mapIdIndex[id]; - this._serverObject.splice(index, 1); - this._items.splice(index, 1); - this._rebuildIndex(); - } - - _rebuildIndex() { - this._items.forEach((a, i) => { - this._mapIdIndex[a.id] = i; - }); - } - - byId(id: AnalyticUnitId): AnalyticUnit { - return this._items[this._mapIdIndex[id]]; - } - - byIndex(index: number): AnalyticUnit { - return this._items[index]; - } -} diff --git a/src/panel/graph_panel/models/analytic_units/analytic_units_set.ts b/src/panel/graph_panel/models/analytic_units/analytic_units_set.ts new file mode 100644 index 0000000..2ac21b5 --- /dev/null +++ b/src/panel/graph_panel/models/analytic_units/analytic_units_set.ts @@ -0,0 +1,47 @@ +import { AnalyticUnit, AnalyticUnitId } from './analytic_unit'; +import { createAnalyticUnit } from './utils'; + + +export class AnalyticUnitsSet { + + private _mapIdIndex: Map; + private _items: AnalyticUnit[]; + + constructor(private _serverObject: any[]) { + if (_serverObject === undefined) { + throw new Error('server object can`t be undefined'); + } + this._mapIdIndex = new Map(); + this._items = _serverObject.map(p => createAnalyticUnit(p)); + this._rebuildIndex(); + } + + get items() { return this._items; } + + addItem(item: AnalyticUnit) { + this._serverObject.push(item.serverObject); + this._mapIdIndex[item.id] = this._items.length; + this._items.push(item); + } + + removeItem(id: AnalyticUnitId) { + var index = this._mapIdIndex[id]; + this._serverObject.splice(index, 1); + this._items.splice(index, 1); + this._rebuildIndex(); + } + + _rebuildIndex() { + this._items.forEach((a, i) => { + this._mapIdIndex[a.id] = i; + }); + } + + byId(id: AnalyticUnitId): AnalyticUnit { + return this._items[this._mapIdIndex[id]]; + } + + byIndex(index: number): AnalyticUnit { + return this._items[index]; + } +} 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 new file mode 100644 index 0000000..c0f6787 --- /dev/null +++ b/src/panel/graph_panel/models/analytic_units/pattern_analytic_unit.ts @@ -0,0 +1,23 @@ +import { AnalyticUnit, DetectorType } from './analytic_unit'; + +import _ from 'lodash'; + +const DEFAULTS = { + detectorType: DetectorType.PATTERN, + type: 'GENERAL' +}; + +export class PatternAnalyticUnit extends AnalyticUnit { + + constructor(_serverObject?: any) { + super(_serverObject); + _.defaults(this._serverObject, DEFAULTS); + } + + toJSON() { + const baseJSON = super.toJSON(); + return { + ...baseJSON + }; + } +} 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 new file mode 100644 index 0000000..3af8a5c --- /dev/null +++ b/src/panel/graph_panel/models/analytic_units/threshold_analytic_unit.ts @@ -0,0 +1,43 @@ +import { AnalyticUnit, DetectorType } from './analytic_unit'; + +import _ from 'lodash'; + + +export enum Condition { + ABOVE = '>', + ABOVE_OR_EQUAL = '>=', + EQUAL = '=', + LESS_OR_EQUAL = '<=', + LESS = '<', + NO_DATA = 'NO_DATA' +}; + +const DEFAULTS = { + detectorType: DetectorType.THRESHOLD, + type: 'THRESHOLD', + value: 0, + condition: Condition.ABOVE_OR_EQUAL +}; + +export class ThresholdAnalyticUnit extends AnalyticUnit { + + constructor(_serverObject?: any) { + super(_serverObject); + _.defaults(this._serverObject, DEFAULTS); + } + + toJSON() { + const baseJSON = super.toJSON(); + return { + ...baseJSON, + value: this.value, + condition: this.condition + }; + } + + set value(val: number) { this._serverObject.value = val; } + get value(): number { return this._serverObject.value; } + + set condition(val: Condition) { this._serverObject.condition = val; } + get condition(): Condition { return this._serverObject.condition; } +} diff --git a/src/panel/graph_panel/models/analytic_units/utils.ts b/src/panel/graph_panel/models/analytic_units/utils.ts new file mode 100644 index 0000000..512bf5e --- /dev/null +++ b/src/panel/graph_panel/models/analytic_units/utils.ts @@ -0,0 +1,15 @@ +import { AnalyticUnit, DetectorType } from './analytic_unit'; +import { PatternAnalyticUnit } from './pattern_analytic_unit'; +import { ThresholdAnalyticUnit } from './threshold_analytic_unit'; + +export function createAnalyticUnit(serverObject: any): AnalyticUnit { + const detectorType: DetectorType = serverObject.detectorType; + switch(detectorType) { + case DetectorType.PATTERN: + return new PatternAnalyticUnit(serverObject); + case DetectorType.THRESHOLD: + return new ThresholdAnalyticUnit(serverObject); + default: + throw new Error(`Can't create analytic unit with type "${detectorType}"`); + } +} diff --git a/src/panel/graph_panel/models/detection.ts b/src/panel/graph_panel/models/detection.ts index 31f7b12..166f8f0 100644 --- a/src/panel/graph_panel/models/detection.ts +++ b/src/panel/graph_panel/models/detection.ts @@ -1,4 +1,4 @@ -import { AnalyticUnitId } from './analytic_unit'; +import { AnalyticUnitId } from './analytic_units/analytic_unit'; export enum DetectionStatus { READY = 'READY', diff --git a/src/panel/graph_panel/models/threshold.ts b/src/panel/graph_panel/models/threshold.ts deleted file mode 100644 index 1c987c3..0000000 --- a/src/panel/graph_panel/models/threshold.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { AnalyticUnitId } from './analytic_unit'; - -export enum Condition { - ABOVE = '>', - ABOVE_OR_EQUAL = '>=', - EQUAL = '=', - LESS_OR_EQUAL = '<=', - LESS = '<', - NO_DATA = 'NO_DATA' -}; - -export type Threshold = { - id: AnalyticUnitId, - value: number, - condition: Condition -}; diff --git a/src/panel/graph_panel/partials/tab_analytics.html b/src/panel/graph_panel/partials/tab_analytics.html index 0057b59..72c37bc 100644 --- a/src/panel/graph_panel/partials/tab_analytics.html +++ b/src/panel/graph_panel/partials/tab_analytics.html @@ -30,7 +30,7 @@ @@ -97,24 +97,23 @@ /> - +