diff --git a/analytics/analytics/analytic_types/segment.py b/analytics/analytics/analytic_types/segment.py index 516b888..4c3760b 100644 --- a/analytics/analytics/analytic_types/segment.py +++ b/analytics/analytics/analytic_types/segment.py @@ -5,14 +5,16 @@ class Segment: Used for segment manipulation instead of { 'from': ..., 'to': ... } dict ''' - def __init__(self, from_timestamp: int, to_timestamp: int): + def __init__(self, from_timestamp: int, to_timestamp: int, message: str = None): if to_timestamp < from_timestamp: raise ValueError(f'Can`t create segment with to < from: {to_timestamp} < {from_timestamp}') self.from_timestamp = from_timestamp self.to_timestamp = to_timestamp + self.message = message def to_json(self): return { 'from': self.from_timestamp, - 'to': self.to_timestamp + 'to': self.to_timestamp, + 'message': self.message } diff --git a/analytics/analytics/detectors/anomaly_detector.py b/analytics/analytics/detectors/anomaly_detector.py index 5bf53a3..72d595c 100644 --- a/analytics/analytics/detectors/anomaly_detector.py +++ b/analytics/analytics/detectors/anomaly_detector.py @@ -116,6 +116,7 @@ class AnomalyDetector(ProcessingDetector): segments = [Segment( utils.convert_pd_timestamp_to_ms(dataframe['timestamp'][segment[0]]), utils.convert_pd_timestamp_to_ms(dataframe['timestamp'][segment[1]]), + f'{data[segment[0]]} out of bound' ) for segment in segments] last_dataframe_time = dataframe.iloc[-1]['timestamp'] diff --git a/analytics/analytics/detectors/threshold_detector.py b/analytics/analytics/detectors/threshold_detector.py index 6ed0fdb..1e3352f 100644 --- a/analytics/analytics/detectors/threshold_detector.py +++ b/analytics/analytics/detectors/threshold_detector.py @@ -1,5 +1,6 @@ import logging as log +import operator import pandas as pd import numpy as np from typing import Optional, List @@ -49,24 +50,23 @@ class ThresholdDetector(Detector): # TODO: merge segments if pd.isnull(current_value): if condition == 'NO_DATA': + segment.message = 'NO_DATA detected' segments.append(segment) continue - if condition == '>': - if current_value > value: - segments.append(segment) - elif condition == '>=': - if current_value >= value: - segments.append(segment) - elif condition == '=': - if current_value == value: - segments.append(segment) - elif condition == '<=': - if current_value <= value: - segments.append(segment) - elif condition == '<': - if current_value < value: - segments.append(segment) + comparators = { + '>': operator.gt, + '<': operator.lt, + '=': operator.eq, + '>=': operator.ge, + '<=': operator.le + } + + assert condition in comparators.keys(), f'condition {condition} not allowed' + + if comparators[condition](current_value, value): + segment.message = f"{current_value} {condition} threshold's value {value}" + segments.append(segment) last_entry = dataframe.iloc[-1] last_detection_time = utils.convert_pd_timestamp_to_ms(last_entry['timestamp']) diff --git a/analytics/analytics/utils/common.py b/analytics/analytics/utils/common.py index 7c6a4f0..191a980 100644 --- a/analytics/analytics/utils/common.py +++ b/analytics/analytics/utils/common.py @@ -137,6 +137,7 @@ def merge_intersecting_segments(segments: List[Segment], time_step: int) -> List previous_segment = segments[0] for i in range(1, len(segments)): if segments[i].from_timestamp <= previous_segment.to_timestamp + time_step: + segments[i].message = segments[-1].message segments[i].from_timestamp = min(previous_segment.from_timestamp, segments[i].from_timestamp) segments[i].to_timestamp = max(previous_segment.to_timestamp, segments[i].to_timestamp) segments[i - 1] = None diff --git a/analytics/tests/test_detectors.py b/analytics/tests/test_detectors.py index 61f13f3..6af58e5 100644 --- a/analytics/tests/test_detectors.py +++ b/analytics/tests/test_detectors.py @@ -2,6 +2,7 @@ import unittest import pandas as pd from detectors import pattern_detector, threshold_detector, anomaly_detector +from analytic_types.detector_typing import DetectionResult class TestPatternDetector(unittest.TestCase): @@ -44,6 +45,8 @@ class TestAnomalyDetector(unittest.TestCase): 'timeStep': 1 } detector = anomaly_detector.AnomalyDetector('test_id') - detect_result = detector.detect(dataframe, cache) + detect_result: DetectionResult = detector.detect(dataframe, cache) + + detected_segments = list(map(lambda s: {'from': s.from_timestamp, 'to': s.to_timestamp}, detect_result.segments)) result = [{ 'from': 1523889000005.0, 'to': 1523889000005.0 }] - self.assertEqual(result, detect_result.to_json()['segments']) + self.assertEqual(result, detected_segments) diff --git a/server/src/controllers/analytics_controller.ts b/server/src/controllers/analytics_controller.ts index a54be11..d754280 100644 --- a/server/src/controllers/analytics_controller.ts +++ b/server/src/controllers/analytics_controller.ts @@ -451,9 +451,9 @@ async function processDetectionResult(analyticUnitId: AnalyticUnit.AnalyticUnitI } console.log(`got detection result for ${analyticUnitId} with ${detectionResult.segments.length} segments`); - const sortedSegments: {from, to}[] = _.sortBy(detectionResult.segments, 'from'); + const sortedSegments: {from, to, message?}[] = _.sortBy(detectionResult.segments, 'from'); const segments = sortedSegments.map( - segment => new Segment.Segment(analyticUnitId, segment.from, segment.to, false, false) + segment => new Segment.Segment(analyticUnitId, segment.from, segment.to, false, false, undefined, segment.message) ); return { diff --git a/server/src/models/segment_model.ts b/server/src/models/segment_model.ts index 7d58329..a6cbc63 100644 --- a/server/src/models/segment_model.ts +++ b/server/src/models/segment_model.ts @@ -17,7 +17,7 @@ export class Segment { public labeled: boolean = false, public deleted: boolean = false, public id?: SegmentId, - public params?: any + public message?: string ) { if(analyticUnitId === undefined) { throw new Error('AnalyticUnitId is undefined'); @@ -43,7 +43,8 @@ export class Segment { from: this.from, to: this.to, labeled: this.labeled, - deleted: this.deleted + deleted: this.deleted, + message: this.message }; } @@ -54,7 +55,8 @@ export class Segment { return new Segment( obj.analyticUnitId, +obj.from, +obj.to, - obj.labeled, obj.deleted, obj._id + obj.labeled, obj.deleted, + obj._id, obj.message ); } diff --git a/server/src/services/alert_service.ts b/server/src/services/alert_service.ts index 6aa6f54..3159d78 100644 --- a/server/src/services/alert_service.ts +++ b/server/src/services/alert_service.ts @@ -33,7 +33,8 @@ export class Alert { analyticUnitId: this.analyticUnit.id, grafanaUrl, from: segment.from, - to: segment.to + to: segment.to, + message: segment.message }; return alert; @@ -46,7 +47,8 @@ export class Alert { ``, `From: ${new Date(meta.from)}`, `To: ${new Date(meta.to)}`, - `ID: ${meta.analyticUnitId}` + `ID: ${meta.analyticUnitId}`, + `Message: ${meta.message}` ].join('\n'); } } @@ -111,12 +113,8 @@ class ThresholdAlert extends Alert { `ID: ${meta.analyticUnitId}` ].join('\n'); - if(meta.params !== undefined) { - const metrics = ` - Metrics: - ${this.analyticUnit.metric.targets[0].expr}: ${meta.params.value} - `; - message += metrics; + if(meta.message !== undefined) { + message += meta.message; } return message; } diff --git a/server/src/services/notification_service.ts b/server/src/services/notification_service.ts index de3652d..81a5267 100644 --- a/server/src/services/notification_service.ts +++ b/server/src/services/notification_service.ts @@ -25,7 +25,7 @@ export declare type AnalyticMeta = { grafanaUrl: string, from: number, to: number - params?: any, + message?: any, regionImage?: any }