From b86b28f703610650dc8aba5b94939f62fae0a5b8 Mon Sep 17 00:00:00 2001 From: rozetko Date: Fri, 19 Apr 2019 21:25:40 +0300 Subject: [PATCH] Detection in selected time range #235 (#259) --- .../controllers/analytic_controller.ts | 65 ++++++++++++++++++- src/panel/graph_panel/graph_ctrl.ts | 13 +++- src/panel/graph_panel/graph_renderer.ts | 7 +- src/panel/graph_panel/models/analytic_unit.ts | 10 +++ .../graph_panel/models/detection_status.ts | 14 ++++ .../graph_panel/partials/tab_analytics.html | 18 ++++- .../graph_panel/services/analytic_service.ts | 13 ++++ 7 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 src/panel/graph_panel/models/detection_status.ts diff --git a/src/panel/graph_panel/controllers/analytic_controller.ts b/src/panel/graph_panel/controllers/analytic_controller.ts index 94285ac..446ca4a 100644 --- a/src/panel/graph_panel/controllers/analytic_controller.ts +++ b/src/panel/graph_panel/controllers/analytic_controller.ts @@ -14,6 +14,7 @@ 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 { DetectionState } from '../models/detection_status'; import text from '../partials/help_section.html'; import { @@ -223,6 +224,28 @@ export class AnalyticController { this.analyticUnits.forEach(a => this._runStatusWaiter(a)); } + async fetchAnalyticUnitsDetectionStatuses(from: number, to: number): Promise { + if(!_.isNumber(+from)) { + throw new Error('from isn`t number'); + } + if(!_.isNumber(+to)) { + throw new Error('to isn`t number'); + } + const tasks = this.analyticUnits + .map(analyticUnit => this.fetchDetectionStatus(analyticUnit, from, to)); + return Promise.all(tasks); + } + + async fetchDetectionStatus(analyticUnit: AnalyticUnit, from: number, to: number): Promise { + if(!_.isNumber(+from)) { + throw new Error('from isn`t number'); + } + if(!_.isNumber(+to)) { + throw new Error('to isn`t number'); + } + analyticUnit.detectionStatuses = await this._analyticService.getDetectionStatus(analyticUnit.id, from, to); + } + async fetchAnalyticUnitsSegments(from: number, to: number): Promise { if(!_.isNumber(+from)) { throw new Error('from isn`t number'); @@ -285,7 +308,9 @@ export class AnalyticController { } // TODO: move to renderer - updateFlotEvents(isEditMode: boolean, options: any): void { + updateFlotEvents(isEditMode: boolean, plot: any): void { + // We get a reference to flot options so we can change it and it'll be rendered + let options = plot.getOptions(); if(options.grid.markings === undefined) { options.markings = []; } @@ -343,6 +368,36 @@ export class AnalyticController { color: segmentBorderColor }); }); + + if(!analyticUnit.inspect) { + return; + } + const detectionStatuses = analyticUnit.detectionStatuses; + if(detectionStatuses === undefined) { + return; + } + const minValue = _.min(_.map(plot.getYAxes(), axis => axis.min)); + detectionStatuses.forEach(detectionStatus => { + let underlineColor; + switch(detectionStatus.state) { + case DetectionState.READY: + underlineColor = 'green' + break; + case DetectionState.RUNNING: + underlineColor = 'yellow' + break; + case DetectionState.FAILED: + underlineColor = 'red' + break; + default: + break; + } + options.grid.markings.push({ + xaxis: { from: detectionStatus.from, to: detectionStatus.to }, + color: underlineColor, + yaxis: { from: minValue, to: minValue } + }); + }); } } @@ -504,6 +559,14 @@ export class AnalyticController { await this.saveAnalyticUnit(analyticUnit); } + public async toggleInspect(id: AnalyticUnitId) { + const analyticUnit = this._analyticUnitsSet.byId(id); + if(!analyticUnit.inspect) { + this.analyticUnits.forEach(analyticUnit => analyticUnit.inspect = false); + } + analyticUnit.inspect = !analyticUnit.inspect; + } + public onAnalyticUnitDetectorChange(analyticUnitTypes: any) { 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 17aaf36..e9ef284 100644 --- a/src/panel/graph_panel/graph_ctrl.ts +++ b/src/panel/graph_panel/graph_ctrl.ts @@ -393,9 +393,13 @@ class GraphCtrl extends MetricsPanelCtrl { } if(this.analyticsController !== undefined) { - var loadTasks = [ + const from = +this.range.from; + const to = +this.range.to; + const loadTasks = [ // this.annotationsPromise, - this.analyticsController.fetchAnalyticUnitsSegments(+this.range.from, +this.range.to) + this.analyticsController.fetchAnalyticUnitsSegments(from, to), + // TODO: run detection status waiter if detection state !== 'READY' + this.analyticsController.fetchAnalyticUnitsDetectionStatuses(from, to) ]; await Promise.all(loadTasks); @@ -665,6 +669,11 @@ class GraphCtrl extends MetricsPanelCtrl { this.refresh(); } + onToggleInspect(id: AnalyticUnitId) { + this.analyticsController.toggleInspect(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 56c901e..0a6501c 100644 --- a/src/panel/graph_panel/graph_renderer.ts +++ b/src/panel/graph_panel/graph_renderer.ts @@ -338,7 +338,6 @@ export class GraphRenderer { this._prepareXAxis(this.panel); this._configureYAxisOptions(this.data); // this.eventManager.addFlotEvents(this.annotations, this.flotOptions); - this._analyticController.updateFlotEvents(this.contextSrv.isEditor, this.flotOptions); this.sortedSeries = this._sortSeries(this.data, this.panel); this._callPlot(true); @@ -441,12 +440,18 @@ export class GraphRenderer { this.ctrl.renderingCompleted(); } } + + private _drawAnalyticHook(plot: any) { + // We call updateFlotEvents from hook cause we need access to min Y axis value + this._analyticController.updateFlotEvents(this.contextSrv.isEditor, plot) + } private _buildFlotOptions(panel) { const stack = panel.stack ? true : null; this.flotOptions = { hooks: { draw: [this._drawHook.bind(this)], + drawBackground: [this._drawAnalyticHook.bind(this)], processOffset: [this._processOffsetHook.bind(this)], }, legend: { show: false }, diff --git a/src/panel/graph_panel/models/analytic_unit.ts b/src/panel/graph_panel/models/analytic_unit.ts index 2fe8a8c..6296ffc 100644 --- a/src/panel/graph_panel/models/analytic_unit.ts +++ b/src/panel/graph_panel/models/analytic_unit.ts @@ -1,6 +1,7 @@ import { SegmentsSet } from './segment_set'; import { SegmentArray } from './segment_array'; import { Segment, SegmentId } from './segment'; +import { DetectionStatus } from './detection_status'; import { ANALYTIC_UNIT_COLORS, DEFAULT_DELETED_SEGMENT_COLOR } from '../colors'; @@ -39,6 +40,8 @@ export class AnalyticUnit { private _selected: boolean = false; private _saving: boolean = false; private _segmentSet = new SegmentArray(); + private _detectionStatuses: DetectionStatus[]; + private _inspect = false; private _status: string; private _error: string; @@ -90,6 +93,9 @@ export class AnalyticUnit { get saving(): boolean { return this._saving; } set saving(value: boolean) { this._saving = value; } + get inspect(): boolean { return this._inspect; } + set inspect(value: boolean) { this._inspect = value; } + get visible(): boolean { return (this._serverObject.visible === undefined) ? true : this._serverObject.visible } @@ -113,8 +119,12 @@ export class AnalyticUnit { this._segmentSet.setSegments(value.getSegments()); } + get detectionStatuses(): DetectionStatus[] { return this._detectionStatuses; } + set detectionStatuses(value: DetectionStatus[]) { this._detectionStatuses = value; } + get status() { return this._status; } set status(value) { + // TODO: use enum if( value !== '404' && value !== 'READY' && diff --git a/src/panel/graph_panel/models/detection_status.ts b/src/panel/graph_panel/models/detection_status.ts new file mode 100644 index 0000000..005be0d --- /dev/null +++ b/src/panel/graph_panel/models/detection_status.ts @@ -0,0 +1,14 @@ +import { AnalyticUnitId } from './analytic_unit'; + +export enum DetectionState { + READY = 'READY', + RUNNING = 'RUNNING', + FAILED = 'FAILED' +}; + +export type DetectionStatus = { + id: AnalyticUnitId, + state: DetectionState, + from: number, + to: number +}; diff --git a/src/panel/graph_panel/partials/tab_analytics.html b/src/panel/graph_panel/partials/tab_analytics.html index 2cbee49..0057b59 100644 --- a/src/panel/graph_panel/partials/tab_analytics.html +++ b/src/panel/graph_panel/partials/tab_analytics.html @@ -139,6 +139,23 @@ + + - diff --git a/src/panel/graph_panel/services/analytic_service.ts b/src/panel/graph_panel/services/analytic_service.ts index 4cf26b2..46b19ed 100644 --- a/src/panel/graph_panel/services/analytic_service.ts +++ b/src/panel/graph_panel/services/analytic_service.ts @@ -5,6 +5,7 @@ import { SegmentsSet } from '../models/segment_set'; import { AnalyticUnitId, AnalyticUnit, AnalyticSegment } from '../models/analytic_unit'; import { HasticServerInfo, HasticServerInfoUnknown } from '../models/hastic_server_info'; import { Threshold } from '../models/threshold'; +import { DetectionStatus } from '../models/detection_status'; import { isHasticServerResponse, isSupportedServerVersion, SUPPORTED_SERVER_VERSION } from '../../../utlis'; @@ -135,6 +136,18 @@ export class AnalyticService { return data.addedIds as SegmentId[]; } + async getDetectionStatus(id: AnalyticUnitId, from: number, to: number): Promise { + if(id === undefined) { + throw new Error('id is undefined'); + } + let payload: any = { id, from, to }; + const data = await this.get('/detectionStatus', payload); + if(data.timeranges === undefined) { + throw new Error('Server didn`t return timeranges array'); + } + return data.timeranges; + } + async getSegments(id: AnalyticUnitId, from?: number, to?: number): Promise { if(id === undefined) { throw new Error('id is undefined');