Browse Source

Merge branch 'analytic-types' into 'main'

Analytic types

See merge request hastic/hastic!1
pull/25/head
Alexey Velikiy 3 years ago
parent
commit
14ea8ab4f0
  1. 74
      client/src/components/ScatterPlot.vue
  2. 6
      client/src/services/analytics.service.ts
  3. 37
      client/src/store/index.ts
  4. 14
      client/src/types/analytic_units/index.ts
  5. 21
      client/src/views/Home.vue
  6. 1
      client/src/views/Model.vue
  7. 21
      server/src/api/analytics.rs
  8. 9
      server/src/services/analytic_service/analytic_client.rs
  9. 13
      server/src/services/analytic_service/analytic_service.rs
  10. 6
      server/src/services/analytic_service/analytic_unit/mod.rs
  11. 4
      server/src/services/analytic_service/analytic_unit/pattern_detector.rs
  12. 12
      server/src/services/analytic_service/analytic_unit/types.rs
  13. 2
      server/src/services/analytic_service/mod.rs
  14. 4
      server/src/services/analytic_service/types.rs

74
client/src/components/ScatterPlot.vue

@ -7,17 +7,85 @@
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import ChartwerkScatterPod from "@chartwerk/scatter-pod"; import { ChartwerkScatterPod } from "@chartwerk/scatter-pod";
import _ from "lodash"; import _ from "lodash";
export default defineComponent({ export default defineComponent({
name: 'ScatterPlot', name: 'ScatterPlot',
props: {}, props: {},
mounted() { mounted() {
// var pod = new ChartwerkScatterPod(
// document.getElementById('chart'),
// [
// {
// target: 'test1',
// datapoints: [
// [100, -50, 0],
// [200, 150, 0],
// [100, 160, 1],
// [150, 170, 1],
// [150, 180, 0],
// [150, 250, 1]
// ] as [number, number][],
// color: 'red',
// lineType: 'dashed',
// pointType: 'circle'
// },
// {
// target: 'test2',
// datapoints: [
// [200, 50, 1],
// [175, 60, 0],
// [150, 70, 1]
// ],
// color: 'purple',
// pointType: 'rectangle',
// pointSize: 5,
// yOrientation: 'right',
// }
// ],
// {
// axis: {
// x: {
// format: 'numeric',
// range: [-100, 300]
// },
// y: {
// invert: true,
// range: [-100, 250]
// },
// y1: {
// isActive: true,
// range: [0, 250]
// }
// },
// zoomEvents: {
// mouse: {
// pan: { isActive: false, orientation: 'both', keyEvent: 'main' },
// zoom: { isActive: true, keyEvent: 'shift' },
// },
// scroll: {
// pan: { isActive: false },
// zoom: { isActive: true, keyEvent: 'main' }
// }
// },
// crosshair: {
// orientation: 'both',
// color: 'gray'
// },
// labelFormat: {
// yAxis: 'y',
// xAxis: 'x'
// },
// eventsCallbacks: {
// zoomOut: () => { pod.render() }
// },
// margin: { top: 30, right: 30, bottom: 40, left: 30 },
// circleView: true,
// }
// );
}, },
methods: { methods: {

6
client/src/services/analytics.service.ts

@ -16,6 +16,12 @@ export async function getStatus(): Promise<string> {
return data.status; return data.status;
} }
export async function getConfig(): Promise<string> {
const uri = ANALYTICS_API_URL + `config`;
const res = await axios.get(uri);
return res['data'] as any;
}
export function getStatusGenerator(): AsyncIterableIterator<string> { export function getStatusGenerator(): AsyncIterableIterator<string> {
return getGenerator<string>(100, getStatus); return getGenerator<string>(100, getStatus);
} }

37
client/src/store/index.ts

@ -1,30 +1,43 @@
import { auth } from "./auth.module"; import { auth } from "./auth.module";
import { createStore } from 'vuex' import { createStore } from 'vuex'
import { getStatusGenerator } from "@/services/analytics.service"; import { getConfig, getStatusGenerator } from "@/services/analytics.service";
import { DetectorConfig, DetectorType } from '@/types/analytic_units'
// import { notify } from "@kyvg/vue3-notification"; // import { notify } from "@kyvg/vue3-notification";
const SET_ANALYTICS_STATUS = 'SET_ANALYTICS_STATUS'; const SET_ANALYTICS_STATUS = 'SET_ANALYTICS_STATUS';
const SET_DETECTOR_CONFIG = 'SET_DETECTOR_CONFIG';
const _SET_STATUS_GENERATOR = '_SET_STATUS_GENERATOR'; const _SET_STATUS_GENERATOR = '_SET_STATUS_GENERATOR';
type State = { type State = {
analyticStatus: string, analyticStatus: string,
detectorType?: DetectorType,
// TODO: maybe rename it
analyticUnitConfig?: DetectorConfig,
_statusGenerator: AsyncIterableIterator<string> _statusGenerator: AsyncIterableIterator<string>
} }
const store = createStore<State>({ const store = createStore<State>({
state: { state: {
analyticStatus: 'loading...', analyticStatus: 'loading...',
detectorType: null,
analyticUnitConfig: null,
_statusGenerator: null _statusGenerator: null
}, },
mutations: { mutations: {
[SET_ANALYTICS_STATUS](state, status: string) { [SET_ANALYTICS_STATUS](state, status: string) {
state.analyticStatus = status; state.analyticStatus = status;
}, },
[SET_DETECTOR_CONFIG](state, { detectorType, detectorConfig }) {
console.log(detectorType);
console.log(detectorConfig);
state.detectorType = detectorType;
state.analyticUnitConfig = detectorConfig;
},
[_SET_STATUS_GENERATOR](state, generator: AsyncIterableIterator<string>) { [_SET_STATUS_GENERATOR](state, generator: AsyncIterableIterator<string>) {
this._statusGenerator = generator; state._statusGenerator = generator;
} }
}, },
actions: { actions: {
@ -36,11 +49,25 @@ const store = createStore<State>({
if(state._statusGenerator !== null) { if(state._statusGenerator !== null) {
return; return;
} }
const g = getStatusGenerator(); const g = getStatusGenerator();
for await (const data of g) { commit(_SET_STATUS_GENERATOR, g);
for await (const data of state._statusGenerator) {
const st = data.toLocaleLowerCase();
if(state.analyticStatus.toLocaleLowerCase() != 'ready' && st == 'ready') {
this.dispatch('fetchConfig');
// TODO: update segments from here
}
// this.status = data.toLowerCase(); // this.status = data.toLowerCase();
commit(SET_ANALYTICS_STATUS, data); commit(SET_ANALYTICS_STATUS, data);
} }
},
async fetchConfig({commit}) {
const c = await getConfig();
// TODO: move this logic to service getConfig()
const detectorType = c['PatternDetector'] !== undefined ? DetectorType.PATTERN : DetectorType.ANOMALY;
const detectorConfig = c['PatternDetector'] as DetectorConfig;
commit(SET_DETECTOR_CONFIG, { detectorType, detectorConfig });
} }
}, },
modules: { modules: {

14
client/src/types/analytic_units/index.ts

@ -13,14 +13,18 @@ export enum DetectorType {
PATTERN = 'pattern', PATTERN = 'pattern',
THRESHOLD = 'threshold', THRESHOLD = 'threshold',
ANOMALY = 'anomaly' ANOMALY = 'anomaly'
}; }
export type DetectorConfig = {
correlation_score: number, model_score: number
}
export enum LabelingMode { export enum LabelingMode {
LABELING = 'LABELING', LABELING = 'LABELING',
UNLABELING = 'UNLABELING', UNLABELING = 'UNLABELING',
DELETING = 'DELETING', DELETING = 'DELETING',
NOT_IN_LABELING_MODE = 'NOT_IN_LABELING_MODE' 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[];
@ -56,8 +60,8 @@ const LABELING_MODES = [];
export class AnalyticUnit { export class AnalyticUnit {
private _labelingMode: LabelingMode = LabelingMode.LABELING; private _labelingMode: LabelingMode = LabelingMode.LABELING;
private _selected: boolean = false; private _selected = false;
private _saving: boolean = false; private _saving = false;
private _segmentSet = new SegmentArray<AnalyticSegment>(); private _segmentSet = new SegmentArray<AnalyticSegment>();
private _detectionSpans: DetectionSpan[]; private _detectionSpans: DetectionSpan[];
private _inspect = false; private _inspect = false;
@ -143,7 +147,7 @@ export class AnalyticUnit {
} }
removeSegmentsInRange(from: number, to: number): AnalyticSegment[] { removeSegmentsInRange(from: number, to: number): AnalyticSegment[] {
let deletedSegments = this._segmentSet.removeInRange(from, to); const deletedSegments = this._segmentSet.removeInRange(from, to);
return deletedSegments; return deletedSegments;
} }

21
client/src/views/Home.vue

@ -3,15 +3,15 @@
<img alt="Vue logo" src="../assets/logo.png"> <img alt="Vue logo" src="../assets/logo.png">
<graph ref="graph" /> <graph ref="graph" />
<analytic-status />
<div id="controls"> <div id="controls">
<div> <div v-if="analyticType == analyticTypes[0]">
Hold <pre>S</pre> to label patterns <br/> Hold <pre>S</pre> to label patterns <br/>
Hold <pre>A</pre> to label anti patterns <br/> Hold <pre>A</pre> to label anti patterns <br/>
Holde key <pre>D</pre> to delete patterns Holde key <pre>D</pre> to delete patterns
<br/>
<button @click="clearAllLabeling"> clear all labeling </button>
</div> </div>
<analytic-status />
<button @click="clearAllLabeling"> clear all labeling </button>
</div> </div>
</div> </div>
@ -21,11 +21,12 @@
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import Graph from '@/components/Graph.vue'; import Graph from '@/components/Graph.vue';
import AnalyticStatus from '@/components/AnlyticsStatus.vue'; import AnalyticStatus from '@/components/AnlyticsStatus.vue';
import { DetectorType } from '@/types/analytic_units';
export default defineComponent({ export default defineComponent({
name: 'Home', name: 'Home',
components: { components: {
Graph, Graph,
AnalyticStatus AnalyticStatus
}, },
@ -33,6 +34,16 @@ export default defineComponent({
clearAllLabeling() { clearAllLabeling() {
this.$refs.graph.deleteAllSegments(); this.$refs.graph.deleteAllSegments();
} }
},
data: function () {
return {
analyticTypes: [DetectorType.PATTERN, DetectorType.ANOMALY],
}
},
computed: {
analyticType() {
return this.$store.state.detectorType;
}
} }
}); });
</script> </script>

1
client/src/views/Model.vue

@ -2,7 +2,6 @@
<div class="home"> <div class="home">
<img alt="Vue logo" src="../assets/logo.png"> <img alt="Vue logo" src="../assets/logo.png">
<scatter-plot /> <scatter-plot />
</div> </div>
</template> </template>

21
server/src/api/analytics.rs

@ -9,6 +9,7 @@ pub mod filters {
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone { ) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
list(client.clone()) list(client.clone())
.or(status(client.clone())) .or(status(client.clone()))
.or(get_config(client.clone()))
.or(list_train(client.clone())) .or(list_train(client.clone()))
// .or(create(db.clone())) // .or(create(db.clone()))
// // .or(update(db.clone())) // // .or(update(db.clone()))
@ -36,6 +37,16 @@ pub mod filters {
.and_then(handlers::status) .and_then(handlers::status)
} }
/// GET /analytics/config
pub fn get_config(
client: Client,
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
warp::path!("analytics" / "config")
.and(warp::get())
.and(with_client(client))
.and_then(handlers::config)
}
/// GET /analytics/model /// GET /analytics/model
pub fn list_train( pub fn list_train(
client: Client, client: Client,
@ -82,6 +93,16 @@ mod handlers {
} }
} }
pub async fn config(client: Client) -> Result<impl warp::Reply, warp::Rejection> {
match client.get_config().await {
Ok(cf) => Ok(API::json(&cf)),
Err(e) => {
println!("{:?}", e);
Err(warp::reject::custom(BadQuery))
}
}
}
pub async fn list_train(client: Client) -> Result<impl warp::Reply, warp::Rejection> { pub async fn list_train(client: Client) -> Result<impl warp::Reply, warp::Rejection> {
match client.get_train().await { match client.get_train().await {
Ok(lt) => Ok(API::json(&lt)), Ok(lt) => Ok(API::json(&lt)),

9
server/src/services/analytic_service/analytic_client.rs

@ -3,6 +3,7 @@ use tokio::sync::oneshot;
use crate::services::segments_service::Segment; use crate::services::segments_service::Segment;
use super::analytic_unit::types::AnalyticUnitConfig;
use super::types::DetectionTask; use super::types::DetectionTask;
use super::types::LearningStatus; use super::types::LearningStatus;
use super::types::LearningTrain; use super::types::LearningTrain;
@ -34,6 +35,14 @@ impl AnalyticClient {
Ok(r) Ok(r)
} }
pub async fn get_config(&self) -> anyhow::Result<AnalyticUnitConfig> {
let (tx, rx) = oneshot::channel();
let req = AnalyticServiceMessage::Request(RequestType::GetConfig(tx));
self.tx.send(req).await?;
let r = rx.await?;
Ok(r)
}
pub async fn get_train(&self) -> anyhow::Result<LearningTrain> { pub async fn get_train(&self) -> anyhow::Result<LearningTrain> {
let (tx, rx) = oneshot::channel(); let (tx, rx) = oneshot::channel();
let req = AnalyticServiceMessage::Request(RequestType::GetLearningTrain(tx)); let req = AnalyticServiceMessage::Request(RequestType::GetLearningTrain(tx));

13
server/src/services/analytic_service/analytic_service.rs

@ -1,7 +1,8 @@
use super::analytic_unit::types::{AnalyticUnitConfig, PatternDetectorConfig};
use super::types::{self, DetectionRunnerConfig, LearningTrain}; use super::types::{self, DetectionRunnerConfig, LearningTrain};
use super::{ use super::{
analytic_client::AnalyticClient, analytic_client::AnalyticClient,
pattern_detector::{self, LearningResults, PatternDetector}, analytic_unit::pattern_detector::{self, LearningResults, PatternDetector},
types::{AnalyticServiceMessage, DetectionTask, LearningStatus, RequestType, ResponseType}, types::{AnalyticServiceMessage, DetectionTask, LearningStatus, RequestType, ResponseType},
}; };
@ -51,6 +52,7 @@ pub struct AnalyticService {
metric_service: MetricService, metric_service: MetricService,
segments_service: SegmentsService, segments_service: SegmentsService,
learning_results: Option<LearningResults>, learning_results: Option<LearningResults>,
analytic_unit_config: AnalyticUnitConfig,
learning_status: LearningStatus, learning_status: LearningStatus,
tx: mpsc::Sender<AnalyticServiceMessage>, tx: mpsc::Sender<AnalyticServiceMessage>,
rx: mpsc::Receiver<AnalyticServiceMessage>, rx: mpsc::Receiver<AnalyticServiceMessage>,
@ -78,8 +80,14 @@ impl AnalyticService {
AnalyticService { AnalyticService {
metric_service, metric_service,
segments_service, segments_service,
// TODO: get it from persistance // TODO: get it from persistance
learning_results: None, learning_results: None,
analytic_unit_config: AnalyticUnitConfig::PatternDetector(PatternDetectorConfig {
correlation_score: 0.95,
model_score: 0.95
}),
learning_status: LearningStatus::Initialization, learning_status: LearningStatus::Initialization,
tx, tx,
rx, rx,
@ -180,6 +188,9 @@ impl AnalyticService {
.unwrap(); .unwrap();
} }
} }
RequestType::GetConfig(tx) => {
tx.send(self.analytic_unit_config.clone()).unwrap();
}
}; };
} }

6
server/src/services/analytic_service/analytic_unit/mod.rs

@ -0,0 +1,6 @@
pub mod pattern_detector;
pub mod types;
trait AnalyticUnit<C> {
}

4
server/src/services/analytic_service/pattern_detector.rs → server/src/services/analytic_service/analytic_unit/pattern_detector.rs

@ -11,7 +11,9 @@ use linfa_svm::{error::Result, Svm};
use ndarray::{Array, ArrayView, Axis}; use ndarray::{Array, ArrayView, Axis};
use super::types::LearningTrain; use crate::services::analytic_service::types::LearningTrain;
#[derive(Clone)] #[derive(Clone)]
pub struct LearningResults { pub struct LearningResults {

12
server/src/services/analytic_service/analytic_unit/types.rs

@ -0,0 +1,12 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct PatternDetectorConfig {
pub correlation_score: f32,
pub model_score: f32
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub enum AnalyticUnitConfig {
PatternDetector(PatternDetectorConfig)
}

2
server/src/services/analytic_service/mod.rs

@ -1,5 +1,5 @@
mod analytic_service; mod analytic_service;
mod pattern_detector; mod analytic_unit;
pub mod types; pub mod types;
pub mod analytic_client; pub mod analytic_client;

4
server/src/services/analytic_service/types.rs

@ -1,6 +1,6 @@
use crate::services::segments_service::Segment; use crate::services::segments_service::Segment;
use super::pattern_detector::{self, LearningResults, PatternDetector}; use super::analytic_unit::{pattern_detector::{self, LearningResults}, types::AnalyticUnitConfig};
use anyhow::Result; use anyhow::Result;
use serde::Serialize; use serde::Serialize;
@ -15,6 +15,7 @@ pub enum LearningStatus {
Ready, Ready,
} }
// TODO: move to analytic_unit config of pattern detector
#[derive(Clone, Serialize, Debug)] #[derive(Clone, Serialize, Debug)]
pub struct LearningTrain { pub struct LearningTrain {
pub features: Vec<pattern_detector::Features>, pub features: Vec<pattern_detector::Features>,
@ -58,6 +59,7 @@ pub enum RequestType {
RunLearning, RunLearning,
RunDetection(DetectionTask), RunDetection(DetectionTask),
GetStatus(oneshot::Sender<LearningStatus>), GetStatus(oneshot::Sender<LearningStatus>),
GetConfig(oneshot::Sender<AnalyticUnitConfig>),
GetLearningTrain(oneshot::Sender<LearningTrain>), GetLearningTrain(oneshot::Sender<LearningTrain>),
} }

Loading…
Cancel
Save