diff --git a/README.md b/README.md index 79d1284..7ce9591 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,8 @@ A version of Grafana's default Graph Panel for rendering and labeling Hastic's patterns. -**Please note that we are still in alpha, so features are subject to change** - - See also: * [Getting started](https://github.com/hastic/hastic-grafana-app/wiki/Getting-started) * [Wiki](https://github.com/hastic/hastic-grafana-app/wiki) @@ -27,16 +24,11 @@ See also: * [Installation from source](https://github.com/hastic/hastic-grafana-app/wiki/Installation-from-source) * [Changelog](https://github.com/hastic/hastic-grafana-app/wiki/Changelog) - # Prerequisites * [hastic-server](https://github.com/hastic/hastic-server) * [Grafana >= 5.4.0](https://grafana.com/grafana/download) +## Support and Consulting -# Credits - -Based on - -* [grafana-plugin-template-webpack-typescript](https://github.com/CorpGlory/grafana-plugin-template-webpack-typescript) -* [@types/grafana](https://github.com/CorpGlory/types-grafana) +Commercial support, consulting, professional services — please send us your inquiry at ping@hastic.io diff --git a/package.json b/package.json index c590768..afce232 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "grafana-hastic-app", - "version": "0.2.8", + "version": "0.3.0", "description": "Hastic app: labeling and rendeting analytics from hastic-server", "main": "dist/module", "scripts": { @@ -41,6 +41,7 @@ "ts-jest": "^22.4.6", "ts-loader": "^4.2.0", "typescript": "^2.8.3", + "url-parse": "^1.4.4", "webpack": "4.7.0", "webpack-cli": "^2.1.2" }, diff --git a/src/config/config_ctrl.ts b/src/config/config_ctrl.ts index 85e2c38..6e125a4 100644 --- a/src/config/config_ctrl.ts +++ b/src/config/config_ctrl.ts @@ -1,13 +1,36 @@ import template from './template.html'; - +import { normalizeUrl } from '../utlis'; class ConfigCtrl { static template = template; appModel: any; + appEditCtrl: any; + constructor() { if(this.appModel.jsonData === undefined) { this.appModel.jsonData = {}; } + + this.appEditCtrl.setPreUpdateHook(this.preUpdate.bind(this)); + this.appEditCtrl.setPostUpdateHook(this.postUpdate.bind(this)); + } + + preUpdate() { + this.normalizeUrl(); + return Promise.resolve(); + } + + postUpdate() { + // TODO: check whether hasticServerUrl is accessible + if(!this.appModel.enabled) { + return Promise.resolve(); + } + + return { message: 'Hastic app installed!' }; + } + + normalizeUrl() { + this.appModel.jsonData.hasticServerUrl = normalizeUrl(this.appModel.jsonData.hasticServerUrl); } } diff --git a/src/config/template.html b/src/config/template.html index 2312aa4..309c183 100644 --- a/src/config/template.html +++ b/src/config/template.html @@ -2,6 +2,11 @@
- +
diff --git a/src/panel/graph_panel/axes_editor.ts b/src/panel/graph_panel/axes_editor.ts index 80677e0..c252d9c 100644 --- a/src/panel/graph_panel/axes_editor.ts +++ b/src/panel/graph_panel/axes_editor.ts @@ -82,7 +82,7 @@ export function axesEditorComponent() { return { restrict: 'E', scope: true, - templateUrl: 'public/plugins/hastic-graph-panel/partials/axes_editor.html', + templateUrl: 'public/plugins/corpglory-hastic-app/panel/graph_panel/partials/axes_editor.html', controller: AxesEditorCtrl, }; } diff --git a/src/panel/graph_panel/colors.ts b/src/panel/graph_panel/colors.ts index 33b9f9f..451fd17 100644 --- a/src/panel/graph_panel/colors.ts +++ b/src/panel/graph_panel/colors.ts @@ -76,6 +76,16 @@ export const ANALYTIC_UNIT_COLORS = [ '#f8c171', ]; +export const DEFAULT_DELETED_SEGMENT_COLOR = '#00f0ff'; +export const REGION_UNLABEL_COLOR_LIGHT = '#d1d1d1'; +export const REGION_UNLABEL_COLOR_DARK = 'white'; +export const LABELED_SEGMENT_BORDER_COLOR = 'black'; +export const DELETED_SEGMENT_BORDER_COLOR = 'black'; + +export const SEGMENT_FILL_ALPHA = 0.5; +export const SEGMENT_STROKE_ALPHA = 0.8; +export const LABELING_MODE_ALPHA = 0.7; + export function hexToHsl(color) { return tinycolor(color).toHsl(); } @@ -85,4 +95,4 @@ export function hslToHex(color) { } -export default colors; \ No newline at end of file +export default colors; diff --git a/src/panel/graph_panel/controllers/analytic_controller.ts b/src/panel/graph_panel/controllers/analytic_controller.ts index 6991cc0..285f1f8 100644 --- a/src/panel/graph_panel/controllers/analytic_controller.ts +++ b/src/panel/graph_panel/controllers/analytic_controller.ts @@ -4,7 +4,8 @@ import { AnalyticService } from '../services/analytic_service' import { AnalyticUnitId, AnalyticUnit, - AnalyticUnitsSet, AnalyticSegment, AnalyticSegmentsSearcher, AnalyticSegmentPair + AnalyticUnitsSet, AnalyticSegment, AnalyticSegmentsSearcher, AnalyticSegmentPair, + LabelingMode } from '../models/analytic_unit'; import { MetricExpanded } from '../models/metric'; import { DatasourceRequest } from '../models/datasource'; @@ -13,30 +14,29 @@ import { SegmentsSet } from '../models/segment_set'; import { SegmentArray } from '../models/segment_array'; import { ServerInfo } from '../models/info'; import { Threshold, Condition } from '../models/threshold'; +import text from '../partials/help_section.html'; -import { ANALYTIC_UNIT_COLORS } from '../colors'; +import { + ANALYTIC_UNIT_COLORS, + LABELED_SEGMENT_BORDER_COLOR, + DELETED_SEGMENT_BORDER_COLOR, + SEGMENT_FILL_ALPHA, + SEGMENT_STROKE_ALPHA, + LABELING_MODE_ALPHA +} from '../colors'; import { Emitter } from 'grafana/app/core/utils/emitter'; import _ from 'lodash'; import * as tinycolor from 'tinycolor2'; -export const REGION_FILL_ALPHA = 0.7; -export const REGION_STROKE_ALPHA = 0.9; -export const REGION_DELETE_COLOR_LIGHT = '#d1d1d1'; -export const REGION_DELETE_COLOR_DARK = 'white'; -const LABELED_SEGMENT_BORDER_COLOR = 'black'; -const DELETED_SEGMENT_FILL_COLOR = 'black'; -const DELETED_SEGMENT_BORDER_COLOR = 'black'; - - export class AnalyticController { private _analyticUnitsSet: AnalyticUnitsSet; private _selectedAnalyticUnitId: AnalyticUnitId = null; private _labelingDataAddedSegments: SegmentsSet; - private _labelingDataDeletedSegments: SegmentsSet; + private _labelingDataRemovedSegments: SegmentsSet; private _newAnalyticUnit: AnalyticUnit = null; private _creatingNewAnalyticType: boolean = false; private _savingNewAnalyticUnit: boolean = false; @@ -53,7 +53,7 @@ export class AnalyticController { _panelObject.analyticUnits = _panelObject.anomalyTypes || []; } this._labelingDataAddedSegments = new SegmentArray(); - this._labelingDataDeletedSegments = new SegmentArray(); + this._labelingDataRemovedSegments = new SegmentArray(); this._analyticUnitsSet = new AnalyticUnitsSet(this._panelObject.analyticUnits); this._thresholds = []; this.updateThresholds(); @@ -61,6 +61,8 @@ export class AnalyticController { // this.analyticUnits.forEach(a => this.runEnabledWaiter(a)); } + get helpSectionText() { return text; } + getSegmentsSearcher(): AnalyticSegmentsSearcher { return this._segmentsSearcher.bind(this); } @@ -78,6 +80,7 @@ export class AnalyticController { async sendThresholdParamsToServer(id: AnalyticUnitId) { await this.saveThreshold(id); + await this._analyticService.runDetect(id); await this._runStatusWaiter(this._analyticUnitsSet.byId(id)); } @@ -86,11 +89,11 @@ export class AnalyticController { this._creatingNewAnalyticType = true; this._savingNewAnalyticUnit = false; if (this.analyticUnits.length === 0) { - this._newAnalyticUnit.color = ANALYTIC_UNIT_COLORS[0]; + this._newAnalyticUnit.labeledColor = ANALYTIC_UNIT_COLORS[0]; } else { - let colorIndex = ANALYTIC_UNIT_COLORS.indexOf(_.last(this.analyticUnits).color) + 1; + let colorIndex = ANALYTIC_UNIT_COLORS.indexOf(_.last(this.analyticUnits).labeledColor) + 1; colorIndex %= ANALYTIC_UNIT_COLORS.length; - this._newAnalyticUnit.color = ANALYTIC_UNIT_COLORS[colorIndex]; + this._newAnalyticUnit.labeledColor = ANALYTIC_UNIT_COLORS[colorIndex]; } } @@ -105,8 +108,9 @@ export class AnalyticController { this._analyticUnitsSet.addItem(this._newAnalyticUnit); this._creatingNewAnalyticType = false; this._savingNewAnalyticUnit = false; - // this.runEnabledWaiter(this._newAnalyticUnit); - this._runStatusWaiter(this._newAnalyticUnit); + if(this._newAnalyticUnit.detectorType !== 'threshold') { + this._runStatusWaiter(this._newAnalyticUnit); + } } get creatingNew() { return this._creatingNewAnalyticType; } @@ -136,6 +140,7 @@ export class AnalyticController { await this.disableLabeling(); this._selectedAnalyticUnitId = id; this.labelingUnit.selected = true; + this.toggleLabelingMode(LabelingMode.LABELING); this.toggleVisibility(id, true); } @@ -160,33 +165,37 @@ export class AnalyticController { this._labelingDataAddedSegments.getSegments().forEach(s => { this.labelingUnit.segments.remove(s.id); }); - this._labelingDataDeletedSegments.getSegments().forEach(s => { - s.deleted = false; + this._labelingDataRemovedSegments.getSegments().forEach(s => { + this.labelingUnit.segments.addSegment(s); }); this.dropLabeling(); } dropLabeling() { this._labelingDataAddedSegments.clear(); - this._labelingDataDeletedSegments.clear(); + this._labelingDataRemovedSegments.clear(); this.labelingUnit.selected = false; this._selectedAnalyticUnitId = null; this._tempIdCounted = -1; } - get labelingMode(): boolean { + get inLabelingMode(): boolean { return this._selectedAnalyticUnitId !== null; } - get labelingDeleteMode(): boolean { - if(!this.labelingMode) { - return false; + get labelingMode(): LabelingMode { + if(!this.inLabelingMode) { + return LabelingMode.NOT_IN_LABELING_MODE; } - return this.labelingUnit.deleteMode; + return this.labelingUnit.labelingMode; } - addLabelSegment(segment: Segment, deleted?: boolean) { - var asegment = this.labelingUnit.addLabeledSegment(segment, deleted); + set labelingMode(labelingMode: LabelingMode) { + this.labelingUnit.labelingMode = labelingMode; + } + + addLabelSegment(segment: Segment, deleted = false) { + const asegment = this.labelingUnit.addLabeledSegment(segment, deleted); this._labelingDataAddedSegments.addSegment(asegment); } @@ -194,11 +203,15 @@ export class AnalyticController { return this._analyticUnitsSet.items; } - onAnalyticUnitColorChange(id: AnalyticUnitId, value: string) { + onAnalyticUnitColorChange(id: AnalyticUnitId, value: string, deleted: boolean) { if(id === undefined) { throw new Error('id is undefined'); } - this._analyticUnitsSet.byId(id).color = value; + if(deleted) { + this._analyticUnitsSet.byId(id).deletedColor = value; + } else { + this._analyticUnitsSet.byId(id).labeledColor = value; + } } fetchAnalyticUnitsStatuses() { @@ -227,7 +240,7 @@ export class AnalyticController { var allSegmentsSet = new SegmentArray(allSegmentsList); if(analyticUnit.selected) { this._labelingDataAddedSegments.getSegments().forEach(s => allSegmentsSet.addSegment(s)); - this._labelingDataDeletedSegments.getSegments().forEach(s => allSegmentsSet.remove(s.id)); + this._labelingDataRemovedSegments.getSegments().forEach(s => allSegmentsSet.remove(s.id)); } analyticUnit.segments = allSegmentsSet; } @@ -240,72 +253,68 @@ export class AnalyticController { if( this._labelingDataAddedSegments.length === 0 && - this._labelingDataDeletedSegments.length === 0 + this._labelingDataRemovedSegments.length === 0 ) { return []; } await this._analyticService.updateMetric(unit.id, this._currentMetric, this._currentDatasource); - return this._analyticService.updateSegments( - unit.id, this._labelingDataAddedSegments, this._labelingDataDeletedSegments + const newIds = await this._analyticService.updateSegments( + unit.id, this._labelingDataAddedSegments, this._labelingDataRemovedSegments ); + if(unit.labelingMode !== LabelingMode.UNLABELING) { + await this._analyticService.runDetect(unit.id); + } + return newIds; } // TODO: move to renderer - updateFlotEvents(isEditMode, options) { + updateFlotEvents(isEditMode: boolean, options: any) { if(options.grid.markings === undefined) { options.markings = []; } for(var i = 0; i < this.analyticUnits.length; i++) { - var analyticUnit = this.analyticUnits[i]; - var borderColor = addAlphaToRGB(analyticUnit.color, REGION_STROKE_ALPHA); - var fillColor = addAlphaToRGB(analyticUnit.color, REGION_FILL_ALPHA); - var segments = analyticUnit.segments.getSegments(); + const analyticUnit = this.analyticUnits[i]; if(!analyticUnit.visible) { continue; } - if(isEditMode && this.labelingMode) { - if(analyticUnit.selected) { - borderColor = addAlphaToRGB(borderColor, 0.7); - fillColor = addAlphaToRGB(borderColor, 0.7); - } else { - continue; - } - } - - var rangeDist = +options.xaxis.max - +options.xaxis.min; + let defaultBorderColor = addAlphaToRGB(analyticUnit.labeledColor, SEGMENT_STROKE_ALPHA); + let defaultFillColor = addAlphaToRGB(analyticUnit.labeledColor, SEGMENT_FILL_ALPHA); let labeledSegmentBorderColor = tinycolor(LABELED_SEGMENT_BORDER_COLOR).toRgbString(); - labeledSegmentBorderColor = addAlphaToRGB(labeledSegmentBorderColor, REGION_STROKE_ALPHA); - let deletedSegmentFillColor = tinycolor(DELETED_SEGMENT_FILL_COLOR).toRgbString(); - deletedSegmentFillColor = addAlphaToRGB(deletedSegmentFillColor, REGION_STROKE_ALPHA); + labeledSegmentBorderColor = addAlphaToRGB(labeledSegmentBorderColor, SEGMENT_STROKE_ALPHA); + let deletedSegmentFillColor = tinycolor(analyticUnit.deletedColor).toRgbString(); + deletedSegmentFillColor = addAlphaToRGB(deletedSegmentFillColor, SEGMENT_FILL_ALPHA); let deletedSegmentBorderColor = tinycolor(DELETED_SEGMENT_BORDER_COLOR).toRgbString(); - deletedSegmentBorderColor = addAlphaToRGB(deletedSegmentBorderColor, REGION_STROKE_ALPHA); + deletedSegmentBorderColor = addAlphaToRGB(deletedSegmentBorderColor, SEGMENT_STROKE_ALPHA); + + if(isEditMode && this.inLabelingMode && analyticUnit.selected) { + defaultBorderColor = addAlphaToRGB(defaultBorderColor, LABELING_MODE_ALPHA); + defaultFillColor = addAlphaToRGB(defaultFillColor, LABELING_MODE_ALPHA); + labeledSegmentBorderColor = addAlphaToRGB(labeledSegmentBorderColor, LABELING_MODE_ALPHA); + deletedSegmentFillColor = addAlphaToRGB(deletedSegmentFillColor, LABELING_MODE_ALPHA); + deletedSegmentBorderColor = addAlphaToRGB(deletedSegmentBorderColor, LABELING_MODE_ALPHA); + } + + const segments = analyticUnit.segments.getSegments(); + const rangeDist = +options.xaxis.max - +options.xaxis.min; segments.forEach(s => { - let segmentBorderColor; - let segmentFillColor = fillColor; + let segmentBorderColor = defaultBorderColor; + let segmentFillColor = defaultFillColor; - if(this.labelingDeleteMode) { - if(s.deleted) { - segmentBorderColor = deletedSegmentBorderColor; - segmentFillColor = deletedSegmentFillColor; - } + if(s.deleted) { + segmentBorderColor = deletedSegmentBorderColor; + segmentFillColor = deletedSegmentFillColor; } else { - if(s.deleted) { - return; + if(s.labeled) { + segmentBorderColor = labeledSegmentBorderColor; } } - if(s.labeled) { - segmentBorderColor = labeledSegmentBorderColor; - } else { - segmentBorderColor = borderColor; - } - - var expanded = s.expandDist(rangeDist, 0.01); + const expanded = s.expandDist(rangeDist, 0.01); options.grid.markings.push({ xaxis: { from: expanded.from, to: expanded.to }, color: segmentFillColor @@ -324,20 +333,24 @@ export class AnalyticController { } deleteLabelingAnalyticUnitSegmentsInRange(from: number, to: number) { - var allRemovedSegs = this.labelingUnit.removeSegmentsInRange(from, to); + const allRemovedSegs = this.labelingUnit.removeSegmentsInRange(from, to); allRemovedSegs.forEach(s => { if(!this._labelingDataAddedSegments.has(s.id)) { - this._labelingDataDeletedSegments.addSegment(s); + this._labelingDataRemovedSegments.addSegment(s); } }); this._labelingDataAddedSegments.removeInRange(from, to); } - toggleDeleteMode() { - if(!this.labelingMode) { - throw new Error('Cant enter delete mode is labeling mode disabled'); + toggleLabelingMode(labelingMode: LabelingMode) { + 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.deleteMode = !this.labelingUnit.deleteMode; } async removeAnalyticUnit(id: AnalyticUnitId, silent: boolean = false) { diff --git a/src/panel/graph_panel/graph_ctrl.ts b/src/panel/graph_panel/graph_ctrl.ts index 745b45f..719487f 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 } from './models/analytic_unit'; +import { AnalyticUnitId, AnalyticUnit, LabelingMode } from './models/analytic_unit'; import { AnalyticService } from './services/analytic_service'; import { AnalyticController } from './controllers/analytic_controller'; import { PanelInfo } from './models/info'; @@ -35,7 +35,6 @@ class GraphCtrl extends MetricsPanelCtrl { private _datasourceRequest: DatasourceRequest; private _datasources: any; - private _panelPath: any; private _renderError: boolean = false; // annotationsPromise: any; @@ -157,19 +156,28 @@ class GraphCtrl extends MetricsPanelCtrl { this.events.on('init-edit-mode', this.onInitEditMode.bind(this)); } - rebindDKey() { + rebindKeys() { + const dKeyCode = 68; + const uKeyCode = 85; + $(document).off('keydown.hasticDKey'); $(document).on('keydown.hasticDKey', (e) => { - // 68 is 'd' key kode - if(e.keyCode === 68) { + if(e.keyCode === dKeyCode) { this.onDKey(); } }); + + $(document).off('keydown.hasticUKey'); + $(document).on('keydown.hasticUKey', (e) => { + if(e.keyCode === uKeyCode) { + this.onUKey(); + } + }); } editPanel() { super.editPanel(); - this.rebindDKey(); + this.rebindKeys(); } async getBackendURL(): Promise { @@ -264,6 +272,9 @@ class GraphCtrl extends MetricsPanelCtrl { await this.analyticsController.removeAnalyticUnit(analyticUnit.id, true); } if(analyticUnit.status === 'READY') { + if(this.range === undefined) { + this.updateTimeRange(); + } await this.analyticsController.fetchSegments(analyticUnit, +this.range.from, +this.range.to); } this.render(this.seriesList); @@ -300,7 +311,7 @@ class GraphCtrl extends MetricsPanelCtrl { onInitEditMode() { - this.rebindDKey(); // a small hask: bind if we open page in edit mode + this.rebindKeys(); // a small hask: bind if we open page in edit mode const partialPath = this.panelPath + '/partials'; this.addEditorTab('Analytics', `${partialPath}/tab_analytics.html`, 2); @@ -581,11 +592,11 @@ class GraphCtrl extends MetricsPanelCtrl { this.analyticsController.fetchAnalyticUnitName(analyticUnit); } - onColorChange(id: AnalyticUnitId, value: string) { + onColorChange(id: AnalyticUnitId, deleted: boolean, value: string) { if(id === undefined) { throw new Error('id is undefined'); } - this.analyticsController.onAnalyticUnitColorChange(id, value); + this.analyticsController.onAnalyticUnitColorChange(id, value, deleted); this.render(); } @@ -624,10 +635,18 @@ class GraphCtrl extends MetricsPanelCtrl { } onDKey() { - if(!this.analyticsController.labelingMode) { + if(!this.analyticsController.inLabelingMode) { return; } - this.analyticsController.toggleDeleteMode(); + this.analyticsController.toggleLabelingMode(LabelingMode.DELETING); + this.refresh(); + } + + onUKey() { + if(!this.analyticsController.inLabelingMode) { + return; + } + this.analyticsController.toggleLabelingMode(LabelingMode.UNLABELING); this.refresh(); } @@ -640,8 +659,12 @@ class GraphCtrl extends MetricsPanelCtrl { const datasource = await this._getDatasourceByName(this.panel.datasource); const backendUrl = await this.getBackendURL(); + let grafanaVersion = 'unknown'; + if(_.has(window, 'grafanaBootData.settings.buildInfo.version')) { + grafanaVersion = window.grafanaBootData.settings.buildInfo.version; + } this._panelInfo = { - grafanaVersion: this.contextSrv.version, + grafanaVersion, grafanaUrl: window.location.host, datasourceType: datasource.type, hasticServerUrl: backendUrl diff --git a/src/panel/graph_panel/graph_renderer.ts b/src/panel/graph_panel/graph_renderer.ts index cf8d9cd..9537249 100644 --- a/src/panel/graph_panel/graph_renderer.ts +++ b/src/panel/graph_panel/graph_renderer.ts @@ -1,15 +1,17 @@ import { Segment } from './models/segment'; +import { LabelingMode } from './models/analytic_unit'; import { GraphTooltip } from './graph_tooltip'; import { convertValuesToHistogram, getSeriesValues } from './histogram'; import { - AnalyticController, - REGION_FILL_ALPHA, - REGION_STROKE_ALPHA, - REGION_DELETE_COLOR_LIGHT, - REGION_DELETE_COLOR_DARK + AnalyticController } from './controllers/analytic_controller'; +import { + REGION_UNLABEL_COLOR_LIGHT, + REGION_UNLABEL_COLOR_DARK +} from './colors'; + import { GraphCtrl } from './graph_ctrl'; import 'jquery'; @@ -140,20 +142,26 @@ export class GraphRenderer { if(this._isHasticEvent(selectionEvent)) { this.plot.clearSelection(); - var id = this._analyticController.getNewTempSegmentId(); - var segment = new Segment( + const id = this._analyticController.getNewTempSegmentId(); + const segment = new Segment( id, Math.round(selectionEvent.xaxis.from), Math.round(selectionEvent.xaxis.to) ); - if(this._analyticController.labelingDeleteMode) { + if(this._analyticController.labelingMode === LabelingMode.DELETING) { this._analyticController.deleteLabelingAnalyticUnitSegmentsInRange( segment.from, segment.to ); + this._analyticController.addLabelSegment(segment, true); + } + if(this._analyticController.labelingMode === LabelingMode.LABELING) { + this._analyticController.addLabelSegment(segment, false); } - this._analyticController.addLabelSegment( - segment, this._analyticController.labelingDeleteMode - ); + if(this._analyticController.labelingMode === LabelingMode.UNLABELING) { + this._analyticController.deleteLabelingAnalyticUnitSegmentsInRange( + segment.from, segment.to + ); + } this.renderPanel(); return; @@ -336,21 +344,22 @@ export class GraphRenderer { } private _chooseSelectionColor(e) { - var color = COLOR_SELECTION; - var fillAlpha = 0.4; - var strokeAlpha = 0.4; + let color = COLOR_SELECTION; + if(this._isHasticEvent(e)) { - if(this._analyticController.labelingDeleteMode) { + if(this._analyticController.labelingMode === LabelingMode.DELETING) { + color = this._analyticController.labelingUnit.deletedColor; + } + if(this._analyticController.labelingMode === LabelingMode.LABELING) { + color = this._analyticController.labelingUnit.labeledColor; + } + if(this._analyticController.labelingMode === LabelingMode.UNLABELING) { color = this.contextSrv.user.lightTheme ? - REGION_DELETE_COLOR_LIGHT : - REGION_DELETE_COLOR_DARK; - } else { - color = this._analyticController.labelingUnit.color; + REGION_UNLABEL_COLOR_LIGHT : + REGION_UNLABEL_COLOR_DARK; } - fillAlpha = REGION_FILL_ALPHA; - strokeAlpha = REGION_STROKE_ALPHA; } - this.plot.getOptions().selection.color = color + this.plot.getOptions().selection.color = color; } private _buildFlotPairs(data) { @@ -807,7 +816,7 @@ export class GraphRenderer { private _isHasticEvent(obj: any) { return (obj.ctrlKey || obj.metaKey) && this.contextSrv.isEditor && - this._analyticController.labelingMode; + this._analyticController.inLabelingMode; } } diff --git a/src/panel/graph_panel/graph_tooltip.ts b/src/panel/graph_panel/graph_tooltip.ts index ae5028e..97c5f31 100644 --- a/src/panel/graph_panel/graph_tooltip.ts +++ b/src/panel/graph_panel/graph_tooltip.ts @@ -200,12 +200,8 @@ export class GraphTooltip { var from = this.dashboard.formatDate(s.segment.from, 'HH:mm:ss.SSS'); var to = this.dashboard.formatDate(s.segment.to, 'HH:mm:ss.SSS'); - if(s.segment.deleted && !s.analyticUnit.deleteMode) { - return; - } - let icon; - if (s.segment.labeled) { + if(s.segment.labeled) { icon = 'fa-thumb-tack'; } else if (s.segment.deleted) { icon = 'fa-trash'; @@ -215,7 +211,7 @@ export class GraphTooltip { result += `
- + ${s.analyticUnit.name}:
diff --git a/src/panel/graph_panel/models/analytic_unit.ts b/src/panel/graph_panel/models/analytic_unit.ts index 92ac121..85e70c8 100644 --- a/src/panel/graph_panel/models/analytic_unit.ts +++ b/src/panel/graph_panel/models/analytic_unit.ts @@ -2,11 +2,18 @@ import { SegmentsSet } from './segment_set'; import { SegmentArray } from './segment_array'; import { Segment, SegmentId } from './segment'; -import { ANALYTIC_UNIT_COLORS } from '../colors'; +import { ANALYTIC_UNIT_COLORS, DEFAULT_DELETED_SEGMENT_COLOR } from '../colors'; import _ from 'lodash'; +export enum LabelingMode { + LABELING = 'LABELING', + UNLABELING = 'UNLABELING', + DELETING = 'DELETING', + NOT_IN_LABELING_MODE = 'NOT_IN_LABELING_MODE' +}; + export type AnalyticSegmentPair = { analyticUnit: AnalyticUnit, segment: AnalyticSegment }; export type AnalyticSegmentsSearcher = (point: number, rangeDist: number) => AnalyticSegmentPair[]; @@ -23,8 +30,8 @@ export class AnalyticSegment extends Segment { export class AnalyticUnit { + private _labelingMode: LabelingMode = LabelingMode.LABELING; private _selected: boolean = false; - private _deleteMode: boolean = false; private _saving: boolean = false; private _segmentSet = new SegmentArray(); private _status: string; @@ -36,7 +43,8 @@ export class AnalyticUnit { } _.defaults(this._panelObject, { name: 'AnalyticUnitName', - color: ANALYTIC_UNIT_COLORS[0], + labeledColor: ANALYTIC_UNIT_COLORS[0], + deletedColor: DEFAULT_DELETED_SEGMENT_COLOR, detectorType: 'pattern', type: 'GENERAL', alert: false @@ -58,8 +66,11 @@ export class AnalyticUnit { set confidence(value: number) { this._panelObject.confidence = value; } get confidence(): number { return this._panelObject.confidence; } - set color(value: string) { this._panelObject.color = value; } - get color(): string { return this._panelObject.color; } + set labeledColor(value: string) { this._panelObject.labeledColor = value; } + get labeledColor(): string { return this._panelObject.labeledColor; } + + set deletedColor(value: string) { this._panelObject.deletedColor = value; } + get deletedColor(): string { return this._panelObject.deletedColor; } set alert(value: boolean) { this._panelObject.alert = value; } get alert(): boolean { return this._panelObject.alert; } @@ -67,8 +78,8 @@ export class AnalyticUnit { get selected(): boolean { return this._selected; } set selected(value: boolean) { this._selected = value; } - get deleteMode(): boolean { return this._deleteMode; } - set deleteMode(value: boolean) { this._deleteMode = value; } + get labelingMode(): LabelingMode { return this._labelingMode; } + set labelingMode(value: LabelingMode) { this._labelingMode = value; } get saving(): boolean { return this._saving; } set saving(value: boolean) { this._saving = value; } @@ -81,16 +92,13 @@ export class AnalyticUnit { } addLabeledSegment(segment: Segment, deleted: boolean): AnalyticSegment { - var asegment = new AnalyticSegment(true, segment.id, segment.from, segment.to, deleted); + const asegment = new AnalyticSegment(!deleted, segment.id, segment.from, segment.to, deleted); this._segmentSet.addSegment(asegment); return asegment; } removeSegmentsInRange(from: number, to: number): AnalyticSegment[] { let deletedSegments = this._segmentSet.removeInRange(from, to); - deletedSegments.forEach(s => { - s.deleted = true; - }); return deletedSegments; } diff --git a/src/panel/graph_panel/models/segment_array.ts b/src/panel/graph_panel/models/segment_array.ts index a07ffa4..5038eeb 100644 --- a/src/panel/graph_panel/models/segment_array.ts +++ b/src/panel/graph_panel/models/segment_array.ts @@ -47,7 +47,7 @@ export class SegmentArray implements SegmentsSet { findSegments(point: number, rangeDist: number): T[] { return this._segments.filter(s => { - var expanded = s.expandDist(rangeDist, 0.01); + const expanded = s.expandDist(rangeDist, 0.01); return (expanded.from <= point) && (point <= expanded.to); }); } diff --git a/src/panel/graph_panel/partials/help_section.html b/src/panel/graph_panel/partials/help_section.html new file mode 100644 index 0000000..64dc52e --- /dev/null +++ b/src/panel/graph_panel/partials/help_section.html @@ -0,0 +1,24 @@ +
+  For usage instructions: Visit our Wiki page.
+  
+  If you encounter any problems:
+  Look for solution or create a new Issue here.
+  
+  Available labeling patterns examples:
+  1) General: patterns in your data that don't fall under any of provided built-in patterns.
+  
+  2) Peaks: a sharp increase to a certain single value, followed by a return to the original value.
+  
+  
+  
+  3) Troughs: a sharp decrease to a certain single value, followed by a return to the original value.
+  
+  
+  4) Jumps: increase to a certain value without returning to the original state.
+  
+  
+  5) Drops: decrease to a certain value without returning to the original state.
+  
+  
+  6) Custom: any custom model created and imported by you.
+
diff --git a/src/panel/graph_panel/partials/tab_analytics.html b/src/panel/graph_panel/partials/tab_analytics.html index 8a4e3f7..81637a7 100644 --- a/src/panel/graph_panel/partials/tab_analytics.html +++ b/src/panel/graph_panel/partials/tab_analytics.html @@ -11,7 +11,7 @@
-