diff --git a/server/src/index.ts b/server/src/index.ts index 53886b1..4962383 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -1,17 +1,16 @@ -import * as Koa from 'koa'; -import * as Router from 'koa-router'; -import * as bodyParser from 'koa-bodyparser'; - - -import { router as anomaliesRouter } from './routes/analytic_units'; -import { router as segmentsRouter } from './routes/segments'; -import { router as alertsRouter } from './routes/alerts'; +import { router as anomaliesRouter } from './routes/analytic_units_router'; +import { router as segmentsRouter } from './routes/segments_router'; +import { router as alertsRouter } from './routes/alerts_router'; -import { checkDataFolders } from './services/data'; +import * as Data from './services/data'; import { HASTIC_PORT } from './config'; -checkDataFolders(); +import * as Koa from 'koa'; +import * as Router from 'koa-router'; +import * as bodyParser from 'koa-bodyparser'; + +Data.checkDataFolders(); var app = new Koa(); diff --git a/server/src/models/analytic_unit.ts b/server/src/models/analytic_unit.ts index 14908fa..7b26c0d 100644 --- a/server/src/models/analytic_unit.ts +++ b/server/src/models/analytic_unit.ts @@ -47,7 +47,7 @@ export function createItem(item: AnalyticUnit): AnalyticUnitId { return newId; } -export function removeItem(id: AnalyticUnitId) { +export function remove(id: AnalyticUnitId) { let filename = path.join(ANALYTIC_UNITS_PATH, `${id}.json`); fs.unlinkSync(filename); } @@ -58,7 +58,7 @@ export function save(id: AnalyticUnitId, unit: AnalyticUnit) { } // TODO: make async -export function loadById(id: AnalyticUnitId): AnalyticUnit { +export function findById(id: AnalyticUnitId): AnalyticUnit { let filename = path.join(ANALYTIC_UNITS_PATH, `${id}.json`); if(!fs.existsSync(filename)) { throw new Error(`Can't find Analytic Unit with id ${id}`); @@ -66,12 +66,8 @@ export function loadById(id: AnalyticUnitId): AnalyticUnit { return getJsonDataSync(filename); } -export function getAnomalyTypeInfo(name) { - return getJsonDataSync(path.join(ANALYTIC_UNITS_PATH, `${name}.json`)); -} - -export function setAnomalyStatus(predictorId: AnalyticUnitId, status: string, error?: string) { - let info = loadById(predictorId); +export function setStatus(predictorId: AnalyticUnitId, status: string, error?: string) { + let info = findById(predictorId); info.status = status; if(error !== undefined) { info.error = error; @@ -82,7 +78,7 @@ export function setAnomalyStatus(predictorId: AnalyticUnitId, status: string, er } export function setPredictionTime(id: AnalyticUnitId, time: number) { - let info = loadById(id); + let info = findById(id); info.lastPredictionTime = time; save(id, info); } diff --git a/server/src/routes/alerts.ts b/server/src/routes/alerts_router.ts similarity index 76% rename from server/src/routes/alerts.ts rename to server/src/routes/alerts_router.ts index 3f229dc..8ff4a1f 100644 --- a/server/src/routes/alerts.ts +++ b/server/src/routes/alerts_router.ts @@ -1,25 +1,23 @@ import * as AnalyticUnit from '../models/analytic_unit'; -import { getAlertsAnomalies, saveAlertsAnomalies } from '../services/alerts'; +import { getAlertsAnomalies, saveAlertsAnomalies } from '../services/alerts_service'; import * as Router from 'koa-router'; function getAlert(ctx: Router.IRouterContext) { - let predictorId: AnalyticUnit.AnalyticUnitId = ctx.request.query.predictor_id.toLowerCase(); + let id: AnalyticUnit.AnalyticUnitId = ctx.request.query.id; let alertsAnomalies = getAlertsAnomalies(); - let pos = alertsAnomalies.indexOf(predictorId); + let pos = alertsAnomalies.indexOf(id); - let enable: boolean = (pos !== -1); - ctx.response.body = { enable }; + let enabled: boolean = (pos !== -1); + ctx.response.body = { enabled }; } function setAlertEnabled(ctx: Router.IRouterContext) { let id: AnalyticUnit.AnalyticUnitId = ctx.request.body.id; let enabled: boolean = ctx.request.body.enabled; - let unit = AnalyticUnit.loadById(id) - let alertsAnomalies = getAlertsAnomalies(); let pos: number = alertsAnomalies.indexOf(id); if(enabled && pos == -1) { @@ -30,7 +28,6 @@ function setAlertEnabled(ctx: Router.IRouterContext) { saveAlertsAnomalies(alertsAnomalies); } ctx.response.body = { status: 'OK' }; - } export const router = new Router(); diff --git a/server/src/routes/analytic_units.ts b/server/src/routes/analytic_units_router.ts similarity index 94% rename from server/src/routes/analytic_units.ts rename to server/src/routes/analytic_units_router.ts index f58aad5..0c393c6 100644 --- a/server/src/routes/analytic_units.ts +++ b/server/src/routes/analytic_units_router.ts @@ -2,7 +2,7 @@ import * as Router from 'koa-router'; import * as AnalyticUnit from '../models/analytic_unit'; -import { runLearning } from '../services/analytics' +import { runLearning } from '../services/analytics_service' import { saveTargets } from '../services/metrics'; async function sendStatus(ctx: Router.IRouterContext) { @@ -13,7 +13,7 @@ async function sendStatus(ctx: Router.IRouterContext) { if(id === undefined) { throw new Error('Id is undefined'); } - let unit = AnalyticUnit.loadById(id); + let unit = AnalyticUnit.findById(id); if(unit.status === undefined) { throw new Error('No status for ' + name); @@ -36,7 +36,7 @@ async function findItem(ctx: Router.IRouterContext) { throw new Error('No id param in query'); } - let unit: AnalyticUnit.AnalyticUnit = AnalyticUnit.loadById(id); + let unit: AnalyticUnit.AnalyticUnit = AnalyticUnit.findById(id); ctx.response.body = { name: unit.name, @@ -118,7 +118,7 @@ function deleteItem(ctx: Router.IRouterContext) { let id = ctx.request.query.id; if(id !== undefined) { - AnalyticUnit.removeItem(id); + AnalyticUnit.remove(id); } ctx.response.body = { diff --git a/server/src/routes/segments.ts b/server/src/routes/segments_router.ts similarity index 95% rename from server/src/routes/segments.ts rename to server/src/routes/segments_router.ts index 3044b44..cda6c7d 100644 --- a/server/src/routes/segments.ts +++ b/server/src/routes/segments_router.ts @@ -7,7 +7,7 @@ import { } from '../services/segments'; import { AnalyticUnitId } from '../models/analytic_unit'; -import { runLearning } from '../services/analytics'; +import { runLearning } from '../services/analytics_service'; async function sendSegments(ctx: Router.IRouterContext) { diff --git a/server/src/services/alerts.ts b/server/src/services/alerts_service.ts similarity index 61% rename from server/src/services/alerts.ts rename to server/src/services/alerts_service.ts index edc3e83..d5c3390 100644 --- a/server/src/services/alerts.ts +++ b/server/src/services/alerts_service.ts @@ -1,7 +1,7 @@ import { getJsonDataSync, writeJsonDataSync } from './json'; import { AnalyticUnitId } from '../models/analytic_unit'; -import { runPredict } from './analytics'; -import { sendNotification } from './notification'; +import { runPredict } from './analytics_service'; +import { sendNotification } from './notification_service'; import { getLabeledSegments } from './segments'; import { ANALYTIC_UNITS_PATH } from '../config'; @@ -10,40 +10,41 @@ import * as path from 'path'; import * as fs from 'fs'; - +const ALERT_TIMEOUT = 60000; // ms const ALERTS_DB_PATH = path.join(ANALYTIC_UNITS_PATH, `alerts_anomalies.json`); -function getAlertsAnomalies(): AnalyticUnitId[] { + +export function getAlertsAnomalies(): AnalyticUnitId[] { if(!fs.existsSync(ALERTS_DB_PATH)) { saveAlertsAnomalies([]); } return getJsonDataSync(ALERTS_DB_PATH); } -function saveAlertsAnomalies(anomalies: AnalyticUnitId[]) { - return writeJsonDataSync(ALERTS_DB_PATH, anomalies); +export function saveAlertsAnomalies(units: AnalyticUnitId[]) { + return writeJsonDataSync(ALERTS_DB_PATH, units); } -function processAlerts(predictorId) { - let segments = getLabeledSegments(predictorId); +function processAlerts(id: AnalyticUnitId) { + let segments = getLabeledSegments(id); const currentTime = new Date().getTime(); - const activeAlert = activeAlerts.has(predictorId); + const activeAlert = activeAlerts.has(id); let newActiveAlert = false; if(segments.length > 0) { let lastSegment = segments[segments.length - 1]; - if(lastSegment.finish >= currentTime - alertTimeout) { + if(lastSegment.finish >= currentTime - ALERT_TIMEOUT) { newActiveAlert = true; } } if(!activeAlert && newActiveAlert) { - activeAlerts.add(predictorId); - sendNotification(predictorId, true); + activeAlerts.add(id); + sendNotification(id, true); } else if(activeAlert && !newActiveAlert) { - activeAlerts.delete(predictorId); - sendNotification(predictorId, false); + activeAlerts.delete(id); + sendNotification(id, false); } } @@ -60,9 +61,6 @@ async function alertsTick() { setTimeout(alertsTick, 5000); } -const alertTimeout = 60000; // ms + const activeAlerts = new Set(); setTimeout(alertsTick, 5000); - - -export { getAlertsAnomalies, saveAlertsAnomalies } diff --git a/server/src/services/analytics.ts b/server/src/services/analytics_service.ts similarity index 59% rename from server/src/services/analytics.ts rename to server/src/services/analytics_service.ts index 98572a3..2da99ef 100644 --- a/server/src/services/analytics.ts +++ b/server/src/services/analytics_service.ts @@ -1,9 +1,9 @@ import { AnalyticUnit, - AnalyticUnitId, getAnomalyTypeInfo, - loadById, + AnalyticUnitId, + findById, setPredictionTime, - setAnomalyStatus + setStatus } from '../models/analytic_unit' import { getTarget } from './metrics'; import { getLabeledSegments, insertSegments, removeSegments } from './segments' @@ -28,10 +28,10 @@ function onResponse(response: any) { } async function runTask(task): Promise { - let anomaly: AnalyticUnit = loadById(task.predictor_id); + let anomaly: AnalyticUnit = findById(task.analyticUnitId); task.metric = { datasource: anomaly.metric.datasource, - targets: anomaly.metric.targets.map(t => getTarget(t)) + targets: anomaly.metric.targets.map(getTarget) }; task.__task_id = nextTaskId++; @@ -42,14 +42,14 @@ async function runTask(task): Promise { }) } -export async function runLearning(predictorId:AnalyticUnitId) { - let segments = getLabeledSegments(predictorId); - setAnomalyStatus(predictorId, 'learning'); - let anomaly:AnalyticUnit = loadById(predictorId); +export async function runLearning(id: AnalyticUnitId) { + let segments = getLabeledSegments(id); + setStatus(id, 'learning'); + let anomaly: AnalyticUnit = findById(id); let pattern = anomaly.type; let task = { + analyticUnitId: id, type: 'learn', - predictor_id: predictorId, pattern, segments: segments }; @@ -57,22 +57,22 @@ export async function runLearning(predictorId:AnalyticUnitId) { let result = await runTask(task); if (result.status === 'success') { - setAnomalyStatus(predictorId, 'ready'); - insertSegments(predictorId, result.segments, false); - setPredictionTime(predictorId, result.last_prediction_time); + setStatus(id, 'ready'); + insertSegments(id, result.segments, false); + setPredictionTime(id, result.lastPredictionTime); } else { - setAnomalyStatus(predictorId, 'failed', result.error); + setStatus(id, 'failed', result.error); } } -export async function runPredict(predictorId:AnalyticUnitId) { - let anomaly:AnalyticUnit = loadById(predictorId); - let pattern = anomaly.type; +export async function runPredict(id: AnalyticUnitId) { + let unit: AnalyticUnit = findById(id); + let pattern = unit.type; let task = { type: 'predict', - predictor_id: predictorId, + predictor_id: id, pattern, - last_prediction_time: anomaly.lastPredictionTime + lastPredictionTime: unit.lastPredictionTime }; let result = await runTask(task); @@ -80,18 +80,18 @@ export async function runPredict(predictorId:AnalyticUnitId) { return []; } // Merging segments - let segments = getLabeledSegments(predictorId); + let segments = getLabeledSegments(id); if(segments.length > 0 && result.segments.length > 0) { let lastOldSegment = segments[segments.length - 1]; let firstNewSegment = result.segments[0]; if(firstNewSegment.start <= lastOldSegment.finish) { result.segments[0].start = lastOldSegment.start; - removeSegments(predictorId, [lastOldSegment.id]); + removeSegments(id, [lastOldSegment.id]); } } - insertSegments(predictorId, result.segments, false); - setPredictionTime(predictorId, result.last_prediction_time); + insertSegments(id, result.segments, false); + setPredictionTime(id, result.last_prediction_time); return result.segments; } diff --git a/server/src/services/metrics.ts b/server/src/services/metrics.ts index 52ce444..5bbe72b 100644 --- a/server/src/services/metrics.ts +++ b/server/src/services/metrics.ts @@ -1,8 +1,11 @@ -import * as path from 'path'; import { getJsonDataSync, writeJsonDataSync } from './json'; import { METRICS_PATH } from '../config'; + import * as crypto from 'crypto'; +import * as path from 'path'; + + function saveTargets(targets) { let metrics = []; for (let target of targets) { diff --git a/server/src/services/notification.ts b/server/src/services/notification_service.ts similarity index 87% rename from server/src/services/notification.ts rename to server/src/services/notification_service.ts index 6f57876..0deaf8b 100644 --- a/server/src/services/notification.ts +++ b/server/src/services/notification_service.ts @@ -1,8 +1,10 @@ +import { findById } from '../models/analytic_unit'; + import axios from 'axios'; -import { loadById } from '../models/analytic_unit'; + export async function sendNotification(predictorId, active) { - let anomalyName = loadById(predictorId).name; + let anomalyName = findById(predictorId).name; console.log('Notification ' + anomalyName); let notification = { diff --git a/server/src/services/segments.ts b/server/src/services/segments.ts index 1605bcd..dccf5a5 100644 --- a/server/src/services/segments.ts +++ b/server/src/services/segments.ts @@ -1,5 +1,5 @@ import { getJsonDataSync, writeJsonDataSync } from './json'; -import { AnalyticUnitId, loadById, save } from '../models/analytic_unit'; +import { AnalyticUnitId, findById, save } from '../models/analytic_unit'; import { SEGMENTS_PATH } from '../config'; import * as _ from 'lodash'; @@ -48,9 +48,9 @@ export function saveSegments(id: AnalyticUnitId, segments) { } } -export function insertSegments(id: AnalyticUnitId, addedSegments, labeled:boolean) { +export function insertSegments(id: AnalyticUnitId, addedSegments, labeled: boolean) { // Set status - let info = loadById(id); + let info = findById(id); let segments = getLabeledSegments(id); let nextId = info.nextId;