Alexey Velikiy
3 years ago
10 changed files with 489 additions and 6 deletions
@ -0,0 +1,195 @@ |
|||||||
|
import { SegmentsSet } from '@/types/segment_set'; |
||||||
|
import { SegmentArray } from '@/types/segment_array'; |
||||||
|
import { Segment, SegmentId } from '@/types/segment'; |
||||||
|
import { DetectionSpan } from '../detection'; |
||||||
|
|
||||||
|
import { ANALYTIC_UNIT_COLORS, DEFAULT_DELETED_SEGMENT_COLOR } from '@/types/colors'; |
||||||
|
|
||||||
|
import _ from 'lodash'; |
||||||
|
|
||||||
|
|
||||||
|
// TODO: move types to ./types
|
||||||
|
export enum DetectorType { |
||||||
|
PATTERN = 'pattern', |
||||||
|
THRESHOLD = 'threshold', |
||||||
|
ANOMALY = 'anomaly' |
||||||
|
}; |
||||||
|
|
||||||
|
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[]; |
||||||
|
|
||||||
|
export type AnalyticUnitId = string; |
||||||
|
|
||||||
|
export class AnalyticSegment extends Segment { |
||||||
|
constructor(public labeled: boolean, id: SegmentId, from: number, to: number, public deleted = false) { |
||||||
|
super(id, from, to); |
||||||
|
if(!_.isBoolean(this.labeled)) { |
||||||
|
throw new Error('labeled value is not boolean'); |
||||||
|
} |
||||||
|
if(labeled && deleted) { |
||||||
|
throw new Error('Segment can`t be both labeled and deleted'); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
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, |
||||||
|
collapsed: false |
||||||
|
}; |
||||||
|
|
||||||
|
const LABELING_MODES = []; |
||||||
|
|
||||||
|
export class AnalyticUnit { |
||||||
|
|
||||||
|
private _labelingMode: LabelingMode = LabelingMode.LABELING; |
||||||
|
private _selected: boolean = false; |
||||||
|
private _saving: boolean = false; |
||||||
|
private _segmentSet = new SegmentArray<AnalyticSegment>(); |
||||||
|
private _detectionSpans: DetectionSpan[]; |
||||||
|
private _inspect = false; |
||||||
|
private _changed = false; |
||||||
|
private _status: string; |
||||||
|
private _error: string; |
||||||
|
|
||||||
|
// TODO: serverObject -> fields
|
||||||
|
constructor(protected _serverObject?: any) { |
||||||
|
if(_serverObject === undefined) { |
||||||
|
this._serverObject = _.clone(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, |
||||||
|
collapsed: this.collapsed |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
get id(): AnalyticUnitId { return this._serverObject.id; } |
||||||
|
set id(value: AnalyticUnitId) { this._serverObject.id = value; } |
||||||
|
|
||||||
|
set name(value: string) { this._serverObject.name = value; } |
||||||
|
get name(): string { return this._serverObject.name; } |
||||||
|
|
||||||
|
set detectorType(value: DetectorType) { this._serverObject.detectorType = value; } |
||||||
|
get detectorType(): DetectorType { return this._serverObject.detectorType; } |
||||||
|
|
||||||
|
set type(value: string) { this._serverObject.type = value; } |
||||||
|
get type(): string { return this._serverObject.type; } |
||||||
|
|
||||||
|
set labeledColor(value: string) { this._serverObject.labeledColor = value; } |
||||||
|
get labeledColor(): string { return this._serverObject.labeledColor; } |
||||||
|
|
||||||
|
set deletedColor(value: string) { this._serverObject.deletedColor = value; } |
||||||
|
get deletedColor(): string { return this._serverObject.deletedColor; } |
||||||
|
|
||||||
|
get collapsed(): boolean { return this._serverObject.collapsed; } |
||||||
|
set collapsed(value: boolean) { this._serverObject.collapsed = value; } |
||||||
|
|
||||||
|
set alert(value: boolean) { this._serverObject.alert = value; } |
||||||
|
get alert(): boolean { return this._serverObject.alert; } |
||||||
|
|
||||||
|
get selected(): boolean { return this._selected; } |
||||||
|
set selected(value: boolean) { this._selected = 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; } |
||||||
|
|
||||||
|
get changed(): boolean { return this._changed; } |
||||||
|
set changed(value: boolean) { this._changed = 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 |
||||||
|
} |
||||||
|
set visible(value: boolean) { |
||||||
|
this._serverObject.visible = value; |
||||||
|
} |
||||||
|
|
||||||
|
addSegment(segment: Segment, deleted: boolean): AnalyticSegment { |
||||||
|
const addedSegment = new AnalyticSegment(!deleted, segment.id, segment.from, segment.to, deleted); |
||||||
|
this._segmentSet.addSegment(addedSegment); |
||||||
|
return addedSegment; |
||||||
|
} |
||||||
|
|
||||||
|
removeSegmentsInRange(from: number, to: number): AnalyticSegment[] { |
||||||
|
let deletedSegments = this._segmentSet.removeInRange(from, to); |
||||||
|
return deletedSegments; |
||||||
|
} |
||||||
|
|
||||||
|
get segments(): SegmentsSet<AnalyticSegment> { return this._segmentSet; } |
||||||
|
set segments(value: SegmentsSet<AnalyticSegment>) { |
||||||
|
this._segmentSet.setSegments(value.getSegments()); |
||||||
|
} |
||||||
|
|
||||||
|
get detectionSpans(): DetectionSpan[] { return this._detectionSpans; } |
||||||
|
set detectionSpans(value: DetectionSpan[]) { this._detectionSpans = value; } |
||||||
|
|
||||||
|
get status() { return this._status; } |
||||||
|
set status(value) { |
||||||
|
// TODO: use enum
|
||||||
|
if( |
||||||
|
value !== '404' && |
||||||
|
value !== 'READY' && |
||||||
|
value !== 'LEARNING' && |
||||||
|
value !== 'DETECTION' && |
||||||
|
value !== 'PENDING' && |
||||||
|
value !== 'FAILED' && |
||||||
|
value !== 'SUCCESS' && |
||||||
|
value !== null |
||||||
|
) { |
||||||
|
throw new Error('Unsupported status value: ' + value); |
||||||
|
} |
||||||
|
this._status = value; |
||||||
|
} |
||||||
|
|
||||||
|
get error() { return this._error; } |
||||||
|
set error(value) { this._error = value; } |
||||||
|
|
||||||
|
get isActiveStatus() { |
||||||
|
switch(this.status) { |
||||||
|
case '404': |
||||||
|
case 'READY': |
||||||
|
case 'FAILED': |
||||||
|
return false; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
get serverObject() { return this._serverObject; } |
||||||
|
|
||||||
|
// TODO: make it abstract
|
||||||
|
get labelingModes() { |
||||||
|
return LABELING_MODES; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,105 @@ |
|||||||
|
import tinycolor from 'tinycolor2'; |
||||||
|
import { DetectionStatus } from '@/types/detection'; |
||||||
|
|
||||||
|
export const PALETTE_ROWS = 4; |
||||||
|
export const PALETTE_COLUMNS = 14; |
||||||
|
export const DEFAULT_ANNOTATION_COLOR = 'rgba(0, 211, 255, 1)'; |
||||||
|
export const OK_COLOR = 'rgba(11, 237, 50, 1)'; |
||||||
|
export const NO_DATA_COLOR = 'rgba(150, 150, 150, 1)'; |
||||||
|
export const REGION_FILL_ALPHA = 0.09; |
||||||
|
|
||||||
|
let colors = [ |
||||||
|
'#7EB26D', |
||||||
|
'#EAB839', |
||||||
|
'#6ED0E0', |
||||||
|
'#EF843C', |
||||||
|
'#E24D42', |
||||||
|
'#1F78C1', |
||||||
|
'#BA43A9', |
||||||
|
'#705DA0', |
||||||
|
'#508642', |
||||||
|
'#CCA300', |
||||||
|
'#447EBC', |
||||||
|
'#C15C17', |
||||||
|
'#890F02', |
||||||
|
'#0A437C', |
||||||
|
'#6D1F62', |
||||||
|
'#584477', |
||||||
|
'#B7DBAB', |
||||||
|
'#F4D598', |
||||||
|
'#70DBED', |
||||||
|
'#F9BA8F', |
||||||
|
'#F29191', |
||||||
|
'#82B5D8', |
||||||
|
'#E5A8E2', |
||||||
|
'#AEA2E0', |
||||||
|
'#629E51', |
||||||
|
'#E5AC0E', |
||||||
|
'#64B0C8', |
||||||
|
'#E0752D', |
||||||
|
'#BF1B00', |
||||||
|
'#0A50A1', |
||||||
|
'#962D82', |
||||||
|
'#614D93', |
||||||
|
'#9AC48A', |
||||||
|
'#F2C96D', |
||||||
|
'#65C5DB', |
||||||
|
'#F9934E', |
||||||
|
'#EA6460', |
||||||
|
'#5195CE', |
||||||
|
'#D683CE', |
||||||
|
'#806EB7', |
||||||
|
'#3F6833', |
||||||
|
'#967302', |
||||||
|
'#2F575E', |
||||||
|
'#99440A', |
||||||
|
'#58140C', |
||||||
|
'#052B51', |
||||||
|
'#511749', |
||||||
|
'#3F2B5B', |
||||||
|
'#E0F9D7', |
||||||
|
'#FCEACA', |
||||||
|
'#CFFAFF', |
||||||
|
'#F9E2D2', |
||||||
|
'#FCE2DE', |
||||||
|
'#BADFF4', |
||||||
|
'#F9D9F9', |
||||||
|
'#DEDAF7', |
||||||
|
]; |
||||||
|
|
||||||
|
export const ANALYTIC_UNIT_COLORS = [ |
||||||
|
'#FF99FF', |
||||||
|
'#71b1f9', |
||||||
|
'#aee9fb', |
||||||
|
'#9ce677', |
||||||
|
'#f88990', |
||||||
|
'#f9e26e', |
||||||
|
'#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 const DETECTION_STATUS_COLORS = new Map<DetectionStatus, string>([ |
||||||
|
[DetectionStatus.READY, 'green'], |
||||||
|
[DetectionStatus.RUNNING, 'gold'], |
||||||
|
[DetectionStatus.FAILED, 'red'] |
||||||
|
]); |
||||||
|
|
||||||
|
export function hexToHsl(color) { |
||||||
|
return tinycolor(color).toHsl(); |
||||||
|
} |
||||||
|
|
||||||
|
export function hslToHex(color) { |
||||||
|
return tinycolor(color).toHexString(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
export default colors; |
@ -0,0 +1,20 @@ |
|||||||
|
import { AnalyticUnitId } from '@/types/analytic_units/'; |
||||||
|
|
||||||
|
export enum DetectionStatus { |
||||||
|
READY = 'READY', |
||||||
|
RUNNING = 'RUNNING', |
||||||
|
FAILED = 'FAILED' |
||||||
|
}; |
||||||
|
|
||||||
|
export type DetectionSpan = { |
||||||
|
id: AnalyticUnitId, |
||||||
|
status: DetectionStatus, |
||||||
|
from: number, |
||||||
|
to: number |
||||||
|
}; |
||||||
|
|
||||||
|
export const DETECTION_STATUS_TEXT = new Map<DetectionStatus, string>([ |
||||||
|
[DetectionStatus.READY, '[DetectionStatus]: done'], |
||||||
|
[DetectionStatus.RUNNING, '[DetectionStatus]: running...'], |
||||||
|
[DetectionStatus.FAILED, '[DetectionStatus]: failed'] |
||||||
|
]); |
@ -0,0 +1,36 @@ |
|||||||
|
export type SegmentId = string; |
||||||
|
|
||||||
|
export class Segment { |
||||||
|
constructor(private _id: SegmentId, public from: number, public to: number) { |
||||||
|
if(this._id === undefined) { |
||||||
|
throw new Error('id is undefined'); |
||||||
|
} |
||||||
|
if(isNaN(+from)) { |
||||||
|
throw new Error('from can`t be NaN'); |
||||||
|
} |
||||||
|
if(isNaN(+to)) { |
||||||
|
throw new Error('to can`t be NaN'); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
get id(): SegmentId { return this._id; } |
||||||
|
set id(value) { this._id = value; } |
||||||
|
|
||||||
|
get middle() { return (this.from + this.to) / 2; } |
||||||
|
|
||||||
|
get length() { |
||||||
|
return Math.max(this.from, this.to) - Math.min(this.from, this.to); |
||||||
|
} |
||||||
|
|
||||||
|
expandDist(allDist: number, portion: number): Segment { |
||||||
|
var p = Math.round(this.middle - allDist * portion / 2); |
||||||
|
var q = Math.round(this.middle + allDist * portion / 2); |
||||||
|
p = Math.min(p, this.from); |
||||||
|
q = Math.max(q, this.to); |
||||||
|
return new Segment(this._id, p, q); |
||||||
|
} |
||||||
|
|
||||||
|
equals(segment: Segment) { |
||||||
|
return this._id === segment._id; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,101 @@ |
|||||||
|
import { SegmentsSet } from './segment_set'; |
||||||
|
import { Segment, SegmentId } from './segment'; |
||||||
|
|
||||||
|
|
||||||
|
export class SegmentArray<T extends Segment> implements SegmentsSet<T> { |
||||||
|
private _segments: T[]; |
||||||
|
private _keyToSegment: Map<SegmentId, T> = new Map<SegmentId, T>(); |
||||||
|
|
||||||
|
constructor(private segments?: T[]) { |
||||||
|
this.setSegments(segments); |
||||||
|
} |
||||||
|
|
||||||
|
getSegments(from?: number, to?: number): T[] { |
||||||
|
if(from === undefined) { |
||||||
|
from = -Infinity; |
||||||
|
} |
||||||
|
if(to === undefined) { |
||||||
|
to = Infinity; |
||||||
|
} |
||||||
|
var result = []; |
||||||
|
for(var i = 0; i < this._segments.length; i++) { |
||||||
|
var s = this._segments[i]; |
||||||
|
if(from <= s.from && s.to <= to) { |
||||||
|
result.push(s); |
||||||
|
} |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
setSegments(segments: T[]) { |
||||||
|
this._segments = []; |
||||||
|
this._keyToSegment.clear(); |
||||||
|
if(segments) { |
||||||
|
segments.forEach(s => { |
||||||
|
this.addSegment(s); |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
addSegment(segment: T) { |
||||||
|
if(this.has(segment.id)) { |
||||||
|
throw new Error(`Segment with key ${segment.id} exists in set`); |
||||||
|
} |
||||||
|
this._keyToSegment.set(segment.id, segment); |
||||||
|
this._segments.push(segment); |
||||||
|
} |
||||||
|
|
||||||
|
findSegments(point: number, rangeDist: number): T[] { |
||||||
|
return this._segments.filter(s => { |
||||||
|
const expanded = s.expandDist(rangeDist, 0.01); |
||||||
|
return (expanded.from <= point) && (point <= expanded.to); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
removeInRange(from: number, to: number): T[] { |
||||||
|
var deleted = []; |
||||||
|
var newSegments = []; |
||||||
|
for(var i = 0; i < this._segments.length; i++) { |
||||||
|
var s = this._segments[i]; |
||||||
|
if(from <= s.from && s.to <= to) { |
||||||
|
this._keyToSegment.delete(s.id); |
||||||
|
deleted.push(s); |
||||||
|
} else { |
||||||
|
newSegments.push(s); |
||||||
|
} |
||||||
|
} |
||||||
|
this._segments = newSegments; |
||||||
|
return deleted; |
||||||
|
} |
||||||
|
|
||||||
|
get length() { |
||||||
|
return this._segments.length; |
||||||
|
} |
||||||
|
|
||||||
|
clear() { |
||||||
|
this._segments = []; |
||||||
|
this._keyToSegment.clear(); |
||||||
|
} |
||||||
|
|
||||||
|
has(key: SegmentId): boolean { |
||||||
|
return this._keyToSegment.has(key); |
||||||
|
} |
||||||
|
|
||||||
|
remove(key: SegmentId): boolean { |
||||||
|
if(!this.has(key)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
var index = this._segments.findIndex(s => s.id === key); |
||||||
|
this._segments.splice(index, 1); |
||||||
|
this._keyToSegment.delete(key); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
updateId(fromKey: SegmentId, toKey: SegmentId) { |
||||||
|
var segment = this._keyToSegment.get(fromKey); |
||||||
|
this._keyToSegment.delete(fromKey); |
||||||
|
segment.id = toKey; |
||||||
|
this._keyToSegment.set(toKey, segment); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,14 @@ |
|||||||
|
import { Segment, SegmentId } from '@/types/segment' |
||||||
|
|
||||||
|
export interface SegmentsSet<T extends Segment> { |
||||||
|
getSegments(from?: number, to?: number): T[]; |
||||||
|
setSegments(segments: T[]): void; |
||||||
|
addSegment(segment: T): void; |
||||||
|
findSegments(point: number, rangeDist: number): T[]; |
||||||
|
removeInRange(from: number, to: number): T[]; |
||||||
|
remove(id: SegmentId): boolean; |
||||||
|
has(id: SegmentId): boolean; |
||||||
|
clear(): void; |
||||||
|
updateId(fromId: SegmentId, toId: SegmentId): void; |
||||||
|
length: number; |
||||||
|
} |
@ -1,5 +1,5 @@ |
|||||||
export class User { |
export class User { |
||||||
public username?: String |
public username?: String |
||||||
public email?: String |
public email?: String |
||||||
public password?: String |
public password?: String |
||||||
} |
} |
Loading…
Reference in new issue