Hastic standalone
https://hastic.io
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
247 lines
6.1 KiB
247 lines
6.1 KiB
<template> |
|
<div> |
|
<div id="chart"></div> |
|
</div> |
|
</template> |
|
|
|
<script lang="ts"> |
|
|
|
import { AnomalyHSR, TimeRange } from "@/types"; |
|
import { PatternPod } from "./pods/pattern_pod"; |
|
import { ThresholdPod } from './pods/threshold_pod'; |
|
import { AnomalyPod } from './pods/anomaly_pod'; |
|
import { getMetrics } from '../services/metrics.service'; |
|
import { getSegments, postSegment, deleteSegment } from '../services/segments.service'; |
|
import { LineTimeSerie } from "@chartwerk/line-pod"; |
|
|
|
import { SegmentArray } from '@/types/segment_array'; |
|
import { Segment, SegmentId } from '@/types/segment'; |
|
|
|
import _ from "lodash"; |
|
import { AnalyticUnitType } from '@/types/analytic_units'; |
|
|
|
import { defineComponent, watch } from 'vue'; |
|
import { getHSRAnomaly } from "@/services/analytics.service"; |
|
|
|
// TODO: move to store |
|
async function resolveDataPatterns(range: TimeRange): Promise<{ |
|
timeserie: LineTimeSerie[], |
|
segments: Segment[] |
|
}> { |
|
|
|
const endTime = Math.floor(range.to); |
|
const startTime = Math.floor(range.from); |
|
|
|
const step = Math.max(Math.round((endTime - startTime) / 5000), 1); |
|
|
|
try { |
|
// TODO: request in parallel |
|
let [target, values] = await getMetrics(startTime, endTime, step); |
|
let segments = await getSegments(startTime, endTime); |
|
return { |
|
timeserie: [{ target: target, datapoints: values, color: 'green' }], |
|
segments: segments |
|
} |
|
} catch (e) { |
|
this.$notify({ |
|
title: "Error during extracting data", |
|
text: e, |
|
type: 'error' |
|
}); |
|
console.error(e); |
|
} |
|
} |
|
|
|
// TODO: move to store |
|
// TODO: remove code repetition |
|
async function resolveDataThreshold(range: TimeRange): Promise<{ |
|
timeserie: LineTimeSerie[], |
|
segments: Segment[] |
|
}> { |
|
|
|
const endTime = Math.floor(range.to); |
|
const startTime = Math.floor(range.from); |
|
|
|
const step = Math.max(Math.round((endTime - startTime) / 5000), 1); |
|
|
|
try { |
|
// TODO: request in parallel |
|
let [target, values] = await getMetrics(startTime, endTime, step); |
|
let segments = await getSegments(startTime, endTime, false); |
|
return { |
|
timeserie: [{ target: target, datapoints: values, color: 'green' }], |
|
segments: segments |
|
} |
|
} catch (e) { |
|
this.$notify({ |
|
title: "Error during extracting data", |
|
text: e, |
|
type: 'error' |
|
}); |
|
console.error(e); |
|
} |
|
} |
|
|
|
// TODO: move to store |
|
// TODO: remove code repetition |
|
async function resolveDataAnomaly(range: TimeRange): Promise<{ |
|
timeserie: LineTimeSerie[], |
|
hsr: AnomalyHSR, |
|
segments: Segment[] |
|
}> { |
|
|
|
const endTime = Math.floor(range.to); |
|
const startTime = Math.floor(range.from); |
|
|
|
const step = Math.max(Math.round((endTime - startTime) / 5000), 1); |
|
|
|
try { |
|
// TODO: request in parallel |
|
let [target, values] = await getMetrics(startTime, endTime, step); |
|
let segments = await getSegments(startTime, endTime, false); |
|
let hsr = await getHSRAnomaly(startTime, endTime); |
|
return { |
|
timeserie: [ |
|
{ target: target, datapoints: values, color: 'green' }, |
|
], |
|
hsr, |
|
segments: segments, |
|
} |
|
} catch (e) { |
|
this.$notify({ |
|
title: "Error during extracting data", |
|
text: e, |
|
type: 'error' |
|
}); |
|
console.error(e); |
|
} |
|
} |
|
|
|
|
|
// TODO: move to store |
|
async function addSegment(segment: Segment): Promise<SegmentId> { |
|
try { |
|
const id = await postSegment(segment); |
|
return id; |
|
} catch (e) { |
|
this.$notify({ |
|
title: "Error during saving segment", |
|
text: e, |
|
type: 'error' |
|
}); |
|
console.error(e); |
|
} |
|
} |
|
|
|
// TODO: move to store |
|
async function _deleteSegment(from: number, to: number): Promise<number> { |
|
try { |
|
return await deleteSegment(from, to); |
|
} catch (e) { |
|
this.$notify({ |
|
title: "Error during saving segment", |
|
text: e, |
|
type: 'error' |
|
}); |
|
console.error(e); |
|
} |
|
} |
|
|
|
|
|
// TODO: convert to class component |
|
export default defineComponent({ |
|
name: 'Graph', |
|
props: {}, |
|
mounted() { |
|
this.rebuildGraph(); |
|
}, |
|
// TODO: it's a hack: listen real events about analytics update and use store |
|
watch: { |
|
analyticUnitConfig(newConfig, prevConfig) { |
|
if(prevConfig == null) { |
|
return; |
|
} |
|
// TODO: remove this hack |
|
if(!_.isEqual(_.keys(newConfig),_.keys(prevConfig))) { |
|
return; |
|
} |
|
|
|
this.rerender(); |
|
}, |
|
analyticUnitType(newType, prevType) { |
|
this.rebuildGraph(); |
|
} |
|
}, |
|
methods: { |
|
rerender() { |
|
this.pod.fetchData(); |
|
}, |
|
async deleteAllSegments() { |
|
await _deleteSegment.bind(this)(0, Date.now()); |
|
this.rerender(); |
|
}, |
|
rebuildGraph() { |
|
let child = document.getElementById('chart').children[0]; |
|
if(child != undefined) { |
|
document.getElementById('chart').removeChild(child); |
|
} |
|
var sa = new SegmentArray(); |
|
|
|
const aut = this.analyticUnitType; |
|
if(aut == null) { |
|
return; |
|
} |
|
|
|
if(aut === AnalyticUnitType.PATTERN) { |
|
this.pod = new PatternPod( |
|
document.getElementById('chart'), |
|
resolveDataPatterns.bind(this), |
|
addSegment.bind(this), |
|
_deleteSegment.bind(this), |
|
sa |
|
); |
|
} |
|
if(aut === AnalyticUnitType.THRESHOLD) { |
|
this.pod = new ThresholdPod( |
|
document.getElementById('chart'), |
|
resolveDataThreshold.bind(this), |
|
sa |
|
); |
|
} |
|
if(aut === AnalyticUnitType.ANOMALY) { |
|
this.pod = new AnomalyPod( |
|
document.getElementById('chart'), |
|
resolveDataAnomaly.bind(this), |
|
this.setSeasonality.bind(this), |
|
sa |
|
); |
|
} |
|
this.pod.render(); |
|
}, |
|
setSeasonality(from: number, to: number) { |
|
let cfg = _.clone(this.analyticUnitConfig); |
|
// TODO: get 10 (step) from API config |
|
cfg.seasonality = Math.ceil(Math.abs(from - to) / 10) * 10; |
|
console.log("cfg.seasonality: " + cfg.seasonality); |
|
|
|
this.$store.dispatch('patchConfig', { Anomaly: cfg }); |
|
} |
|
}, |
|
computed: { |
|
analyticUnitConfig() { |
|
return this.$store.state.analyticUnitConfig; |
|
}, |
|
analyticUnitType() { |
|
return this.$store.state.analyticUnitType; |
|
} |
|
} |
|
}); |
|
</script> |
|
|
|
<style scoped lang="scss"> |
|
#chart { |
|
margin: auto; |
|
width: 80%; |
|
height: 350px; |
|
} |
|
</style>
|
|
|