Browse Source

Detection in selected time range #235 (#259)

master
rozetko 6 years ago committed by GitHub
parent
commit
b86b28f703
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 65
      src/panel/graph_panel/controllers/analytic_controller.ts
  2. 13
      src/panel/graph_panel/graph_ctrl.ts
  3. 7
      src/panel/graph_panel/graph_renderer.ts
  4. 10
      src/panel/graph_panel/models/analytic_unit.ts
  5. 14
      src/panel/graph_panel/models/detection_status.ts
  6. 18
      src/panel/graph_panel/partials/tab_analytics.html
  7. 13
      src/panel/graph_panel/services/analytic_service.ts

65
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 { SegmentArray } from '../models/segment_array';
import { HasticServerInfo, HasticServerInfoUnknown } from '../models/hastic_server_info'; import { HasticServerInfo, HasticServerInfoUnknown } from '../models/hastic_server_info';
import { Threshold, Condition } from '../models/threshold'; import { Threshold, Condition } from '../models/threshold';
import { DetectionState } from '../models/detection_status';
import text from '../partials/help_section.html'; import text from '../partials/help_section.html';
import { import {
@ -223,6 +224,28 @@ export class AnalyticController {
this.analyticUnits.forEach(a => this._runStatusWaiter(a)); this.analyticUnits.forEach(a => this._runStatusWaiter(a));
} }
async fetchAnalyticUnitsDetectionStatuses(from: number, to: number): Promise<void[]> {
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<void> {
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<void[]> { async fetchAnalyticUnitsSegments(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');
@ -285,7 +308,9 @@ export class AnalyticController {
} }
// TODO: move to renderer // 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) { if(options.grid.markings === undefined) {
options.markings = []; options.markings = [];
} }
@ -343,6 +368,36 @@ export class AnalyticController {
color: segmentBorderColor 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); 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) { public onAnalyticUnitDetectorChange(analyticUnitTypes: any) {
this.newAnalyticUnit.type = analyticUnitTypes[this.newAnalyticUnit.detectorType][0].value; this.newAnalyticUnit.type = analyticUnitTypes[this.newAnalyticUnit.detectorType][0].value;
} }

13
src/panel/graph_panel/graph_ctrl.ts

@ -393,9 +393,13 @@ class GraphCtrl extends MetricsPanelCtrl {
} }
if(this.analyticsController !== undefined) { if(this.analyticsController !== undefined) {
var loadTasks = [ const from = +this.range.from;
const to = +this.range.to;
const loadTasks = [
// this.annotationsPromise, // 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); await Promise.all(loadTasks);
@ -665,6 +669,11 @@ class GraphCtrl extends MetricsPanelCtrl {
this.refresh(); this.refresh();
} }
onToggleInspect(id: AnalyticUnitId) {
this.analyticsController.toggleInspect(id);
this.refresh();
}
private async _updatePanelInfo() { private async _updatePanelInfo() {
let datasource = undefined; let datasource = undefined;
if(this.panel.datasource) { if(this.panel.datasource) {

7
src/panel/graph_panel/graph_renderer.ts

@ -338,7 +338,6 @@ export class GraphRenderer {
this._prepareXAxis(this.panel); this._prepareXAxis(this.panel);
this._configureYAxisOptions(this.data); this._configureYAxisOptions(this.data);
// this.eventManager.addFlotEvents(this.annotations, this.flotOptions); // this.eventManager.addFlotEvents(this.annotations, this.flotOptions);
this._analyticController.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);
@ -442,11 +441,17 @@ export class GraphRenderer {
} }
} }
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) { private _buildFlotOptions(panel) {
const stack = panel.stack ? true : null; const stack = panel.stack ? true : null;
this.flotOptions = { this.flotOptions = {
hooks: { hooks: {
draw: [this._drawHook.bind(this)], draw: [this._drawHook.bind(this)],
drawBackground: [this._drawAnalyticHook.bind(this)],
processOffset: [this._processOffsetHook.bind(this)], processOffset: [this._processOffsetHook.bind(this)],
}, },
legend: { show: false }, legend: { show: false },

10
src/panel/graph_panel/models/analytic_unit.ts

@ -1,6 +1,7 @@
import { SegmentsSet } from './segment_set'; import { SegmentsSet } from './segment_set';
import { SegmentArray } from './segment_array'; import { SegmentArray } from './segment_array';
import { Segment, SegmentId } from './segment'; import { Segment, SegmentId } from './segment';
import { DetectionStatus } from './detection_status';
import { ANALYTIC_UNIT_COLORS, DEFAULT_DELETED_SEGMENT_COLOR } from '../colors'; import { ANALYTIC_UNIT_COLORS, DEFAULT_DELETED_SEGMENT_COLOR } from '../colors';
@ -39,6 +40,8 @@ export class AnalyticUnit {
private _selected: boolean = false; private _selected: boolean = false;
private _saving: boolean = false; private _saving: boolean = false;
private _segmentSet = new SegmentArray<AnalyticSegment>(); private _segmentSet = new SegmentArray<AnalyticSegment>();
private _detectionStatuses: DetectionStatus[];
private _inspect = false;
private _status: string; private _status: string;
private _error: string; private _error: string;
@ -90,6 +93,9 @@ export class AnalyticUnit {
get saving(): boolean { return this._saving; } get saving(): boolean { return this._saving; }
set saving(value: boolean) { this._saving = value; } set saving(value: boolean) { this._saving = value; }
get inspect(): boolean { return this._inspect; }
set inspect(value: boolean) { this._inspect = value; }
get visible(): boolean { get visible(): boolean {
return (this._serverObject.visible === undefined) ? true : this._serverObject.visible return (this._serverObject.visible === undefined) ? true : this._serverObject.visible
} }
@ -113,8 +119,12 @@ export class AnalyticUnit {
this._segmentSet.setSegments(value.getSegments()); this._segmentSet.setSegments(value.getSegments());
} }
get detectionStatuses(): DetectionStatus[] { return this._detectionStatuses; }
set detectionStatuses(value: DetectionStatus[]) { this._detectionStatuses = value; }
get status() { return this._status; } get status() { return this._status; }
set status(value) { set status(value) {
// TODO: use enum
if( if(
value !== '404' && value !== '404' &&
value !== 'READY' && value !== 'READY' &&

14
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
};

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

@ -140,6 +140,23 @@
</a> </a>
</label> </label>
<label class="gf-form-label" ng-if="analyticUnit.status === 'READY' && analyticUnit.visible">
<a
class="pointer"
ng-click="ctrl.onToggleInspect(analyticUnit.id)"
ng-if="!analyticUnit.inspect"
>
Inspect
</a>
<a
class="pointer"
ng-click="ctrl.onToggleInspect(analyticUnit.id)"
ng-if="analyticUnit.inspect"
>
Disable Inspect
</a>
</label>
<label class="gf-form-label"> <label class="gf-form-label">
<a <a
ng-if="!analyticUnit.selected" ng-if="!analyticUnit.selected"
@ -164,7 +181,6 @@
<i ng-if="analyticUnit.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="analyticUnit.status === 'FAILED'" class="grafana-tip fa fa-exclamation-circle ng-scope" bs-tooltip="'Error: ' + analyticUnit.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>

13
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 { AnalyticUnitId, AnalyticUnit, AnalyticSegment } from '../models/analytic_unit';
import { HasticServerInfo, HasticServerInfoUnknown } from '../models/hastic_server_info'; import { HasticServerInfo, HasticServerInfoUnknown } from '../models/hastic_server_info';
import { Threshold } from '../models/threshold'; import { Threshold } from '../models/threshold';
import { DetectionStatus } from '../models/detection_status';
import { isHasticServerResponse, isSupportedServerVersion, SUPPORTED_SERVER_VERSION } from '../../../utlis'; import { isHasticServerResponse, isSupportedServerVersion, SUPPORTED_SERVER_VERSION } from '../../../utlis';
@ -135,6 +136,18 @@ export class AnalyticService {
return data.addedIds as SegmentId[]; return data.addedIds as SegmentId[];
} }
async getDetectionStatus(id: AnalyticUnitId, from: number, to: number): Promise<DetectionStatus[]> {
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<AnalyticSegment[]> { async getSegments(id: AnalyticUnitId, from?: number, to?: number): Promise<AnalyticSegment[]> {
if(id === undefined) { if(id === undefined) {
throw new Error('id is undefined'); throw new Error('id is undefined');

Loading…
Cancel
Save