Browse Source

Unlabeling mode

master
rozetko 5 years ago
parent
commit
a9a82d374b
  1. 9
      src/panel/graph_panel/colors.ts
  2. 55
      src/panel/graph_panel/controllers/analytic_controller.ts
  3. 33
      src/panel/graph_panel/graph_ctrl.ts
  4. 44
      src/panel/graph_panel/graph_renderer.ts
  5. 13
      src/panel/graph_panel/models/analytic_unit.ts
  6. 15
      src/panel/graph_panel/partials/tab_analytics.html
  7. 4
      src/panel/graph_panel/services/analytic_service.ts

9
src/panel/graph_panel/colors.ts

@ -76,12 +76,15 @@ export const ANALYTIC_UNIT_COLORS = [
'#f8c171', '#f8c171',
]; ];
export const REGION_DELETE_COLOR_LIGHT = '#d1d1d1'; export const REGION_UNLABEL_COLOR_LIGHT = '#d1d1d1';
export const REGION_DELETE_COLOR_DARK = 'white'; export const REGION_UNLABEL_COLOR_DARK = 'white';
export const LABELED_SEGMENT_BORDER_COLOR = 'black'; export const LABELED_SEGMENT_BORDER_COLOR = 'black';
export const DELETED_SEGMENT_FILL_COLOR = '#00f0ff';
export const DELETED_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) { export function hexToHsl(color) {
return tinycolor(color).toHsl(); return tinycolor(color).toHsl();
} }

55
src/panel/graph_panel/controllers/analytic_controller.ts

@ -4,7 +4,8 @@ import { AnalyticService } from '../services/analytic_service'
import { import {
AnalyticUnitId, AnalyticUnit, AnalyticUnitId, AnalyticUnit,
AnalyticUnitsSet, AnalyticSegment, AnalyticSegmentsSearcher, AnalyticSegmentPair AnalyticUnitsSet, AnalyticSegment, AnalyticSegmentsSearcher, AnalyticSegmentPair,
LabelingMode
} from '../models/analytic_unit'; } from '../models/analytic_unit';
import { MetricExpanded } from '../models/metric'; import { MetricExpanded } from '../models/metric';
import { DatasourceRequest } from '../models/datasource'; import { DatasourceRequest } from '../models/datasource';
@ -14,11 +15,13 @@ import { SegmentArray } from '../models/segment_array';
import { ServerInfo } from '../models/info'; import { ServerInfo } from '../models/info';
import { Threshold, Condition } from '../models/threshold'; import { Threshold, Condition } from '../models/threshold';
import { import {
ANALYTIC_UNIT_COLORS, ANALYTIC_UNIT_COLORS,
LABELED_SEGMENT_BORDER_COLOR, LABELED_SEGMENT_BORDER_COLOR,
DELETED_SEGMENT_FILL_COLOR, DELETED_SEGMENT_BORDER_COLOR,
DELETED_SEGMENT_BORDER_COLOR SEGMENT_FILL_ALPHA,
SEGMENT_STROKE_ALPHA,
LABELING_MODE_ALPHA
} from '../colors'; } from '../colors';
import { Emitter } from 'grafana/app/core/utils/emitter'; import { Emitter } from 'grafana/app/core/utils/emitter';
@ -26,10 +29,6 @@ import { Emitter } from 'grafana/app/core/utils/emitter';
import _ from 'lodash'; import _ from 'lodash';
import * as tinycolor from 'tinycolor2'; import * as tinycolor from 'tinycolor2';
export const REGION_FILL_ALPHA = 0.5;
export const REGION_STROKE_ALPHA = 0.8;
export const LABELING_MODE_ALPHA = 0.7;
export class AnalyticController { export class AnalyticController {
private _analyticUnitsSet: AnalyticUnitsSet; private _analyticUnitsSet: AnalyticUnitsSet;
@ -174,15 +173,15 @@ export class AnalyticController {
this._tempIdCounted = -1; this._tempIdCounted = -1;
} }
get labelingMode(): boolean { get inLabelingMode(): boolean {
return this._selectedAnalyticUnitId !== null; return this._selectedAnalyticUnitId !== null;
} }
get labelingDeleteMode(): boolean { get labelingMode(): LabelingMode {
if(!this.labelingMode) { if(!this.inLabelingMode) {
return false; return LabelingMode.NOT_IN_LABELING_MODE;
} }
return this.labelingUnit.deleteMode; return this.labelingUnit.labelingMode;
} }
addLabelSegment(segment: Segment, deleted = false) { addLabelSegment(segment: Segment, deleted = false) {
@ -251,9 +250,13 @@ export class AnalyticController {
await this._analyticService.updateMetric(unit.id, this._currentMetric, this._currentDatasource); await this._analyticService.updateMetric(unit.id, this._currentMetric, this._currentDatasource);
return this._analyticService.updateSegments( const newIds = await this._analyticService.updateSegments(
unit.id, this._labelingDataAddedSegments, this._labelingDataRemovedSegments unit.id, this._labelingDataAddedSegments, this._labelingDataRemovedSegments
); );
if(unit.labelingMode !== LabelingMode.UNLABELING) {
await this._analyticService.runDetect(unit.id);
}
return newIds;
} }
// TODO: move to renderer // TODO: move to renderer
@ -268,16 +271,16 @@ export class AnalyticController {
continue; continue;
} }
let defaultBorderColor = addAlphaToRGB(analyticUnit.labeledColor, REGION_STROKE_ALPHA); let defaultBorderColor = addAlphaToRGB(analyticUnit.labeledColor, SEGMENT_STROKE_ALPHA);
let defaultFillColor = addAlphaToRGB(analyticUnit.labeledColor, REGION_FILL_ALPHA); let defaultFillColor = addAlphaToRGB(analyticUnit.labeledColor, SEGMENT_FILL_ALPHA);
let labeledSegmentBorderColor = tinycolor(LABELED_SEGMENT_BORDER_COLOR).toRgbString(); let labeledSegmentBorderColor = tinycolor(LABELED_SEGMENT_BORDER_COLOR).toRgbString();
labeledSegmentBorderColor = addAlphaToRGB(labeledSegmentBorderColor, REGION_STROKE_ALPHA); labeledSegmentBorderColor = addAlphaToRGB(labeledSegmentBorderColor, SEGMENT_STROKE_ALPHA);
let deletedSegmentFillColor = tinycolor(analyticUnit.deletedColor).toRgbString(); let deletedSegmentFillColor = tinycolor(analyticUnit.deletedColor).toRgbString();
deletedSegmentFillColor = addAlphaToRGB(deletedSegmentFillColor, REGION_FILL_ALPHA); deletedSegmentFillColor = addAlphaToRGB(deletedSegmentFillColor, SEGMENT_FILL_ALPHA);
let deletedSegmentBorderColor = tinycolor(DELETED_SEGMENT_BORDER_COLOR).toRgbString(); let deletedSegmentBorderColor = tinycolor(DELETED_SEGMENT_BORDER_COLOR).toRgbString();
deletedSegmentBorderColor = addAlphaToRGB(deletedSegmentBorderColor, REGION_STROKE_ALPHA); deletedSegmentBorderColor = addAlphaToRGB(deletedSegmentBorderColor, SEGMENT_STROKE_ALPHA);
if(isEditMode && this.labelingMode && analyticUnit.selected) { if(isEditMode && this.inLabelingMode && analyticUnit.selected) {
defaultBorderColor = addAlphaToRGB(defaultBorderColor, LABELING_MODE_ALPHA); defaultBorderColor = addAlphaToRGB(defaultBorderColor, LABELING_MODE_ALPHA);
defaultFillColor = addAlphaToRGB(defaultFillColor, LABELING_MODE_ALPHA); defaultFillColor = addAlphaToRGB(defaultFillColor, LABELING_MODE_ALPHA);
labeledSegmentBorderColor = addAlphaToRGB(labeledSegmentBorderColor, LABELING_MODE_ALPHA); labeledSegmentBorderColor = addAlphaToRGB(labeledSegmentBorderColor, LABELING_MODE_ALPHA);
@ -329,11 +332,15 @@ export class AnalyticController {
this._labelingDataAddedSegments.removeInRange(from, to); this._labelingDataAddedSegments.removeInRange(from, to);
} }
toggleDeleteMode() { toggleLabelingMode(mode: LabelingMode) {
if(!this.labelingMode) { if(!this.inLabelingMode) {
throw new Error('Cant enter delete mode is labeling mode disabled'); throw new Error(`Can't enter ${mode} mode when labeling mode is disabled`);
}
if(this.labelingUnit.labelingMode === mode) {
this.labelingUnit.labelingMode = LabelingMode.LABELING;
} else {
this.labelingUnit.labelingMode = mode;
} }
this.labelingUnit.deleteMode = !this.labelingUnit.deleteMode;
} }
async removeAnalyticUnit(id: AnalyticUnitId, silent: boolean = false) { async removeAnalyticUnit(id: AnalyticUnitId, silent: boolean = false) {

33
src/panel/graph_panel/graph_ctrl.ts

@ -7,7 +7,7 @@ import { GraphLegend } from './graph_legend';
import { DataProcessor } from './data_processor'; import { DataProcessor } from './data_processor';
import { MetricExpanded } from './models/metric'; import { MetricExpanded } from './models/metric';
import { DatasourceRequest } from './models/datasource'; 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 { AnalyticService } from './services/analytic_service';
import { AnalyticController } from './controllers/analytic_controller'; import { AnalyticController } from './controllers/analytic_controller';
import { PanelInfo } from './models/info'; import { PanelInfo } from './models/info';
@ -156,19 +156,28 @@ class GraphCtrl extends MetricsPanelCtrl {
this.events.on('init-edit-mode', this.onInitEditMode.bind(this)); this.events.on('init-edit-mode', this.onInitEditMode.bind(this));
} }
rebindDKey() { rebindKeys() {
const dKeyCode = 68;
const uKeyCode = 85;
$(document).off('keydown.hasticDKey'); $(document).off('keydown.hasticDKey');
$(document).on('keydown.hasticDKey', (e) => { $(document).on('keydown.hasticDKey', (e) => {
// 68 is 'd' key kode if(e.keyCode === dKeyCode) {
if(e.keyCode === 68) {
this.onDKey(); this.onDKey();
} }
}); });
$(document).off('keydown.hasticUKey');
$(document).on('keydown.hasticUKey', (e) => {
if(e.keyCode === uKeyCode) {
this.onUKey();
}
});
} }
editPanel() { editPanel() {
super.editPanel(); super.editPanel();
this.rebindDKey(); this.rebindKeys();
} }
async getBackendURL(): Promise<string> { async getBackendURL(): Promise<string> {
@ -299,7 +308,7 @@ class GraphCtrl extends MetricsPanelCtrl {
onInitEditMode() { 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'; const partialPath = this.panelPath + '/partials';
this.addEditorTab('Analytics', `${partialPath}/tab_analytics.html`, 2); this.addEditorTab('Analytics', `${partialPath}/tab_analytics.html`, 2);
@ -623,10 +632,18 @@ class GraphCtrl extends MetricsPanelCtrl {
} }
onDKey() { onDKey() {
if(!this.analyticsController.labelingMode) { if(!this.analyticsController.inLabelingMode) {
return;
}
this.analyticsController.toggleLabelingMode(LabelingMode.DELETING);
this.refresh();
}
onUKey() {
if(!this.analyticsController.inLabelingMode) {
return; return;
} }
this.analyticsController.toggleDeleteMode(); this.analyticsController.toggleLabelingMode(LabelingMode.UNLABELING);
this.refresh(); this.refresh();
} }

44
src/panel/graph_panel/graph_renderer.ts

@ -1,16 +1,15 @@
import { Segment } from './models/segment'; import { Segment } from './models/segment';
import { LabelingMode } from './models/analytic_unit';
import { GraphTooltip } from './graph_tooltip'; import { GraphTooltip } from './graph_tooltip';
import { convertValuesToHistogram, getSeriesValues } from './histogram'; import { convertValuesToHistogram, getSeriesValues } from './histogram';
import { import {
AnalyticController, AnalyticController
REGION_FILL_ALPHA,
REGION_STROKE_ALPHA
} from './controllers/analytic_controller'; } from './controllers/analytic_controller';
import { import {
REGION_DELETE_COLOR_LIGHT, REGION_UNLABEL_COLOR_LIGHT,
REGION_DELETE_COLOR_DARK REGION_UNLABEL_COLOR_DARK
} from './colors'; } from './colors';
import { GraphCtrl } from './graph_ctrl'; import { GraphCtrl } from './graph_ctrl';
@ -149,14 +148,20 @@ export class GraphRenderer {
Math.round(selectionEvent.xaxis.from), Math.round(selectionEvent.xaxis.from),
Math.round(selectionEvent.xaxis.to) Math.round(selectionEvent.xaxis.to)
); );
if(this._analyticController.labelingDeleteMode) { if(this._analyticController.labelingMode === LabelingMode.DELETING) {
this._analyticController.deleteLabelingAnalyticUnitSegmentsInRange( this._analyticController.deleteLabelingAnalyticUnitSegmentsInRange(
segment.from, segment.to segment.from, segment.to
); );
this._analyticController.addLabelSegment(segment, true); this._analyticController.addLabelSegment(segment, true);
} else { }
if(this._analyticController.labelingMode === LabelingMode.LABELING) {
this._analyticController.addLabelSegment(segment, false); this._analyticController.addLabelSegment(segment, false);
} }
if(this._analyticController.labelingMode === LabelingMode.UNLABELING) {
this._analyticController.deleteLabelingAnalyticUnitSegmentsInRange(
segment.from, segment.to
);
}
this.renderPanel(); this.renderPanel();
return; return;
@ -339,21 +344,22 @@ export class GraphRenderer {
} }
private _chooseSelectionColor(e) { private _chooseSelectionColor(e) {
var color = COLOR_SELECTION; let color = COLOR_SELECTION;
var fillAlpha = 0.4;
var strokeAlpha = 0.4;
if(this._isHasticEvent(e)) { if(this._isHasticEvent(e)) {
if(this._analyticController.labelingDeleteMode) { if(this._analyticController.labelingMode === LabelingMode.DELETING) {
color = this.contextSrv.user.lightTheme ? color = this._analyticController.labelingUnit.deletedColor;
REGION_DELETE_COLOR_LIGHT : }
REGION_DELETE_COLOR_DARK; if(this._analyticController.labelingMode === LabelingMode.LABELING) {
} else {
color = this._analyticController.labelingUnit.labeledColor; color = this._analyticController.labelingUnit.labeledColor;
} }
fillAlpha = REGION_FILL_ALPHA; if(this._analyticController.labelingMode === LabelingMode.UNLABELING) {
strokeAlpha = REGION_STROKE_ALPHA; color = this.contextSrv.user.lightTheme ?
REGION_UNLABEL_COLOR_LIGHT :
REGION_UNLABEL_COLOR_DARK;
}
} }
this.plot.getOptions().selection.color = color this.plot.getOptions().selection.color = color;
} }
private _buildFlotPairs(data) { private _buildFlotPairs(data) {
@ -810,7 +816,7 @@ export class GraphRenderer {
private _isHasticEvent(obj: any) { private _isHasticEvent(obj: any) {
return (obj.ctrlKey || obj.metaKey) && return (obj.ctrlKey || obj.metaKey) &&
this.contextSrv.isEditor && this.contextSrv.isEditor &&
this._analyticController.labelingMode; this._analyticController.inLabelingMode;
} }
} }

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

@ -7,6 +7,13 @@ import { ANALYTIC_UNIT_COLORS } from '../colors';
import _ from 'lodash'; 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 AnalyticSegmentPair = { analyticUnit: AnalyticUnit, segment: AnalyticSegment };
export type AnalyticSegmentsSearcher = (point: number, rangeDist: number) => AnalyticSegmentPair[]; export type AnalyticSegmentsSearcher = (point: number, rangeDist: number) => AnalyticSegmentPair[];
@ -23,8 +30,8 @@ export class AnalyticSegment extends Segment {
export class AnalyticUnit { export class AnalyticUnit {
private _labelingMode: LabelingMode = LabelingMode.LABELING;
private _selected: boolean = false; private _selected: boolean = false;
private _deleteMode: boolean = false;
private _saving: boolean = false; private _saving: boolean = false;
private _segmentSet = new SegmentArray<AnalyticSegment>(); private _segmentSet = new SegmentArray<AnalyticSegment>();
private _status: string; private _status: string;
@ -71,8 +78,8 @@ export class AnalyticUnit {
get selected(): boolean { return this._selected; } get selected(): boolean { return this._selected; }
set selected(value: boolean) { this._selected = value; } set selected(value: boolean) { this._selected = value; }
get deleteMode(): boolean { return this._deleteMode; } get labelingMode(): LabelingMode { return this._labelingMode; }
set deleteMode(value: boolean) { this._deleteMode = value; } set labelingMode(value: LabelingMode) { this._labelingMode = value; }
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; }

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

@ -11,7 +11,7 @@
<div class="editor-row"> <div class="editor-row">
<div class="gf-form" ng-repeat="analyticUnit in ctrl.analyticsController.analyticUnits"> <div class="gf-form" ng-repeat="analyticUnit in ctrl.analyticsController.analyticUnits">
<label class="gf-form-label width-6"> <label class="gf-form-label width-5">
<i class="fa fa-info" bs-tooltip="'Analytic unit id: ' + analyticUnit.id"></i> <i class="fa fa-info" bs-tooltip="'Analytic unit id: ' + analyticUnit.id"></i>
&nbsp; Name &nbsp; Name
</label> </label>
@ -21,7 +21,7 @@
ng-change="ctrl.onAnalyticUnitNameChange(analyticUnit)" ng-change="ctrl.onAnalyticUnitNameChange(analyticUnit)"
> >
<label class="gf-form-label width-8"> Type </label> <label class="gf-form-label width-4"> Type </label>
<div class="gf-form-select-wrapper"> <div class="gf-form-select-wrapper">
<select class="gf-form-input width-12" <select class="gf-form-input width-12"
ng-model="analyticUnit.type" ng-model="analyticUnit.type"
@ -66,8 +66,9 @@
> >
<i class="fa fa-bar-chart" ng-if="!analyticUnit.saving"></i> <i class="fa fa-bar-chart" ng-if="!analyticUnit.saving"></i>
<i class="fa fa-spinner fa-spin" ng-if="analyticUnit.saving"></i> <i class="fa fa-spinner fa-spin" ng-if="analyticUnit.saving"></i>
<b ng-if="analyticUnit.selected && !analyticUnit.deleteMode && !analyticUnit.saving"> labeling </b> <b ng-if="analyticUnit.selected && analyticUnit.labelingMode === 'LABELING' && !analyticUnit.saving"> labeling </b>
<b ng-if="analyticUnit.selected && analyticUnit.deleteMode && !analyticUnit.saving"> deleting </b> <b ng-if="analyticUnit.selected && analyticUnit.labelingMode === 'DELETING' && !analyticUnit.saving"> deleting </b>
<b ng-if="analyticUnit.selected && analyticUnit.labelingMode === 'UNLABELING' && !analyticUnit.saving"> unlabeling </b>
<b ng-if="analyticUnit.saving" ng-disabled="true"> saving... </b> <b ng-if="analyticUnit.saving" ng-disabled="true"> saving... </b>
</a> </a>
</label> </label>
@ -87,9 +88,9 @@
" "
/> />
<!-- TODO set .saving flag to thresholds, when learning is in progress --> <!-- TODO set .saving flag to thresholds, when learning is in progress -->
<button <button
class="btn btn-inverse" class="btn btn-inverse"
ng-if="analyticUnit.detectorType === 'threshold'" ng-if="analyticUnit.detectorType === 'threshold'"
ng-click="ctrl.analyticsController.sendThresholdParamsToServer(analyticUnit.id)" ng-click="ctrl.analyticsController.sendThresholdParamsToServer(analyticUnit.id)"
ng-disabled="analyticUnit.status === 'PENDING' || analyticUnit.status === 'LEARNING'" ng-disabled="analyticUnit.status === 'PENDING' || analyticUnit.status === 'LEARNING'"
> >

4
src/panel/graph_panel/services/analytic_service.ts

@ -160,6 +160,10 @@ export class AnalyticService {
return this.patch('/analyticUnits', updateObj); return this.patch('/analyticUnits', updateObj);
} }
async runDetect(id: AnalyticUnitId) {
return this.post('/analyticUnits/detect', { id });
}
private async _analyticRequest(method: string, url: string, data?: any) { private async _analyticRequest(method: string, url: string, data?: any) {
try { try {
method = method.toUpperCase(); method = method.toUpperCase();

Loading…
Cancel
Save