Browse Source

Removed old validator

Lukas Cerny 1 year ago
parent
commit
616c3597e5
39 changed files with 368 additions and 1413 deletions
  1. 12 0
      src/main/java/cz/senslog/analytics/domain/NotifyTriggerMode.java
  2. 3 3
      src/main/java/cz/senslog/analytics/domain/Sensor.java
  3. 2 4
      src/main/java/cz/senslog/analytics/domain/ThresholdDimension.java
  4. 9 5
      src/main/java/cz/senslog/analytics/module/DoubleStatisticsModule.java
  5. 6 9
      src/main/java/cz/senslog/analytics/module/ObservationReceiverModule.java
  6. 3 3
      src/main/java/cz/senslog/analytics/repository/AnalyticsRepositoryImpl.java
  7. 69 0
      src/main/java/cz/senslog/analytics/utils/DBDomainToValidator.java
  8. 0 11
      src/main/java/cz/senslog/analytics/utils/newvalidator/DisableNotifyTrigger.java
  9. 0 20
      src/main/java/cz/senslog/analytics/utils/newvalidator/InstantNotifyTrigger.java
  10. 0 32
      src/main/java/cz/senslog/analytics/utils/newvalidator/NotifyTrigger.java
  11. 0 58
      src/main/java/cz/senslog/analytics/utils/newvalidator/OnChangeNotifyTrigger.java
  12. 0 77
      src/main/java/cz/senslog/analytics/utils/newvalidator/ThresholdChecker.java
  13. 0 102
      src/main/java/cz/senslog/analytics/utils/newvalidator/ThresholdManager.java
  14. 0 4
      src/main/java/cz/senslog/analytics/utils/newvalidator/ThresholdViolationReport.java
  15. 0 93
      src/main/java/cz/senslog/analytics/utils/newvalidator/Validator.java
  16. 2 2
      src/main/java/cz/senslog/analytics/utils/validator/DataSource.java
  17. 5 23
      src/main/java/cz/senslog/analytics/utils/validator/DisableNotifyTrigger.java
  18. 11 67
      src/main/java/cz/senslog/analytics/utils/validator/InstantNotifyTrigger.java
  19. 24 44
      src/main/java/cz/senslog/analytics/utils/validator/MessageFormatter.java
  20. 1 1
      src/main/java/cz/senslog/analytics/utils/validator/NotifyReport.java
  21. 6 15
      src/main/java/cz/senslog/analytics/utils/validator/NotifyTrigger.java
  22. 35 92
      src/main/java/cz/senslog/analytics/utils/validator/OnChangeNotifyTrigger.java
  23. 1 1
      src/main/java/cz/senslog/analytics/utils/validator/Threshold.java
  24. 30 78
      src/main/java/cz/senslog/analytics/utils/validator/ThresholdChecker.java
  25. 79 59
      src/main/java/cz/senslog/analytics/utils/validator/ThresholdManager.java
  26. 4 0
      src/main/java/cz/senslog/analytics/utils/validator/ThresholdViolationReport.java
  27. 1 1
      src/main/java/cz/senslog/analytics/utils/validator/ValidationReport.java
  28. 1 1
      src/main/java/cz/senslog/analytics/utils/validator/ValidationResult.java
  29. 17 13
      src/main/java/cz/senslog/analytics/utils/validator/Validator.java
  30. 1 1
      src/main/java/cz/senslog/analytics/utils/validator/ViolationReport.java
  31. 0 14
      src/main/java/cz/senslog/analytics/utils/validator/domain/ThresholdValidationResult.java
  32. 0 41
      src/main/java/cz/senslog/analytics/utils/validator/domain/ValidationReport.java
  33. 0 41
      src/main/java/cz/senslog/analytics/utils/validator/domain/ValidationResult.java
  34. 0 6
      src/main/java/cz/senslog/analytics/utils/validator/domain/ViolationReport.java
  35. 0 43
      src/test/java/cz/senslog/analytics/utils/AttributeRegexTest.java
  36. 0 329
      src/test/java/cz/senslog/analytics/utils/ThresholdCheckerTest.java
  37. 18 15
      src/test/java/cz/senslog/analytics/utils/validator/MessageFormatterTest.java
  38. 0 75
      src/test/java/cz/senslog/analytics/utils/validator/OnChangeNotifyTriggerTest.java
  39. 28 30
      src/test/java/cz/senslog/analytics/utils/validator/ThresholdCheckerTest.java

+ 12 - 0
src/main/java/cz/senslog/analytics/domain/NotifyTriggerMode.java

@@ -0,0 +1,12 @@
+package cz.senslog.analytics.domain;
+
+public enum NotifyTriggerMode {
+
+    DISABLED,
+    INSTANT,
+    ON_CHANGE;
+
+    public static NotifyTriggerMode of(String mode) {
+        return valueOf(mode.toUpperCase());
+    }
+}

+ 3 - 3
src/main/java/cz/senslog/analytics/domain/Sensor.java

@@ -1,9 +1,9 @@
 package cz.senslog.analytics.domain;
 
 
-public record Sensor(long id, long unitId, long sensorId) {
+public record Sensor(long id, long unitId, long sensorId, String name) {
 
-    public static Sensor of(long id, long unitId, long  sensorId) {
-        return new Sensor(id, unitId, sensorId);
+    public static Sensor of(long id, long unitId, long  sensorId, String name) {
+        return new Sensor(id, unitId, sensorId, name);
     }
 }

+ 2 - 4
src/main/java/cz/senslog/analytics/domain/ThresholdDimension.java

@@ -1,12 +1,10 @@
 package cz.senslog.analytics.domain;
 
-import cz.senslog.analytics.utils.validator.NotifyTrigger;
-
 import java.util.List;
 
-public record ThresholdDimension(long id, NotifyTrigger.Mode notifyTriggerMode, AttributeType attributeType, List<ThresholdDimensionRule> rules) {
+public record ThresholdDimension(long id, NotifyTriggerMode notifyTriggerMode, AttributeType attributeType, List<ThresholdDimensionRule> rules) {
 
-    public static ThresholdDimension of(long id, NotifyTrigger.Mode notifyTriggerMode, AttributeType attributeType, List<ThresholdDimensionRule> rules) {
+    public static ThresholdDimension of(long id, NotifyTriggerMode notifyTriggerMode, AttributeType attributeType, List<ThresholdDimensionRule> rules) {
         return new ThresholdDimension(id, notifyTriggerMode, attributeType, rules);
     }
 }

+ 9 - 5
src/main/java/cz/senslog/analytics/module/DoubleStatisticsModule.java

@@ -3,9 +3,10 @@ package cz.senslog.analytics.module;
 import cz.senslog.analytics.domain.*;
 import cz.senslog.analytics.module.api.CollectorModule;
 import cz.senslog.analytics.repository.AnalyticsRepository;
+import cz.senslog.analytics.utils.DBDomainToValidator;
 import cz.senslog.analytics.utils.validator.ThresholdChecker;
 import cz.senslog.analytics.utils.validator.Validator;
-import cz.senslog.analytics.utils.validator.domain.ViolationReport;
+import cz.senslog.analytics.utils.validator.ViolationReport;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
@@ -28,7 +29,8 @@ public class DoubleStatisticsModule extends CollectorModule {
         validator = Validator.<DoubleStatistics>create()
                 .addMapping(MIN, s -> s::min)
                 .addMapping(MAX, s -> s::max)
-                .addMapping(AVG, s -> s::average);
+                .addMapping(AVG, s -> s::average)
+                .addMapping(COUNT, s -> () -> (double) s.count());
     }
 
     public DoubleStatisticsModule(AnalyticsRepository configRepo) {
@@ -37,13 +39,15 @@ public class DoubleStatisticsModule extends CollectorModule {
 
     @Override
     protected void init() {
-        thresholdChecker = new ThresholdChecker<>(thresholds(), validator, this::notifyIfViolation, true);
+        thresholdChecker = ThresholdChecker.create(
+                DBDomainToValidator.groupsToDataSources(type().name().toLowerCase(), groups().values(), thresholds()),
+                validator, this::notifyIfViolation, true);
     }
 
     private void notifyIfViolation(ViolationReport report) {
-        Optional<AnalyticGroup> source = Optional.ofNullable(groups().get(report.datasourceId()));
+        Optional<AnalyticGroup> source = Optional.ofNullable(groups().get(report.dataSourceId()));
         String sourceName = source.map(AnalyticGroup::name).orElse(DEFAULT_GROUP_NAME);
-        notify(new ThresholdViolationAlert(type().name(), report.datasourceId(), sourceName, report.message(), report.timestamp()));
+        notify(new ThresholdViolationAlert(type().name(), report.dataSourceId(), sourceName, report.message(), report.timestamp()));
     }
 
     private Stream<StatisticRecord> mapToStatisticRecord(DoubleStatistics st) {

+ 6 - 9
src/main/java/cz/senslog/analytics/module/ObservationReceiverModule.java

@@ -2,14 +2,14 @@ package cz.senslog.analytics.module;
 
 import cz.senslog.analytics.domain.*;
 import cz.senslog.analytics.repository.AnalyticsRepository;
-import cz.senslog.analytics.utils.newvalidator.NotifyTrigger;
-import cz.senslog.analytics.utils.newvalidator.ThresholdChecker;
+import cz.senslog.analytics.utils.DBDomainToValidator;
+import cz.senslog.analytics.utils.validator.ThresholdChecker;
 import cz.senslog.analytics.module.api.Module;
 import cz.senslog.analytics.module.api.ModuleDescriptor;
 import cz.senslog.analytics.module.api.SimpleModule;
-import cz.senslog.analytics.utils.newvalidator.Validator;
+import cz.senslog.analytics.utils.validator.Validator;
 import cz.senslog.analytics.utils.Tuple;
-import cz.senslog.analytics.utils.newvalidator.ViolationReport;
+import cz.senslog.analytics.utils.validator.ViolationReport;
 import io.vertx.core.Future;
 import io.vertx.core.Promise;
 import org.apache.logging.log4j.LogManager;
@@ -65,11 +65,8 @@ public class ObservationReceiverModule extends SimpleModule {
                     }
 
                     List<Threshold> sensorsThresholds = ar.resultAt(1);
-                    // TODO refactor
-                    thresholdChecker = ThresholdChecker.create(MODULE_NAME,
-                            sensorsThresholds.stream().map(t -> new cz.senslog.analytics.utils.newvalidator.Threshold(t.id(), t.datasourceId(), t.enableProcess(), t.enableAlert(), t.customAlertMessage(),
-                                    t.thresholdDimensions().stream().map(d -> new cz.senslog.analytics.utils.newvalidator.Threshold.Dimension(d.attributeType(), NotifyTrigger.Mode.of(d.notifyTriggerMode().name()),
-                                            d.rules().stream().map(r -> new cz.senslog.analytics.utils.newvalidator.Threshold.Dimension.Rule(r.comparisonOperator(), r.value())).toList())).toList())).toList(),
+                    thresholdChecker = ThresholdChecker.create(
+                            DBDomainToValidator.sensorsToDataSources(MODULE_NAME, allSensors, sensorsThresholds),
                             validator, this::notifyIfViolated, true);
 
                     List<Tuple<Long, AnalyticGroup>> groupsWithDS = ar.resultAt(2);

+ 3 - 3
src/main/java/cz/senslog/analytics/repository/AnalyticsRepositoryImpl.java

@@ -3,7 +3,6 @@ package cz.senslog.analytics.repository;
 import cz.senslog.analytics.domain.*;
 import cz.senslog.analytics.utils.Tuple;
 import cz.senslog.analytics.domain.ComparisonOperator;
-import cz.senslog.analytics.utils.validator.NotifyTrigger;
 import io.vertx.core.CompositeFuture;
 import io.vertx.core.Future;
 import io.vertx.sqlclient.Pool;
@@ -62,7 +61,8 @@ public final class AnalyticsRepositoryImpl implements AnalyticsRepository {
                         .map(row -> Sensor.of(
                                 row.getLong("id"),
                                 row.getLong("unit_id"),
-                                row.getLong("sensor_id")
+                                row.getLong("sensor_id"),
+                                row.getString("name")
                         )).toList()
                 );
     }
@@ -90,7 +90,7 @@ public final class AnalyticsRepositoryImpl implements AnalyticsRepository {
                             .map(rs -> StreamSupport.stream(rs.spliterator(), false)
                                     .map(row -> ThresholdDimension.of(
                                             row.getLong("id"),
-                                            NotifyTrigger.Mode.of(row.getString("notify_trigger_mode")),
+                                            NotifyTriggerMode.of(row.getString("notify_trigger_mode")),
                                             AttributeType.of(row.getString("attribute_type")),
                                                     new ArrayList<>() {{
                                                         add(ThresholdDimensionRule.of(

+ 69 - 0
src/main/java/cz/senslog/analytics/utils/DBDomainToValidator.java

@@ -0,0 +1,69 @@
+package cz.senslog.analytics.utils;
+
+import cz.senslog.analytics.domain.*;
+import cz.senslog.analytics.utils.validator.DataSource;
+import cz.senslog.analytics.utils.validator.NotifyTrigger;
+import cz.senslog.analytics.utils.validator.Threshold;
+
+import java.util.*;
+
+import static java.util.Collections.emptyList;
+
+public final class DBDomainToValidator {
+
+    public static NotifyTrigger.Mode toNotifyTrigger(final NotifyTriggerMode dbNotifyTriggerMode) {
+        switch (dbNotifyTriggerMode) {
+            case DISABLED -> {
+                return NotifyTrigger.Mode.DISABLED;
+            }
+            case ON_CHANGE -> {
+                return NotifyTrigger.Mode.ON_CHANGE;
+            }
+            case INSTANT -> {
+                return NotifyTrigger.Mode.INSTANT;
+            }
+            default -> {
+                return null;
+            }
+        }
+    }
+
+    public static Threshold toThreshold(final cz.senslog.analytics.domain.Threshold threshold) {
+        List<Threshold.Dimension> dimensions = new ArrayList<>(threshold.thresholdDimensions().size());
+        for (ThresholdDimension dm : threshold.thresholdDimensions()) {
+            List<Threshold.Dimension.Rule> rules = new ArrayList<>(dm.rules().size());
+            for (ThresholdDimensionRule r : dm.rules()) {
+                rules.add(new Threshold.Dimension.Rule(r.comparisonOperator(), r.value()));
+            }
+            dimensions.add(new Threshold.Dimension(dm.attributeType(), toNotifyTrigger(dm.notifyTriggerMode()), rules));
+        }
+        return new Threshold(
+                threshold.id(), threshold.datasourceId(), threshold.enableProcess(), threshold.enableAlert(), threshold.customAlertMessage(), dimensions);
+    }
+
+    public static List<DataSource> sensorsToDataSources(final String moduleName, final Collection<Sensor> sensors, final Collection<cz.senslog.analytics.domain.Threshold> thresholds) {
+        final Map<Long, List<Threshold>> thresholdsMap = new HashMap<>();
+        for (cz.senslog.analytics.domain.Threshold th : thresholds) {
+            thresholdsMap.computeIfAbsent(th.datasourceId(), dsID -> new ArrayList<>()).add(toThreshold(th));
+        }
+        final List<DataSource> dataSources = new ArrayList<>(thresholdsMap.size());
+        for (Sensor s : sensors) {
+            final long datasourceId = s.id();
+            dataSources.add(new DataSource(datasourceId, s.name(), moduleName, thresholdsMap.getOrDefault(datasourceId, emptyList())));
+        }
+        return dataSources;
+    }
+
+    public static List<DataSource> groupsToDataSources(final String moduleName, final Collection<AnalyticGroup> analyticGroups, final Collection<cz.senslog.analytics.domain.Threshold> thresholds) {
+        final Map<Long, List<Threshold>> thresholdsMap = new HashMap<>(analyticGroups.size());
+        for (cz.senslog.analytics.domain.Threshold th : thresholds) {
+            thresholdsMap.computeIfAbsent(th.datasourceId(), dsID -> new ArrayList<>()).add(toThreshold(th));
+        }
+        final List<DataSource> dataSources = new ArrayList<>(thresholdsMap.size());
+        for (AnalyticGroup ag : analyticGroups) {
+            final long datasourceId = ag.id();
+            dataSources.add(new DataSource(datasourceId, ag.name(), moduleName, thresholdsMap.getOrDefault(datasourceId, emptyList())));
+        }
+        return dataSources;
+    }
+}

+ 0 - 11
src/main/java/cz/senslog/analytics/utils/newvalidator/DisableNotifyTrigger.java

@@ -1,11 +0,0 @@
-package cz.senslog.analytics.utils.newvalidator;
-
-import java.util.Optional;
-
-class DisableNotifyTrigger implements NotifyTrigger {
-
-    @Override
-    public Optional<NotifyReport> accept(ValidationResult validationResult) {
-        return Optional.empty();
-    }
-}

+ 0 - 20
src/main/java/cz/senslog/analytics/utils/newvalidator/InstantNotifyTrigger.java

@@ -1,20 +0,0 @@
-package cz.senslog.analytics.utils.newvalidator;
-
-import org.apache.logging.log4j.util.Strings;
-
-import java.util.Optional;
-
-class InstantNotifyTrigger implements NotifyTrigger {
-
-    @Override
-    public Optional<NotifyReport> accept(ValidationResult validationResult) {
-        if (validationResult.hasViolation()) {
-            return Optional.of(new NotifyReport(
-                    validationResult.violatedValue(),
-                    validationResult.violatedAttribute(),
-                    "Inside " + validationResult
-            ));
-        }
-        return Optional.empty();
-    }
-}

+ 0 - 32
src/main/java/cz/senslog/analytics/utils/newvalidator/NotifyTrigger.java

@@ -1,32 +0,0 @@
-package cz.senslog.analytics.utils.newvalidator;
-
-import java.util.Optional;
-import java.util.function.Supplier;
-
-public interface NotifyTrigger {
-
-    Optional<NotifyReport> accept(ValidationResult validationResult);
-
-    enum Mode {
-        DISABLED    (DisableNotifyTrigger::new),
-        INSTANT     (InstantNotifyTrigger::new),
-        ON_CHANGE   (OnChangeNotifyTrigger::new),
-
-        ;
-
-        Mode(Supplier<NotifyTrigger> constructCreator) {
-            this.constructCreator = constructCreator;
-        }
-
-        private final Supplier<NotifyTrigger> constructCreator;
-
-        NotifyTrigger createInstance() {
-            return constructCreator.get();
-        }
-
-        public static NotifyTrigger.Mode of(String mode) {
-            return NotifyTrigger.Mode.valueOf(mode.toUpperCase());
-        }
-    }
-
-}

+ 0 - 58
src/main/java/cz/senslog/analytics/utils/newvalidator/OnChangeNotifyTrigger.java

@@ -1,58 +0,0 @@
-package cz.senslog.analytics.utils.newvalidator;
-
-import java.util.Optional;
-
-class OnChangeNotifyTrigger implements NotifyTrigger {
-
-    private static final int CACHE_LEN = 2;
-
-    private static int nextIndex(int index) {
-        return  (index + 1) % CACHE_LEN;
-    }
-
-    private static int previousIndex(int index) {
-        return (index <= 0 ? CACHE_LEN : index) - 1;
-    }
-
-    private static final class ResultCache {
-        private final ValidationResult[] resultsArr;
-        private int freeIndex;
-
-        private ResultCache(ValidationResult[] resultsArr, int freeIndex) {
-            this.resultsArr = resultsArr;
-            this.freeIndex = freeIndex;
-        }
-    }
-
-    private final ResultCache resultCache;
-
-    public OnChangeNotifyTrigger() {
-        this.resultCache = new ResultCache(new ValidationResult[CACHE_LEN], 0);
-    }
-
-    @Override
-    public Optional<NotifyReport> accept(ValidationResult validationResult) {
-
-        resultCache.resultsArr[resultCache.freeIndex] = validationResult;
-        resultCache.freeIndex = nextIndex(resultCache.freeIndex);
-
-        int currentDataInd = previousIndex(resultCache.freeIndex);
-        ValidationResult currentRes = resultCache.resultsArr[currentDataInd];
-        ValidationResult previousRes = resultCache.resultsArr[previousIndex(currentDataInd)];
-
-        if ((previousRes == null || !previousRes.hasViolation()) && currentRes.hasViolation()) {
-            // notify about the enter the threshold
-            return Optional.of(new NotifyReport(
-                currentRes.violatedValue(), currentRes.violatedAttribute(), ("Entry " + currentRes)
-            ));
-        } else if(!currentRes.hasViolation() && (previousRes != null && previousRes.hasViolation())) {
-            // notify about the exit the threshold
-            return Optional.of(new NotifyReport(
-                    currentRes.violatedValue(), currentRes.violatedAttribute(), ("Exit " + currentRes)
-            ));
-        }
-
-        // nothing to notify
-        return Optional.empty();
-    }
-}

+ 0 - 77
src/main/java/cz/senslog/analytics/utils/newvalidator/ThresholdChecker.java

@@ -1,77 +0,0 @@
-package cz.senslog.analytics.utils.newvalidator;
-
-import cz.senslog.analytics.domain.TimeSeriesDatasource;
-
-import java.time.OffsetDateTime;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-import static java.util.Collections.emptyList;
-import static java.util.stream.Collectors.mapping;
-import static java.util.stream.Collectors.toList;
-
-public class ThresholdChecker<DS extends TimeSeriesDatasource> {
-
-    public static <T extends TimeSeriesDatasource> ThresholdChecker<T> disabled() {
-        return ThresholdChecker.create("disabled", emptyList(), null, r -> {}, true);
-    }
-
-    private final Map<Long, ThresholdManager<DS>> thresholdManagers;
-    private final Consumer<ViolationReport> notifier;
-    private final boolean defaultEnablingProcess;
-
-    public static <DS extends TimeSeriesDatasource> ThresholdChecker<DS> create(
-            final String moduleName,
-            final List<Threshold> thresholds,
-            final Validator<DS> validator,
-            final Consumer<ViolationReport> notifier,
-            final boolean defaultEnablingProcess
-    ) {
-//        Objects.requireNonNull(validator);
-        Objects.requireNonNull(thresholds);
-        Objects.requireNonNull(notifier);
-
-        final Map<Long, List<Threshold>> dsToTh = thresholds.stream()
-                .collect(Collectors.groupingBy(Threshold::dataSourceId, mapping(Function.identity(), toList())));
-
-        // TODO get datasource
-        final Map<Long, ThresholdManager<DS>> thresholdManagers = new HashMap<>(dsToTh.size());
-        for (Map.Entry<Long, List<Threshold>> datasourceEntry : dsToTh.entrySet()) {
-            thresholdManagers.put(datasourceEntry.getKey(), new ThresholdManager<>(
-                    new DataSource(datasourceEntry.getKey(), "datasource("+datasourceEntry.getKey()+")", moduleName), validator, datasourceEntry.getValue()
-            ));
-        }
-
-        return new ThresholdChecker<>(notifier, thresholdManagers, defaultEnablingProcess);
-    }
-
-    private ThresholdChecker(Consumer<ViolationReport> notifier, Map<Long, ThresholdManager<DS>> thresholdManagers, boolean defaultEnablingProcess) {
-        this.defaultEnablingProcess = defaultEnablingProcess;
-        this.notifier = notifier;
-        this.thresholdManagers = thresholdManagers;
-    }
-
-    public boolean check(DS data) {
-
-        long sourceId = data.datasourceId();
-        OffsetDateTime timestamp = data.timestamp();
-
-        ThresholdManager<DS> thresholdManager = thresholdManagers.get(sourceId);
-        if (sourceId <= 0 || thresholdManager == null) {
-            return defaultEnablingProcess;
-        }
-
-        ValidationReport validationReport = thresholdManager.accept(data);
-
-        if (validationReport.message().isPresent()) {
-            notifier.accept(new ViolationReport(sourceId, timestamp, validationReport.message().get()));
-        }
-
-        return validationReport.process();
-    }
-}

+ 0 - 102
src/main/java/cz/senslog/analytics/utils/newvalidator/ThresholdManager.java

@@ -1,102 +0,0 @@
-package cz.senslog.analytics.utils.newvalidator;
-
-import cz.senslog.analytics.domain.AttributeType;
-import cz.senslog.analytics.domain.TimeSeriesDatasource;
-import cz.senslog.analytics.domain.VariableType;
-
-import java.time.OffsetDateTime;
-import java.time.format.DateTimeFormatter;
-import java.util.*;
-
-import static cz.senslog.analytics.domain.VariableType.*;
-import static java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME;
-
-class ThresholdManager<DS extends TimeSeriesDatasource> {
-
-    private final DataSource datasource;
-    private final Validator<DS> validator;
-    private final List<Threshold> thresholds;
-    private final Map<Long, Map<AttributeType, NotifyTrigger>> notifyTriggers;
-    private final MessageFormatter messageFormatter;
-
-    protected ThresholdManager(DataSource datasource, Validator<DS> validator, List<Threshold> thresholds) {
-        this.datasource = datasource;
-        this.validator = validator;
-        this.thresholds = thresholds;
-
-        Set<AttributeType> supportedAttributes = validator.attributes();
-        this.messageFormatter = MessageFormatter.createOf(Set.of(VariableType.values()), supportedAttributes);
-        this.notifyTriggers = new HashMap<>(thresholds.size());
-        for (Threshold th : thresholds) {
-            // TODO check if Threshold#message is valid according to the dimension#attributeType
-            for (Threshold.Dimension dim : th.dimensions()) {
-                notifyTriggers.computeIfAbsent(th.id(), thId -> new HashMap<>(dim.rules().size()))
-                        .computeIfAbsent(dim.attributeType(), attr -> dim.notifyTriggerMode().createInstance());
-            }
-        }
-    }
-
-    public ValidationReport accept(final DS data) {
-        if (data == null || data.datasourceId() != this.datasource.id()) {
-            return null;
-        }
-
-        OffsetDateTime timestamp = data.timestamp();
-        List<String> messagesToNotify = new ArrayList<>(thresholds.size());
-
-        boolean shouldProcess = false;
-        boolean shouldNotify = false;
-
-        for (Threshold threshold : thresholds) {
-            ValidationResult[] validationResults = validator.validate(data, threshold);
-            Map<AttributeType, NotifyTrigger> triggers = notifyTriggers.get(threshold.id());
-            List<NotifyReport> thReports = new ArrayList<>(validationResults.length);
-
-            boolean thIsViolated = true;
-            boolean thShouldNotify = threshold.enableAlert(); // if alert is disabled then it is always false
-
-            for (ValidationResult result : validationResults) {
-                Optional<NotifyReport> dimNotifyReportOpt = triggers.get(result.violatedAttribute()).accept(result); // all trigger must have something to notify
-                dimNotifyReportOpt.ifPresent(thReports::add);
-                thShouldNotify = thShouldNotify && dimNotifyReportOpt.isPresent(); // isPresent() = true = violation should notify
-                thIsViolated = thIsViolated && result.hasViolation(); // all must be violated
-            }
-
-            // should process if at least one threshold is valid over all dimensions OR the process is enabled if violation
-            shouldProcess = shouldProcess || !thIsViolated || threshold.enableProcess();
-
-            // none or at least one has something to notify
-            shouldNotify = shouldNotify || thShouldNotify;
-
-            if (thShouldNotify) {
-                if (threshold.messagePattern() != null) {
-                    String patternMessage = threshold.messagePattern();
-                    Map<AttributeType, Double> messageArgs = new HashMap<>(thReports.size());
-                    for (NotifyReport thR : thReports) {
-                        messageArgs.put(thR.attributeType(), thR.value());
-                    }
-
-                    messagesToNotify.add(messageFormatter.format(patternMessage, messageArgs, Map.of(
-                            TIMESTAMP, (f -> timestamp.format(f.map(DateTimeFormatter::ofPattern).orElse(ISO_OFFSET_DATE_TIME))),
-                            MODULE, (f -> datasource.moduleName()),
-                            DATASOURCE, (f -> datasource.moduleName())
-                    )));
-
-                } else {
-                    StringBuilder thMessage = new StringBuilder();
-                    NotifyReport r0 = thReports.get(0);
-                    thMessage.append(r0.violatedRulesMessage());
-                    for (int i = 1; i < thReports.size(); i++) {
-                        thMessage.append("\n").append(thReports.get(i).violatedRulesMessage());
-                    }
-                    messagesToNotify.add(thMessage.toString());
-                }
-            }
-        }
-
-        return new ValidationReport(
-                shouldProcess,
-                shouldNotify ? Optional.of( String.join("\n\n", messagesToNotify)) : Optional.empty()
-        );
-    }
-}

+ 0 - 4
src/main/java/cz/senslog/analytics/utils/newvalidator/ThresholdViolationReport.java

@@ -1,4 +0,0 @@
-package cz.senslog.analytics.utils.newvalidator;
-
-record ThresholdViolationReport() {
-}

+ 0 - 93
src/main/java/cz/senslog/analytics/utils/newvalidator/Validator.java

@@ -1,93 +0,0 @@
-package cz.senslog.analytics.utils.newvalidator;
-
-import cz.senslog.analytics.domain.AttributeType;
-import cz.senslog.analytics.domain.ComparisonOperator;
-
-import java.util.*;
-import java.util.function.BiFunction;
-import java.util.function.Function;
-import java.util.function.Supplier;
-
-import static cz.senslog.analytics.domain.ComparisonOperator.*;
-import static cz.senslog.analytics.domain.ComparisonOperator.NE;
-import static java.util.Collections.emptyList;
-
-public class Validator<T> {
-
-    private static final Map<ComparisonOperator, BiFunction<Double, Double, Boolean>> functions;
-
-    static {
-        functions = new HashMap<>(6);
-        functions.put(GT, (val, ths) -> val > ths);
-        functions.put(GE, (val, ths) -> val >= ths);
-        functions.put(LT, (val, ths) -> val < ths);
-        functions.put(LE, (val, ths) -> val <= ths);
-        functions.put(EQ, Double::equals);
-        functions.put(NE, (val, ths) -> !val.equals(ths));
-    }
-
-    public static boolean checkThresholdValue(ComparisonOperator mode, Double value, Double threshold) {
-        if (mode == null || value == null || threshold == null) return false;
-        return functions.getOrDefault(mode, (val, ths) -> false).apply(value, threshold);
-    }
-
-    public static boolean checkThresholdValue(Threshold.Dimension.Rule rule, Double value) {
-        if (rule == null || value == null) return false;
-        return checkThresholdValue(rule.comparisonOperator(), value, rule.value());
-    }
-
-    public interface AttributeMapping<T> {
-        Validator<T> addMapping(AttributeType property, Function<T, Supplier<Double>> getter);
-    }
-
-
-    public static <T> Validator.AttributeMapping<T> create() {
-        return new Validator.AttributeMapping<>() {
-            private final Validator<T> validator = new Validator<>();
-            @Override
-            public Validator<T> addMapping(AttributeType property, Function<T, Supplier<Double>> getter) {
-                validator.map.put(property, getter);
-                return validator;
-            }
-        };
-    }
-
-    private final Map<AttributeType, Function<T, Supplier<Double>>> map;
-
-    private Validator() {
-        this.map = new HashMap<>();
-    }
-
-    public Set<AttributeType> attributes() {
-        return map.keySet();
-    }
-
-    public Validator<T> addMapping(AttributeType property, Function<T, Supplier<Double>> getter) {
-        map.put(property, getter);
-        return this;
-    }
-
-    public ValidationResult[] validate(T object, Threshold threshold) {
-        return validate(object, threshold, map);
-    }
-
-    private static <T> ValidationResult validate(T object, Threshold.Dimension dimension, Map<AttributeType, Function<T, Supplier<Double>>> attributeMapping) {
-        final Double testingValue = attributeMapping.getOrDefault(dimension.attributeType(), (o) -> () -> null).apply(object).get();
-        final List<Threshold.Dimension.Rule> violatedRules = new ArrayList<>(dimension.rules().size());
-        boolean passed = true;
-        for (Threshold.Dimension.Rule rule : dimension.rules()) {
-            passed &= checkThresholdValue(rule, testingValue);
-            violatedRules.add(rule);
-        }
-        return new ValidationResult(testingValue, dimension.attributeType(), passed ? violatedRules : emptyList());
-    }
-
-    private static <T> ValidationResult[] validate(T object, Threshold threshold, Map<AttributeType, Function<T, Supplier<Double>>> attributeMapping) {
-        final ValidationResult[] results = new ValidationResult[threshold.dimensions().size()];
-        int resultIndex = 0;
-        for (Threshold.Dimension dimension : threshold.dimensions()) {
-            results[resultIndex++] = validate(object, dimension, attributeMapping);
-        }
-        return results;
-    }
-}

+ 2 - 2
src/main/java/cz/senslog/analytics/utils/newvalidator/DataSource.java → src/main/java/cz/senslog/analytics/utils/validator/DataSource.java

@@ -1,6 +1,6 @@
-package cz.senslog.analytics.utils.newvalidator;
+package cz.senslog.analytics.utils.validator;
 
 import java.util.List;
 
-public record DataSource(long id, String name, String moduleName) {
+public record DataSource(long id, String name, String moduleName, List<Threshold> thresholds) {
 }

+ 5 - 23
src/main/java/cz/senslog/analytics/utils/validator/DisableNotifyTrigger.java

@@ -1,29 +1,11 @@
 package cz.senslog.analytics.utils.validator;
 
+import java.util.Optional;
 
-import cz.senslog.analytics.domain.Threshold;
-import cz.senslog.analytics.utils.validator.domain.ValidationReport;
-import cz.senslog.analytics.utils.validator.domain.ValidationResult;
-
-public class DisableNotifyTrigger implements NotifyTrigger {
-
-    public DisableNotifyTrigger() {}
-
-    @Override
-    public void accept(Threshold threshold, ValidationResult... validationResults) {}
-
-    @Override
-    public Mode mode() {
-        return Mode.DISABLED;
-    }
-
-    @Override
-    public boolean shouldNotify() {
-        return false;
-    }
+class DisableNotifyTrigger implements NotifyTrigger {
 
     @Override
-    public ValidationReport resultsToNotify() {
-        return new ValidationReport(null, new ValidationResult[0]);
+    public Optional<NotifyReport> accept(ValidationResult validationResult) {
+        return Optional.empty();
     }
-}
+}

+ 11 - 67
src/main/java/cz/senslog/analytics/utils/validator/InstantNotifyTrigger.java

@@ -1,74 +1,18 @@
 package cz.senslog.analytics.utils.validator;
 
-import cz.senslog.analytics.domain.*;
-import cz.senslog.analytics.utils.validator.domain.ThresholdValidationResult;
-import cz.senslog.analytics.utils.validator.domain.ValidationReport;
-import cz.senslog.analytics.utils.validator.domain.ValidationResult;
+import java.util.Optional;
 
-import java.util.*;
-
-/**
- * The class InstantNotifyTrigger is unique for each datasource, i.e., unit/sensor = one NotifyTrigger
- */
-public class InstantNotifyTrigger implements NotifyTrigger {
-
-    private final Map<Long, ThresholdValidationResult> currentResultMap;
-    private Set<Long> tempAttrsToNotify;
-
-    public InstantNotifyTrigger() {
-        this.currentResultMap = new HashMap<>();
-    }
+class InstantNotifyTrigger implements NotifyTrigger {
 
     @Override
-    public void accept(Threshold threshold, ValidationResult... validationResults) {
-        currentResultMap.put(threshold.id(), new ThresholdValidationResult(threshold, validationResults));
-    }
-
-    @Override
-    public Mode mode() {
-        return Mode.INSTANT;
-    }
-
-    @Override
-    public boolean shouldNotify() {
-        boolean notify = false;
-        tempAttrsToNotify = new HashSet<>();
-        for (Map.Entry<Long, ThresholdValidationResult> thresholdResultEntry : currentResultMap.entrySet()) {
-            if (thresholdResultEntry.getValue().isViolated()) {
-                notify = true;
-                tempAttrsToNotify.add(thresholdResultEntry.getKey());
-            }
+    public Optional<NotifyReport> accept(ValidationResult validationResult) {
+        if (validationResult.hasViolation()) {
+            return Optional.of(new NotifyReport(
+                    validationResult.violatedValue(),
+                    validationResult.violatedAttribute(),
+                    "Inside " + validationResult
+            ));
         }
-        return notify;
-    }
-
-    @Override
-    public ValidationReport resultsToNotify() {
-
-        if (tempAttrsToNotify.isEmpty()) {
-            return ValidationReport.empty();
-        }
-
-        StringBuilder alertMessageBuilder = new StringBuilder();
-        List<ValidationResult> attributeValidationResults = new ArrayList<>();
-
-        for (Long thresholdId : tempAttrsToNotify) {
-            ThresholdValidationResult thResult = currentResultMap.get(thresholdId);
-            attributeValidationResults.addAll(Arrays.asList(thResult.validationResults()));
-
-            if (thResult.threshold().enableAlert()) {
-                String alertMessage = thResult.threshold().customAlertMessage();
-                if (alertMessage != null) {
-                    alertMessageBuilder.append(alertMessage).append("\n");
-                }
-            }
-        }
-
-        tempAttrsToNotify = null;
-
-        return new ValidationReport(
-                alertMessageBuilder.toString(),
-                attributeValidationResults.toArray(new ValidationResult[0])
-        );
+        return Optional.empty();
     }
-}
+}

+ 24 - 44
src/main/java/cz/senslog/analytics/utils/newvalidator/MessageFormatter.java → src/main/java/cz/senslog/analytics/utils/validator/MessageFormatter.java

@@ -1,21 +1,16 @@
-package cz.senslog.analytics.utils.newvalidator;
+package cz.senslog.analytics.utils.validator;
 
 import cz.senslog.analytics.domain.AttributeType;
 import cz.senslog.analytics.domain.VariableType;
-import cz.senslog.analytics.utils.Tuple;
 import org.apache.logging.log4j.util.Strings;
 
 import java.time.DateTimeException;
 import java.time.OffsetDateTime;
 import java.util.*;
-import java.util.function.BiFunction;
-import java.util.function.Consumer;
 import java.util.function.Function;
-import java.util.function.Supplier;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import static java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME;
 import static java.time.format.DateTimeFormatter.ofPattern;
 
 public class MessageFormatter {
@@ -24,9 +19,6 @@ public class MessageFormatter {
     private final Pattern variablePattern;
 
     public static MessageFormatter createOf(Set<VariableType> variables, Set<AttributeType> attributes) {
-        if (variables.isEmpty() || attributes.isEmpty()) {
-            return null;
-        }
         return new MessageFormatter(variables, attributes);
     }
 
@@ -87,35 +79,23 @@ public class MessageFormatter {
         return message;
     }
 
-    /*
-    public boolean isValid(final String patternMessage, List<ThresholdDimension> thresholdDimensions) {
-        AttributeType[] types = new AttributeType[thresholdDimensions.size()];
-        for (int i = 0; i < thresholdDimensions.size(); i++) {
-            types[i] = thresholdDimensions.get(i).attributeType();
-        }
-        return isValid(patternMessage, types);
+    public boolean isNotValid(final String patternMessage) {
+        return !isValid(patternMessage);
     }
 
-    public boolean isValid(final String patternMessage, AttributeType[] attributeTypes) {
+    public boolean isValid(final String patternMessage) {
 
         if (patternMessage == null || Strings.isBlank(patternMessage) || patternMessage.isEmpty()) {
             return true;
         }
-
-        AttributeType[] enableTypes = new AttributeType[AttributeType.values().length];
-        for (AttributeType type : attributeTypes) {
-            enableTypes[type.ordinal()] = type;
-        }
-
         double testValue = 0.0;
 
-        Matcher attrMatcher = attributePattern.matcher(patternMessage);
-        while (attrMatcher.find()) {
-            String attribute = attrMatcher.group("attribute").substring(1);
-            String format = attrMatcher.group("format");
+        Matcher aMatcher = attributePattern.matcher(patternMessage);
+        while (aMatcher.find()) {
+            AttributeType attribute = AttributeType.of(aMatcher.group("attribute").substring(1));
+            String format = aMatcher.group("format");
 
-            AttributeType attrType = AttributeType.of(attribute);
-            if (attrType == null || enableTypes[attrType.ordinal()] == null) {
+            if (attribute == null) {
                 return false;
             }
             if (Strings.isNotBlank(format)) {
@@ -127,26 +107,26 @@ public class MessageFormatter {
             }
         }
 
-        Matcher variableMatcher = variablePattern.matcher(patternMessage);
-        while (variableMatcher.find()) {
-            String variable = variableMatcher.group("variable").substring(1);
-            String format = variableMatcher.group("format");
+        Matcher vMatcher = variablePattern.matcher(patternMessage);
+        while (vMatcher.find()) {
+            VariableType variable = VariableType.of(vMatcher.group("variable").substring(1));
+            String format = vMatcher.group("format");
 
-            if (variable.equalsIgnoreCase("timestamp") && Strings.isNotBlank(format)) {
-                try {
-                    OffsetDateTime.now().format(ofPattern(format));
-                } catch (IllegalArgumentException | DateTimeException e) {
-                    return false;
+            if (variable == null) {
+                return false;
+            }
+
+            if (format != null) {
+                if (variable == VariableType.TIMESTAMP) {
+                    try {
+                        OffsetDateTime.now().format(ofPattern(format));
+                    } catch (IllegalArgumentException | DateTimeException e) {
+                        return false;
+                    }
                 }
             }
         }
 
         return true;
     }
-
-    public boolean isNotValid(final String patternMessage, Map<AttributeType, Double> args) {
-        return !isValid(patternMessage, args);
-    }
-
-     */
 }

+ 1 - 1
src/main/java/cz/senslog/analytics/utils/newvalidator/NotifyReport.java → src/main/java/cz/senslog/analytics/utils/validator/NotifyReport.java

@@ -1,4 +1,4 @@
-package cz.senslog.analytics.utils.newvalidator;
+package cz.senslog.analytics.utils.validator;
 
 import cz.senslog.analytics.domain.AttributeType;
 

+ 6 - 15
src/main/java/cz/senslog/analytics/utils/validator/NotifyTrigger.java

@@ -1,19 +1,11 @@
 package cz.senslog.analytics.utils.validator;
 
-
-import cz.senslog.analytics.domain.Threshold;
-import cz.senslog.analytics.utils.validator.domain.ValidationReport;
-import cz.senslog.analytics.utils.validator.domain.ValidationResult;
-
+import java.util.Optional;
 import java.util.function.Supplier;
 
 public interface NotifyTrigger {
 
-    void accept(Threshold threshold, ValidationResult... validationResults);
-
-    Mode mode();
-    boolean shouldNotify();
-    ValidationReport resultsToNotify();
+    Optional<NotifyReport> accept(ValidationResult validationResult);
 
     enum Mode {
         DISABLED    (DisableNotifyTrigger::new),
@@ -28,14 +20,13 @@ public interface NotifyTrigger {
 
         private final Supplier<NotifyTrigger> constructCreator;
 
-        public NotifyTrigger createInstance() {
+        NotifyTrigger createInstance() {
             return constructCreator.get();
         }
 
-        public static Mode of(String mode) {
-            return Mode.valueOf(mode.toUpperCase());
+        public static NotifyTrigger.Mode of(String mode) {
+            return NotifyTrigger.Mode.valueOf(mode.toUpperCase());
         }
     }
 
-
-}
+}

+ 35 - 92
src/main/java/cz/senslog/analytics/utils/validator/OnChangeNotifyTrigger.java

@@ -1,55 +1,11 @@
 package cz.senslog.analytics.utils.validator;
 
+import java.util.Optional;
 
-import cz.senslog.analytics.domain.Threshold;
-import cz.senslog.analytics.domain.ThresholdDimension;
-import cz.senslog.analytics.utils.validator.domain.ThresholdValidationResult;
-import cz.senslog.analytics.utils.validator.domain.ValidationReport;
-import cz.senslog.analytics.utils.validator.domain.ValidationResult;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class OnChangeNotifyTrigger implements NotifyTrigger {
-
-    private static final class CacheWrapper {
-        private final ThresholdValidationResult[] resultsArr;
-        private int freeIndex;
-
-        private CacheWrapper(ThresholdValidationResult[] resultsArr, int freeIndex) {
-            this.resultsArr = resultsArr;
-            this.freeIndex = freeIndex;
-        }
-    }
+class OnChangeNotifyTrigger implements NotifyTrigger {
 
     private static final int CACHE_LEN = 2;
 
-    private final Map<Long, CacheWrapper> cacheMap;
-
-    private List<ThresholdValidationResult> tempToNotify;
-
-    public OnChangeNotifyTrigger() {
-        this.cacheMap = new HashMap<>();
-    }
-
-    @Override
-    public void accept(Threshold threshold, ValidationResult... validationResults) {
-        long thresholdId = threshold.id();
-        if (!cacheMap.containsKey(thresholdId)) {
-            cacheMap.put(thresholdId, new CacheWrapper(new ThresholdValidationResult[CACHE_LEN], 0));
-        }
-        CacheWrapper cache = cacheMap.get(thresholdId);
-        cache.resultsArr[cache.freeIndex] = new ThresholdValidationResult(threshold, validationResults);
-        cache.freeIndex = nextIndex(cache.freeIndex);
-    }
-
-    @Override
-    public Mode mode() {
-        return Mode.ON_CHANGE;
-    }
-
     private static int nextIndex(int index) {
         return  (index + 1) % CACHE_LEN;
     }
@@ -58,58 +14,45 @@ public class OnChangeNotifyTrigger implements NotifyTrigger {
         return (index <= 0 ? CACHE_LEN : index) - 1;
     }
 
-    @Override
-    public boolean shouldNotify() {
-        boolean notify = false;
-        tempToNotify = new ArrayList<>(cacheMap.size());
-        for (CacheWrapper cache : cacheMap.values()) { // over all thresholds
-            int currentDataInd = previousIndex(cache.freeIndex);
-            ThresholdValidationResult currentRes = cache.resultsArr[currentDataInd];
-            ThresholdValidationResult previousRes = cache.resultsArr[previousIndex(currentDataInd)];
-            if ((previousRes == null || !previousRes.isViolated()) && currentRes.isViolated()) {
-                notify = true; // enter the threshold
-                tempToNotify.add(currentRes);
-            } else if(!currentRes.isViolated() && (previousRes != null && previousRes.isViolated())) {
-                notify = true; // exit the threshold
-                tempToNotify.add(currentRes);
-            }
-        }
-        return notify;
-    }
-
-    @Override
-    public ValidationReport resultsToNotify() {
+    private static final class ResultCache {
+        private final ValidationResult[] resultsArr;
+        private int freeIndex;
 
-        if (tempToNotify == null) {
-            return ValidationReport.empty();
+        private ResultCache(ValidationResult[] resultsArr, int freeIndex) {
+            this.resultsArr = resultsArr;
+            this.freeIndex = freeIndex;
         }
+    }
 
-        StringBuilder alertMessageBuilder = new StringBuilder();
-        List<ValidationResult> attributeValidationResults = new ArrayList<>();
+    private final ResultCache resultCache;
 
-        for (ThresholdValidationResult thResult : tempToNotify) {
-            for (ThresholdDimension thEdge : thResult.threshold().thresholdDimensions()) {
-                    for (ValidationResult validationResult : thResult.validationResults()) {
-                        if (validationResult.attributeType().equals(thEdge.attributeType())) {
-                            attributeValidationResults.add(validationResult);
-                        }
-                }
-            }
+    public OnChangeNotifyTrigger() {
+        this.resultCache = new ResultCache(new ValidationResult[CACHE_LEN], 0);
+    }
 
-            if (thResult.threshold().enableAlert()) {
-                String alertMessage = thResult.threshold().customAlertMessage();
-                if (alertMessage != null) {
-                    alertMessageBuilder.append(alertMessage).append("\n");
-                }
-            }
+    @Override
+    public Optional<NotifyReport> accept(ValidationResult validationResult) {
+
+        resultCache.resultsArr[resultCache.freeIndex] = validationResult;
+        resultCache.freeIndex = nextIndex(resultCache.freeIndex);
+
+        int currentDataInd = previousIndex(resultCache.freeIndex);
+        ValidationResult currentRes = resultCache.resultsArr[currentDataInd];
+        ValidationResult previousRes = resultCache.resultsArr[previousIndex(currentDataInd)];
+
+        if ((previousRes == null || !previousRes.hasViolation()) && currentRes.hasViolation()) {
+            // notify about the enter the threshold
+            return Optional.of(new NotifyReport(
+                currentRes.violatedValue(), currentRes.violatedAttribute(), ("Entry " + currentRes)
+            ));
+        } else if(!currentRes.hasViolation() && (previousRes != null && previousRes.hasViolation())) {
+            // notify about the exit the threshold
+            return Optional.of(new NotifyReport(
+                    currentRes.violatedValue(), currentRes.violatedAttribute(), ("Exit " + currentRes)
+            ));
         }
 
-        tempToNotify = null;
-
-        return new ValidationReport(
-                alertMessageBuilder.toString(),
-                attributeValidationResults.toArray(new ValidationResult[0])
-        );
+        // nothing to notify
+        return Optional.empty();
     }
-
 }

+ 1 - 1
src/main/java/cz/senslog/analytics/utils/newvalidator/Threshold.java → src/main/java/cz/senslog/analytics/utils/validator/Threshold.java

@@ -1,4 +1,4 @@
-package cz.senslog.analytics.utils.newvalidator;
+package cz.senslog.analytics.utils.validator;
 
 import cz.senslog.analytics.domain.AttributeType;
 import cz.senslog.analytics.domain.ComparisonOperator;

+ 30 - 78
src/main/java/cz/senslog/analytics/utils/validator/ThresholdChecker.java

@@ -1,108 +1,60 @@
 package cz.senslog.analytics.utils.validator;
 
-import cz.senslog.analytics.domain.*;
-import cz.senslog.analytics.utils.newvalidator.MessageFormatter;
-import cz.senslog.analytics.utils.validator.domain.ValidationReport;
-import cz.senslog.analytics.utils.validator.domain.ValidationResult;
-import cz.senslog.analytics.utils.validator.domain.ViolationReport;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
+import cz.senslog.analytics.domain.TimeSeriesDatasource;
 
 import java.time.OffsetDateTime;
-import java.util.*;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 import java.util.function.Consumer;
 
 import static java.util.Collections.emptyList;
 
 public class ThresholdChecker<DS extends TimeSeriesDatasource> {
 
-    private static final Logger logger = LogManager.getLogger(ThresholdChecker.class);
-
     public static <T extends TimeSeriesDatasource> ThresholdChecker<T> disabled() {
-        return ThresholdChecker.create(emptyList(), null, null, true);
+        return ThresholdChecker.create(emptyList(), null, r -> {}, true);
     }
 
-    private final Map<Long, ThresholdManager> datasourceThresholdManagers;
-
-    private final Validator<DS> validator;
-
-    private final Consumer<ViolationReport> ifViolated;
-
-    private final boolean defaultIfNotPresent;
-
-    public static <DS extends TimeSeriesDatasource> ThresholdChecker<DS> create(List<Threshold> thresholds,
-                                                           Validator<DS> validator,
-                                                           Consumer<ViolationReport> ifViolated, boolean defaultIfNotPresent)
-    {
-        return new ThresholdChecker<>(thresholds, validator, ifViolated, defaultIfNotPresent);
-    }
+    private final Map<Long, ThresholdManager<DS>> thresholdManagers;
+    private final Consumer<ViolationReport> notifier;
+    private final boolean defaultEnablingProcess;
 
-    public ThresholdChecker(List<Threshold> thresholds,
-                            Validator<DS> validator,
-                            Consumer<ViolationReport> ifViolated,
-                            boolean defaultIfNotPresent
+    public static <DS extends TimeSeriesDatasource> ThresholdChecker<DS> create(
+            final List<DataSource> dataSources,
+            final Validator<DS> validator,
+            final Consumer<ViolationReport> notifier,
+            final boolean defaultEnablingProcess
     ) {
-        this.validator = validator;
-        this.ifViolated = ifViolated;
-        this.defaultIfNotPresent = defaultIfNotPresent;
-        this.datasourceThresholdManagers = new HashMap<>();
-
-        Map<Long, Map<NotifyTrigger.Mode, NotifyTrigger>> dataSourceToNotifyTriggers = new HashMap<>();
-        for (Threshold th : thresholds) {
-            Set<NotifyTrigger> triggers = new HashSet<>(th.thresholdDimensions().size());
-            for (int i = 0; i < th.thresholdDimensions().size(); i++) {
-                ThresholdDimension thDim = th.thresholdDimensions().get(i);
-                triggers.add(dataSourceToNotifyTriggers
-                    .computeIfAbsent(th.datasourceId(), dataSource -> new HashMap<>(NotifyTrigger.Mode.values().length))
-                    .computeIfAbsent(thDim.notifyTriggerMode(), NotifyTrigger.Mode::createInstance));
-            }
-
-            this.datasourceThresholdManagers.computeIfAbsent(th.datasourceId(), ThresholdManager::new).register(th, triggers.toArray(new NotifyTrigger[0]));
+        final Map<Long, ThresholdManager<DS>> thresholdManagers = new HashMap<>(dataSources.size());
+        for (DataSource ds : dataSources) {
+            thresholdManagers.put(ds.id(), new ThresholdManager<>(ds, validator));
         }
+        return new ThresholdChecker<>(notifier, thresholdManagers, defaultEnablingProcess);
     }
 
-    /**
-     * The method checks the DS as datasource over all thresholds and returns boolean value of enable/disable further processing.
-     * If the validation result if 'false' but processing on failure is true, the return value is true.
-     * @param data DS to check
-     * @return boolean of enable/disable further processing
-     */
-    public boolean check(DS data) {
-        return validateThreshold(data);
+    private ThresholdChecker(Consumer<ViolationReport> notifier, Map<Long, ThresholdManager<DS>> thresholdManagers, boolean defaultEnablingProcess) {
+        this.defaultEnablingProcess = defaultEnablingProcess;
+        this.notifier = notifier;
+        this.thresholdManagers = thresholdManagers;
     }
 
-    private boolean validateThreshold(DS data) {
+    public boolean check(DS data) {
 
         long sourceId = data.datasourceId();
         OffsetDateTime timestamp = data.timestamp();
 
-        if (sourceId <= 0 || !datasourceThresholdManagers.containsKey(sourceId)) {
-            return defaultIfNotPresent;
+        ThresholdManager<DS> thresholdManager = thresholdManagers.get(sourceId);
+        if (sourceId <= 0 || thresholdManager == null) {
+            return defaultEnablingProcess;
         }
-        boolean process = true;
-
-        ThresholdManager thresholdManager = datasourceThresholdManagers.get(sourceId);
-        for (Threshold th : thresholdManager.thresholds()) {
-            ValidationResult[] res = validator.validate(data, th);
-            thresholdManager.accept(th, res);
 
-            if (!th.enableProcess()) {
-                // process on failure is set to false then calculate if the validation is true or false
-                boolean isNotValid = res[0].isNotValid();
-                for (int i = 1; i < res.length; i++) {
-                    isNotValid &= res[i].isNotValid();
-                }
-                if (isNotValid) {
-                    process = false;
-                }
-            }
-        }
+        ValidationReport validationReport = thresholdManager.accept(data);
 
-        if (thresholdManager.shouldNotify()) {
-            ValidationReport report = thresholdManager.resultsToNotify();
-            ifViolated.accept(new ViolationReport(sourceId, timestamp, report.message(),  report.validationResults()));
+        if (validationReport.message().isPresent()) {
+            notifier.accept(new ViolationReport(sourceId, timestamp, validationReport.message().get()));
         }
 
-        return process;
+        return validationReport.process();
     }
-}
+}

+ 79 - 59
src/main/java/cz/senslog/analytics/utils/validator/ThresholdManager.java

@@ -1,81 +1,101 @@
 package cz.senslog.analytics.utils.validator;
 
 import cz.senslog.analytics.domain.AttributeType;
-import cz.senslog.analytics.domain.Threshold;
-import cz.senslog.analytics.domain.ThresholdDimension;
-import cz.senslog.analytics.utils.validator.domain.ValidationReport;
-import cz.senslog.analytics.utils.validator.domain.ValidationResult;
+import cz.senslog.analytics.domain.TimeSeriesDatasource;
+import cz.senslog.analytics.domain.VariableType;
 
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
+import java.time.OffsetDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
 
-import static java.util.Collections.emptyMap;
+import static cz.senslog.analytics.domain.VariableType.*;
+import static java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME;
 
-class ThresholdManager {
+class ThresholdManager<DS extends TimeSeriesDatasource> {
 
-    private static final NotifyTrigger DISABLED_TRIGGER = NotifyTrigger.Mode.DISABLED.createInstance();
-
-    private final long datasourceId;
-
-    private final Map<Long, Threshold> thresholds;
+    private final DataSource datasource;
+    private final Validator<DS> validator;
     private final Map<Long, Map<AttributeType, NotifyTrigger>> notifyTriggers;
+    private final MessageFormatter messageFormatter;
 
-    ThresholdManager(long datasourceId) {
-        this.datasourceId = datasourceId;
-        this.thresholds = new HashMap<>();
-        this.notifyTriggers = new HashMap<>();
-    }
+    protected ThresholdManager(DataSource datasource, Validator<DS> validator) {
+        this.datasource = datasource;
+        this.validator = validator;
 
-    void register(Threshold threshold, NotifyTrigger... notifyTrigger) {
-        thresholds.put(threshold.id(), threshold);
-
-        for (ThresholdDimension dimension : threshold.thresholdDimensions()) {
-            for (NotifyTrigger trigger : notifyTrigger) {
-                if (dimension.notifyTriggerMode().equals(trigger.mode())) {
-                    notifyTriggers.computeIfAbsent(threshold.id(), id -> new HashMap<>())
-                            .put(dimension.attributeType(), trigger);
-                }
+        this.messageFormatter = MessageFormatter.createOf(Set.of(VariableType.values()), validator.attributes());
+        this.notifyTriggers = new HashMap<>(datasource.thresholds().size());
+        for (Threshold th : datasource.thresholds()) {
+            if (messageFormatter.isNotValid(th.messagePattern())) {
+                throw new IllegalArgumentException("The message for the threshold <"+th.id()+"> is invalid: " + th.messagePattern());
             }
-        }
-    }
-
-    Collection<Threshold> thresholds() {
-        return thresholds.values();
-    }
-
-    void accept(Threshold threshold, ValidationResult... res) {
-        if (threshold.datasourceId() == this.datasourceId) {
-            Map<AttributeType, NotifyTrigger> triggers = notifyTriggers.getOrDefault(threshold.id(), emptyMap());
-            for (ValidationResult vRes : res) {
-                triggers.getOrDefault(vRes.attributeType(), DISABLED_TRIGGER).accept(threshold, vRes);
+            for (Threshold.Dimension dim : th.dimensions()) {
+                notifyTriggers.computeIfAbsent(th.id(), thId -> new HashMap<>(dim.rules().size()))
+                        .computeIfAbsent(dim.attributeType(), attr -> dim.notifyTriggerMode().createInstance());
             }
         }
     }
 
-    boolean shouldNotify() {
-        boolean shouldNotify = true;
-        for (Map<AttributeType, NotifyTrigger> triggers : notifyTriggers.values()) { // over all thresholds
-            for (NotifyTrigger trigger : triggers.values()) {
-                shouldNotify &= trigger.shouldNotify();
-            }
+    public ValidationReport accept(final DS data) {
+        if (data == null || data.datasourceId() != this.datasource.id()) {
+            return null;
         }
-        return shouldNotify;
-    }
 
-    ValidationReport resultsToNotify() {
-        ValidationReport finalReport = ValidationReport.empty();
-        for (Map.Entry<Long, Map<AttributeType, NotifyTrigger>> entry : notifyTriggers.entrySet()) {
-            Threshold threshold = thresholds.get(entry.getKey());
-            ValidationReport thReport = ValidationReport.empty();
-            for (NotifyTrigger trigger : entry.getValue().values()) {
-                thReport = thReport.join(trigger.resultsToNotify());
-            }
+        OffsetDateTime timestamp = data.timestamp();
+        List<String> messagesToNotify = new ArrayList<>(datasource.thresholds().size());
+
+        boolean shouldProcess = false;
+        boolean shouldNotify = false;
+
+        for (Threshold threshold : datasource.thresholds()) {
+            ValidationResult[] validationResults = validator.validate(data, threshold);
+            Map<AttributeType, NotifyTrigger> triggers = notifyTriggers.get(threshold.id());
+            List<NotifyReport> thReports = new ArrayList<>(validationResults.length);
 
+            boolean thIsViolated = true;
+            boolean thShouldNotify = threshold.enableAlert(); // if alert is disabled then it is always false
 
-            finalReport.join(thReport);
+            for (ValidationResult result : validationResults) {
+                Optional<NotifyReport> dimNotifyReportOpt = triggers.get(result.violatedAttribute()).accept(result); // all trigger must have something to notify
+                dimNotifyReportOpt.ifPresent(thReports::add);
+                thShouldNotify = thShouldNotify && dimNotifyReportOpt.isPresent(); // isPresent() = true = violation should notify
+                thIsViolated = thIsViolated && result.hasViolation(); // all must be violated
+            }
+
+            // should process if at least one threshold is valid over all dimensions OR the process is enabled if violation
+            shouldProcess = shouldProcess || !thIsViolated || threshold.enableProcess();
+
+            // none or at least one has something to notify
+            shouldNotify = shouldNotify || thShouldNotify;
+
+            if (thShouldNotify) {
+                if (threshold.messagePattern() != null) {
+                    String patternMessage = threshold.messagePattern();
+                    Map<AttributeType, Double> messageArgs = new HashMap<>(thReports.size());
+                    for (NotifyReport thR : thReports) {
+                        messageArgs.put(thR.attributeType(), thR.value());
+                    }
+
+                    messagesToNotify.add(messageFormatter.format(patternMessage, messageArgs, Map.of(
+                            TIMESTAMP, (f -> timestamp.format(f.map(DateTimeFormatter::ofPattern).orElse(ISO_OFFSET_DATE_TIME))),
+                            MODULE, (f -> datasource.moduleName()),
+                            DATASOURCE, (f -> datasource.moduleName())
+                    )));
+
+                } else {
+                    StringBuilder thMessage = new StringBuilder();
+                    NotifyReport r0 = thReports.get(0);
+                    thMessage.append(r0.violatedRulesMessage());
+                    for (int i = 1; i < thReports.size(); i++) {
+                        thMessage.append("\n").append(thReports.get(i).violatedRulesMessage());
+                    }
+                    messagesToNotify.add(thMessage.toString());
+                }
+            }
         }
-        return finalReport;
+
+        return new ValidationReport(
+                shouldProcess,
+                shouldNotify ? Optional.of( String.join("\n\n", messagesToNotify)) : Optional.empty()
+        );
     }
 }

+ 4 - 0
src/main/java/cz/senslog/analytics/utils/validator/ThresholdViolationReport.java

@@ -0,0 +1,4 @@
+package cz.senslog.analytics.utils.validator;
+
+record ThresholdViolationReport() {
+}

+ 1 - 1
src/main/java/cz/senslog/analytics/utils/newvalidator/ValidationReport.java → src/main/java/cz/senslog/analytics/utils/validator/ValidationReport.java

@@ -1,4 +1,4 @@
-package cz.senslog.analytics.utils.newvalidator;
+package cz.senslog.analytics.utils.validator;
 
 import java.util.Optional;
 

+ 1 - 1
src/main/java/cz/senslog/analytics/utils/newvalidator/ValidationResult.java → src/main/java/cz/senslog/analytics/utils/validator/ValidationResult.java

@@ -1,4 +1,4 @@
-package cz.senslog.analytics.utils.newvalidator;
+package cz.senslog.analytics.utils.validator;
 
 import cz.senslog.analytics.domain.AttributeType;
 

+ 17 - 13
src/main/java/cz/senslog/analytics/utils/validator/Validator.java

@@ -1,8 +1,7 @@
 package cz.senslog.analytics.utils.validator;
 
-import cz.senslog.analytics.domain.*;
+import cz.senslog.analytics.domain.AttributeType;
 import cz.senslog.analytics.domain.ComparisonOperator;
-import cz.senslog.analytics.utils.validator.domain.ValidationResult;
 
 import java.util.*;
 import java.util.function.BiFunction;
@@ -10,6 +9,7 @@ import java.util.function.Function;
 import java.util.function.Supplier;
 
 import static cz.senslog.analytics.domain.ComparisonOperator.*;
+import static cz.senslog.analytics.domain.ComparisonOperator.NE;
 import static java.util.Collections.emptyList;
 
 public class Validator<T> {
@@ -31,7 +31,7 @@ public class Validator<T> {
         return functions.getOrDefault(mode, (val, ths) -> false).apply(value, threshold);
     }
 
-    public static boolean checkThresholdValue(ThresholdDimensionRule rule, Double value) {
+    public static boolean checkThresholdValue(Threshold.Dimension.Rule rule, Double value) {
         if (rule == null || value == null) return false;
         return checkThresholdValue(rule.comparisonOperator(), value, rule.value());
     }
@@ -41,8 +41,8 @@ public class Validator<T> {
     }
 
 
-    public static <T> AttributeMapping<T> create() {
-        return new AttributeMapping<>() {
+    public static <T> Validator.AttributeMapping<T> create() {
+        return new Validator.AttributeMapping<>() {
             private final Validator<T> validator = new Validator<>();
             @Override
             public Validator<T> addMapping(AttributeType property, Function<T, Supplier<Double>> getter) {
@@ -58,6 +58,10 @@ public class Validator<T> {
         this.map = new HashMap<>();
     }
 
+    public Set<AttributeType> attributes() {
+        return map.keySet();
+    }
+
     public Validator<T> addMapping(AttributeType property, Function<T, Supplier<Double>> getter) {
         map.put(property, getter);
         return this;
@@ -67,23 +71,23 @@ public class Validator<T> {
         return validate(object, threshold, map);
     }
 
-    private static <T> ValidationResult validate(T object, ThresholdDimension dimension, Map<AttributeType, Function<T, Supplier<Double>>> attributeMapping) {
+    private static <T> ValidationResult validate(T object, Threshold.Dimension dimension, Map<AttributeType, Function<T, Supplier<Double>>> attributeMapping) {
         final Double testingValue = attributeMapping.getOrDefault(dimension.attributeType(), (o) -> () -> null).apply(object).get();
-        final List<ValidationResult.Rule> violatedRules = new ArrayList<>(dimension.rules().size());
+        final List<Threshold.Dimension.Rule> violatedRules = new ArrayList<>(dimension.rules().size());
         boolean passed = true;
-        for (ThresholdDimensionRule rule : dimension.rules()) {
+        for (Threshold.Dimension.Rule rule : dimension.rules()) {
             passed &= checkThresholdValue(rule, testingValue);
-            violatedRules.add(new ValidationResult.Rule(rule.comparisonOperator(), rule.value()));
+            violatedRules.add(rule);
         }
-        return new ValidationResult(dimension.attributeType(), testingValue, passed ? violatedRules : emptyList());
+        return new ValidationResult(testingValue, dimension.attributeType(), passed ? violatedRules : emptyList());
     }
 
     private static <T> ValidationResult[] validate(T object, Threshold threshold, Map<AttributeType, Function<T, Supplier<Double>>> attributeMapping) {
-        final ValidationResult[] results = new ValidationResult[threshold.thresholdDimensions().size()];
+        final ValidationResult[] results = new ValidationResult[threshold.dimensions().size()];
         int resultIndex = 0;
-        for (ThresholdDimension dimension : threshold.thresholdDimensions()) {
+        for (Threshold.Dimension dimension : threshold.dimensions()) {
             results[resultIndex++] = validate(object, dimension, attributeMapping);
         }
         return results;
     }
-}
+}

+ 1 - 1
src/main/java/cz/senslog/analytics/utils/newvalidator/ViolationReport.java → src/main/java/cz/senslog/analytics/utils/validator/ViolationReport.java

@@ -1,4 +1,4 @@
-package cz.senslog.analytics.utils.newvalidator;
+package cz.senslog.analytics.utils.validator;
 
 import java.time.OffsetDateTime;
 

+ 0 - 14
src/main/java/cz/senslog/analytics/utils/validator/domain/ThresholdValidationResult.java

@@ -1,14 +0,0 @@
-package cz.senslog.analytics.utils.validator.domain;
-
-import cz.senslog.analytics.domain.Threshold;
-
-public record ThresholdValidationResult(Threshold threshold, ValidationResult[] validationResults) {
-
-    public boolean isViolated() {
-        boolean notValid = validationResults[0].isNotValid();
-        for (int i = 1; i < validationResults.length; i++) {
-            notValid = notValid && validationResults[i].isNotValid();
-        }
-        return notValid;
-    }
-}

+ 0 - 41
src/main/java/cz/senslog/analytics/utils/validator/domain/ValidationReport.java

@@ -1,41 +0,0 @@
-package cz.senslog.analytics.utils.validator.domain;
-
-import org.apache.logging.log4j.util.Strings;
-
-public record ValidationReport(String message, ValidationResult[] validationResults) {
-
-    public static ValidationReport empty() {
-        return new ValidationReport(null, new ValidationResult[0]);
-    }
-
-    public boolean isEmpty() {
-        return validationResults.length == 0 && Strings.isEmpty(message);
-    }
-
-    public ValidationReport join(ValidationReport validationReport) {
-        if (validationReport == null || validationReport.isEmpty()) {
-            return this;
-        }
-
-        if (this.isEmpty() && !validationReport.isEmpty()) {
-            return validationReport;
-        }
-
-        ValidationResult[] newResults = new ValidationResult[this.validationResults.length + validationReport.validationResults.length];
-        System.arraycopy(this.validationResults, 0, newResults, 0, this.validationResults.length);
-        System.arraycopy(validationReport.validationResults, 0, newResults, this.validationResults.length, validationReport.validationResults.length);
-
-        String newMessage;
-        if (this.message == null && validationReport.message == null) {
-            newMessage = null;
-        } else if (this.message == null) {
-            newMessage = validationReport.message;
-        } else if (validationReport.message == null) {
-            newMessage = this.message;
-        } else {
-            newMessage = this.message + validationReport.message;
-        }
-
-        return new ValidationReport(newMessage, newResults);
-    }
-}

+ 0 - 41
src/main/java/cz/senslog/analytics/utils/validator/domain/ValidationResult.java

@@ -1,41 +0,0 @@
-package cz.senslog.analytics.utils.validator.domain;
-
-import cz.senslog.analytics.domain.AttributeType;
-import cz.senslog.analytics.domain.ComparisonOperator;
-
-import java.util.List;
-
-public class ValidationResult {
-
-    public record Rule(ComparisonOperator operator, double thresholdValue) {}
-
-    private final double validatedValue;
-    private final AttributeType attributeType;
-    private final List<Rule> violatedRules;
-
-    public ValidationResult(AttributeType attributeType, double validatedValue, List<Rule> violatedRules) {
-        this.attributeType = attributeType;
-        this.validatedValue = validatedValue;
-        this.violatedRules = violatedRules;
-    }
-
-    public boolean isValid() {
-        return violatedRules.isEmpty();
-    }
-
-    public boolean isNotValid() {
-        return !isValid();
-    }
-
-    public List<Rule> records() {
-        return violatedRules;
-    }
-
-    public double validatedValue() {
-        return validatedValue;
-    }
-
-    public AttributeType attributeType() {
-        return attributeType;
-    }
-}

+ 0 - 6
src/main/java/cz/senslog/analytics/utils/validator/domain/ViolationReport.java

@@ -1,6 +0,0 @@
-package cz.senslog.analytics.utils.validator.domain;
-
-import java.time.OffsetDateTime;
-
-public record ViolationReport(long datasourceId, OffsetDateTime timestamp, String message, ValidationResult[] violatedData) {
-}

+ 0 - 43
src/test/java/cz/senslog/analytics/utils/AttributeRegexTest.java

@@ -1,43 +0,0 @@
-package cz.senslog.analytics.utils;
-
-import cz.senslog.analytics.domain.AttributeType;
-import org.junit.jupiter.api.Test;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-public class AttributeRegexTest {
-
-    @Test
-    public void regex() {
-        String attributes = Stream.of(AttributeType.values()).map(AttributeType::name).map(String::toLowerCase).collect(Collectors.joining("|"));
-        String variables = "timestamp|module|datasource";
-
-        final String attrRegex = "(?<attribute>\\$("+attributes+"))(\\((?<format>\\S*)\\))?";
-        final String varRegex = "(?<variable>\\$("+variables+"))(\\((?<format>\\S*)\\))?";
-
-
-        final String test1 = "Hodnota $val(%.2f) překročila práh $VAL >= 25 °C v čase $timestamp.";
-        final String test2 = "$val(%.2f) >= 25 & 12h <= $timestamp(HH:mm) <= 15h v čase $timestamp.";
-        final String test3 = "$timestamp | $val";
-
-        final String[] test = {test1, test2, test3};
-
-        Pattern attrPattern = Pattern.compile(attrRegex, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL );
-        Pattern varPattern = Pattern.compile(varRegex, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL );
-
-        for (String s : test) {
-            System.out.println(s);
-            Matcher m1 = attrPattern.matcher(s);
-            while (m1.find()) {
-                System.out.format("\tAttr: %s\tFormat: %s\n", m1.group("attribute"), m1.group("format"));
-            }
-            Matcher m2 = varPattern.matcher(s);
-            while (m2.find()) {
-                System.out.format("\tVar: %s\tFormat: %s\n", m2.group("variable"), m2.group("format"));
-            }
-        }
-    }
-}

+ 0 - 329
src/test/java/cz/senslog/analytics/utils/ThresholdCheckerTest.java

@@ -1,329 +0,0 @@
-package cz.senslog.analytics.utils;
-
-import cz.senslog.analytics.domain.*;
-import cz.senslog.analytics.domain.ComparisonOperator;
-import cz.senslog.analytics.utils.validator.NotifyTrigger;
-import cz.senslog.analytics.utils.validator.ThresholdChecker;
-import cz.senslog.analytics.utils.validator.Validator;
-import cz.senslog.analytics.utils.validator.domain.ValidationResult;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-
-import java.time.OffsetDateTime;
-import java.time.ZoneOffset;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.stream.Stream;
-
-import static cz.senslog.analytics.domain.AttributeType.TIME;
-import static cz.senslog.analytics.domain.AttributeType.VAL;
-import static cz.senslog.analytics.domain.ComparisonOperator.*;
-import static cz.senslog.analytics.utils.validator.NotifyTrigger.Mode.INSTANT;
-import static cz.senslog.analytics.utils.validator.NotifyTrigger.Mode.ON_CHANGE;
-import static org.junit.jupiter.api.Assertions.*;
-
-class ThresholdCheckerTest {
-
-    private static final Sensor SENSOR = new Sensor(100L, 1000L, 1001L);
-
-    private static Validator<Observation> validator;
-
-    @BeforeAll
-    static void setUp() {
-        validator = Validator.<Observation>create()
-                .addMapping(VAL, o -> o::value)
-                .addMapping(AttributeType.TIME, o -> () ->  (double) o.timestamp().toLocalTime().toSecondOfDay());
-    }
-
-    @Test
-    void test_1() {
-
-        final OffsetDateTime startInterval = OffsetDateTime.of(2024, 1, 1, 12, 0, 0, 0, ZoneOffset.ofHours(2));
-        final OffsetDateTime endInterval = OffsetDateTime.of(2024, 1, 1, 15, 0, 0, 0, ZoneOffset.ofHours(2));
-
-
-        ThresholdChecker<Observation> checker = ThresholdChecker.create(
-                List.of(
-                        Threshold.of(1, SENSOR.id(), true, false, true, null, List.of(
-                                ThresholdDimension.of(1, INSTANT, VAL, List.of(
-                                            ThresholdDimensionRule.of(GT, 25)
-                                        )
-                                ),
-                                ThresholdDimension.of(2, INSTANT, TIME, List.of(
-                                            ThresholdDimensionRule.of(GE, 43200),
-                                            ThresholdDimensionRule.of(LE, 54000)
-                                        )
-                                )
-                        ))
-                ),
-                validator,
-                (alert) -> {
-                    assertEquals(SENSOR.id(), alert.datasourceId());
-                    assertFalse(alert.timestamp().isBefore(startInterval) || alert.timestamp().isAfter(endInterval));
-                    assertEquals(2, alert.violatedData().length);
-                },
-                true
-        );
-
-        List<Observation> passedObservations = Stream.of(
-        /* pass | none  */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 25.3, OffsetDateTime.of(2024, 1, 1, 11, 59, 59, 0, ZoneOffset.ofHours(2)))),
-        /* fail | alert */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 25.3, OffsetDateTime.of(2024, 1, 1, 12, 0, 0, 0, ZoneOffset.ofHours(2)))),
-        /* fail | alert */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 25.3, OffsetDateTime.of(2024, 1, 1, 12, 0, 1, 0, ZoneOffset.ofHours(2)))),
-        /* fail | alert */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 25.3, OffsetDateTime.of(2024, 1, 1, 14, 59, 59, 0, ZoneOffset.ofHours(2)))),
-        /* fail | alert */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 25.3, OffsetDateTime.of(2024, 1, 1, 15, 0, 0, 0, ZoneOffset.ofHours(2)))),
-        /* pass | none  */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 25.3, OffsetDateTime.of(2024, 1, 1, 15, 0, 1, 0, ZoneOffset.ofHours(2))))
-        ).filter(checker::check).toList();
-
-        assertEquals(2, passedObservations.size());
-    }
-
-    @Test
-    void test_2() {
-
-        final OffsetDateTime startInterval = OffsetDateTime.of(2024, 1, 1, 12, 0, 0, 0, ZoneOffset.ofHours(2));
-        final OffsetDateTime endInterval = OffsetDateTime.of(2024, 1, 1, 15, 0, 0, 0, ZoneOffset.ofHours(2));
-
-
-        ThresholdChecker<Observation> checker = ThresholdChecker.create(
-                List.of(
-                        Threshold.of(1, SENSOR.id(), true, false, true, null, List.of(
-                                ThresholdDimension.of(1, ON_CHANGE, VAL, List.of(
-                                                ThresholdDimensionRule.of(GT, 25)
-                                        )
-                                ),
-                                ThresholdDimension.of(2, INSTANT, TIME, List.of(
-                                                ThresholdDimensionRule.of(GE, 43200),
-                                                ThresholdDimensionRule.of(LE, 54000)
-                                        )
-                                )
-                        )),
-                        Threshold.of(2, SENSOR.id(), true, false, true, null, List.of(
-                                ThresholdDimension.of(3, INSTANT, VAL, List.of(
-                                                ThresholdDimensionRule.of(GT, 25)
-                                        )
-                                ),
-                                ThresholdDimension.of(4, ON_CHANGE, TIME, List.of(
-                                                ThresholdDimensionRule.of(GE, 43200),
-                                                ThresholdDimensionRule.of(LE, 54000)
-                                        )
-                                )
-                        ))
-                ),
-                validator,
-                (alert) -> {
-                    assertEquals(SENSOR.id(), alert.datasourceId());
-                    assertTrue(alert.timestamp().isEqual(startInterval.plusSeconds(1)) || alert.timestamp().isEqual(endInterval));
-                    assertEquals(2, alert.violatedData().length);
-                },
-                true
-        );
-
-        List<Observation> passedObservations = Stream.of(
-        /* pass | none  */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 24.3, OffsetDateTime.of(2024, 1, 1, 11, 59, 59, 0, ZoneOffset.ofHours(2)))),
-        /* pass | none  */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 24.3, OffsetDateTime.of(2024, 1, 1, 12, 0, 0, 0, ZoneOffset.ofHours(2)))),
-        /* fail | alert */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 25.3, OffsetDateTime.of(2024, 1, 1, 12, 0, 1, 0, ZoneOffset.ofHours(2)))),
-        /* fail | none  */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 25.3, OffsetDateTime.of(2024, 1, 1, 14, 59, 59, 0, ZoneOffset.ofHours(2)))),
-        /* pass | alert */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 24.3, OffsetDateTime.of(2024, 1, 1, 15, 0, 0, 0, ZoneOffset.ofHours(2)))),
-        /* pass | none  */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 25.3, OffsetDateTime.of(2024, 1, 1, 15, 0, 1, 0, ZoneOffset.ofHours(2))))
-        ).filter(checker::check).toList();
-
-        assertEquals(4, passedObservations.size());
-    }
-
-    @Test
-    void test_3() {
-
-        final OffsetDateTime startInterval = OffsetDateTime.of(2024, 1, 1, 12, 0, 0, 0, ZoneOffset.ofHours(2));
-        final OffsetDateTime endInterval = OffsetDateTime.of(2024, 1, 1, 15, 0, 0, 0, ZoneOffset.ofHours(2));
-
-
-        ThresholdChecker<Observation> checker = ThresholdChecker.create(
-                List.of(
-                        Threshold.of(1, SENSOR.id(), true, false, true, null, List.of(
-                                ThresholdDimension.of(1, ON_CHANGE, VAL, List.of(
-                                                ThresholdDimensionRule.of(GT, 25)
-                                        )
-                                ),
-                                ThresholdDimension.of(2, INSTANT, TIME, List.of(
-                                                ThresholdDimensionRule.of(GE, 43200),
-                                                ThresholdDimensionRule.of(LE, 54000)
-                                        )
-                                )
-                        )),
-                        Threshold.of(2, SENSOR.id(), true, false, true, null, List.of(
-                                ThresholdDimension.of(3, INSTANT, VAL, List.of(
-                                                ThresholdDimensionRule.of(GT, 25)
-                                        )
-                                ),
-                                ThresholdDimension.of(4, ON_CHANGE, TIME, List.of(
-                                                ThresholdDimensionRule.of(GE, 43200),
-                                                ThresholdDimensionRule.of(LE, 54000)
-                                        )
-                                )
-                        ))
-                ),
-                validator,
-                (alert) -> {
-                    assertEquals(SENSOR.id(), alert.datasourceId());
-                    assertTrue(alert.timestamp().isEqual(startInterval) || alert.timestamp().isEqual(endInterval));
-                    assertEquals(2, alert.violatedData().length);
-                },
-                true
-        );
-
-        List<Observation> passedObservations = Stream.of(
-                /* pass | none  */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 25.3, OffsetDateTime.of(2024, 1, 1, 11, 59, 59, 0, ZoneOffset.ofHours(2)))),
-                /* fail | alert */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 25.3, OffsetDateTime.of(2024, 1, 1, 12, 0, 0, 0, ZoneOffset.ofHours(2)))),
-                /* fail | none  */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 25.3, OffsetDateTime.of(2024, 1, 1, 12, 0, 1, 0, ZoneOffset.ofHours(2)))),
-                /* fail | none  */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 25.3, OffsetDateTime.of(2024, 1, 1, 14, 59, 59, 0, ZoneOffset.ofHours(2)))),
-                /* pass | alert */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 24.3, OffsetDateTime.of(2024, 1, 1, 15, 0, 0, 0, ZoneOffset.ofHours(2)))),
-                /* pass | none  */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 24.3, OffsetDateTime.of(2024, 1, 1, 15, 0, 1, 0, ZoneOffset.ofHours(2))))
-        ).filter(checker::check).toList();
-
-        assertEquals(3, passedObservations.size());
-    }
-
-    @Test
-    void check_size_1_INSTANT() {
-
-        ThresholdChecker<Observation> checker = new ThresholdChecker<>(
-                List.of(
-                    new Threshold(1, SENSOR.id(), true, false, true, null, List.of(
-                            ThresholdDimension.of(1, INSTANT, VAL, List.of(ThresholdDimensionRule.of(GT, 10.0)))
-                    ))
-                ),
-                Validator.<Observation>create()
-                        .addMapping(VAL, o -> o::value),
-                (report) -> {
-                    assertEquals(SENSOR.id(), report.datasourceId());
-                    assertEquals(1, report.violatedData().length);
-                    ValidationResult valResult = report.violatedData()[0];
-                    assertEquals(VAL, valResult.attributeType());
-                    assertEquals(23.3, valResult.validatedValue());
-                    assertEquals(1, valResult.records().size());
-                },
-                true
-        );
-
-        long count = Stream.of(
-                Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 23.3, OffsetDateTime.now()))
-        ).filter(checker::check).count();
-        assertEquals(1, count);
-    }
-
-    @Test
-    void check_size_3_ON_CHANGE() {
-
-        ThresholdChecker<Observation> checker = new ThresholdChecker<>(
-                List.of(
-                        new Threshold(1, SENSOR.id(), true,false, true, null, List.of(
-                                ThresholdDimension.of(1, NotifyTrigger.Mode.ON_CHANGE, VAL, List.of(ThresholdDimensionRule.of(GT, 10.0)))
-                        ))
-                ),
-                Validator.<Observation>create()
-                        .addMapping(VAL, o -> o::value),
-                (report) -> {
-                    assertEquals(SENSOR.id(), report.datasourceId());
-                    assertEquals(1, report.violatedData().length);
-                    ValidationResult valResult = report.violatedData()[0];
-                    assertEquals(VAL, valResult.attributeType());
-                    assertEquals(25.3, valResult.validatedValue());
-                    assertEquals(1, valResult.records().size());
-                },
-                true
-        );
-
-        long count = Stream.of(
-                Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(),9.9, OffsetDateTime.now())),
-                Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(),25.3, OffsetDateTime.now())),
-                Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(),30.5, OffsetDateTime.now()))
-        ).filter(checker::check).count();
-        assertEquals(3, count);
-    }
-
-    @Test
-    void check_size_4_ON_CHANGE() {
-        AtomicInteger reportStep = new AtomicInteger(1);
-        ThresholdChecker<Observation> checker = new ThresholdChecker<>(
-                List.of(
-                        new Threshold(1, SENSOR.id(), true,false, true, null, List.of(
-                                ThresholdDimension.of(1, NotifyTrigger.Mode.ON_CHANGE, VAL, List.of(ThresholdDimensionRule.of(GT, 10.0)))
-                        ))
-                ),
-                Validator.<Observation>create()
-                        .addMapping(VAL, o -> o::value),
-                (report) -> {
-                    assertEquals(SENSOR.id(), report.datasourceId());
-                    assertEquals(1, report.violatedData().length);
-                    ValidationResult valResult = report.violatedData()[0];
-                    assertEquals(VAL, valResult.attributeType());
-
-                    if (reportStep.compareAndSet(1, 2)) {
-                        assertEquals(25.3, valResult.validatedValue());
-                        assertTrue(valResult.isNotValid());
-                        assertEquals(1, valResult.records().size());
-
-                    } else if (reportStep.compareAndSet(2, 3)) {
-                        assertEquals(8.5, valResult.validatedValue());
-                        assertTrue(valResult.isValid());
-                        assertEquals(0, valResult.records().size());
-                    }
-                },
-                true
-        );
-
-        long count = Stream.of(
-                Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 9.9, OffsetDateTime.now())),
-                Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 25.3, OffsetDateTime.now())),
-                Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(),30.5, OffsetDateTime.now())),
-                Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 8.5, OffsetDateTime.now()))
-        ).filter(checker::check).count();
-        assertEquals(4, count);
-    }
-    
-    @Test
-    void check_doubleStatistics_INSTANT() {
-        long datasourceId = SENSOR.id();
-
-        ThresholdChecker<DoubleStatistics> checker = new ThresholdChecker<>(
-                List.of(
-                        new Threshold(1, -1, true, false, true, null, List.of(
-                                ThresholdDimension.of(1, INSTANT, AttributeType.MIN, List.of(ThresholdDimensionRule.of( ComparisonOperator.LT, 0.0))))
-                        ),
-                        new Threshold(1, -1, true, false, true, null, List.of(
-                                ThresholdDimension.of(2, INSTANT, AttributeType.MAX, List.of(ThresholdDimensionRule.of(GT, 5.0))))
-                        )
-                ),
-                Validator.<DoubleStatistics>create()
-                        .addMapping(AttributeType.MIN, s -> s::min)
-                        .addMapping(AttributeType.MAX, s -> s::max)
-                        .addMapping(AttributeType.AVG, s -> s::average),
-                (report) -> {
-                    assertEquals(datasourceId, report.datasourceId());
-                    assertEquals(2, report.violatedData().length);
-                    ValidationResult[] violatedData = report.violatedData();
-                    ValidationResult minResult = violatedData[0].attributeType().equals(AttributeType.MIN) ? violatedData[0] : violatedData[1];
-                    ValidationResult maxResult = violatedData[0].attributeType().equals(AttributeType.MAX) ? violatedData[0] : violatedData[1];
-
-                    assertEquals(AttributeType.MIN, minResult.attributeType());
-                    assertEquals(-1.5, minResult.validatedValue());
-                    assertEquals(1, minResult.records().size());
-
-                    assertEquals(AttributeType.MAX, maxResult.attributeType());
-                    assertEquals(5.2, maxResult.validatedValue());
-                    assertEquals(1, maxResult.records().size());
-                },
-                true
-        );
-
-        DoubleStatistics ds = DoubleStatistics.init(datasourceId, OffsetDateTime.now());
-        ds.accept(datasourceId, -1.5);
-        ds.accept(datasourceId, 3.3);
-        ds.accept(datasourceId, 5.2);
-
-
-        long count = Stream.of(ds)
-                .filter(checker::check).count();
-        assertEquals(1, count);
-    }
-}

+ 18 - 15
src/test/java/cz/senslog/analytics/utils/validator/MessageFormatterTest.java

@@ -2,16 +2,15 @@ package cz.senslog.analytics.utils.validator;
 
 import cz.senslog.analytics.domain.AttributeType;
 import cz.senslog.analytics.domain.ThresholdDimension;
-import cz.senslog.analytics.domain.ThresholdViolationAlert;
-import cz.senslog.analytics.utils.newvalidator.MessageFormatter;
-import cz.senslog.analytics.utils.validator.domain.ValidationResult;
+import cz.senslog.analytics.domain.VariableType;
 import org.junit.jupiter.api.Test;
 
 import java.time.OffsetDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.List;
+import java.util.Map;
 
-import static java.util.Collections.emptyList;
-import static org.junit.jupiter.api.Assertions.*;
+import static java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME;
 
 class MessageFormatterTest {
 
@@ -27,15 +26,19 @@ class MessageFormatterTest {
                 ThresholdDimension.of(2, null, AttributeType.TIME, null)
         );
 
-//        MessageFormatter formatter = MessageFormatter.create();
-//        for (String test : new String[]{test1, test2, test3}) {
-//            assertTrue(formatter.isValid(test, thresholdDimensions));
-//
-//            String message = formatter.format(ThresholdViolationAlert.of("moduleName", 1, "sourceName", test, new ValidationResult[]{
-//                    new ValidationResult(AttributeType.VAL, 25.3432, emptyList()), new ValidationResult(AttributeType.TIME, 48800, emptyList())
-//            }, OffsetDateTime.now()));
-//
-//            System.out.println(message);
-//        }
+        MessageFormatter formatter = MessageFormatter.create();
+        for (String test : new String[]{test1, test2, test3}) {
+
+            String message = formatter.format(test,
+                    Map.of(
+                            AttributeType.TIME, 48800.0,
+                            AttributeType.VAL, 25.3432
+                    ),
+                    Map.of(
+                            VariableType.TIMESTAMP, f -> OffsetDateTime.now().format(f.map(DateTimeFormatter::ofPattern).orElse(ISO_OFFSET_DATE_TIME)))
+            );
+
+            System.out.println(message);
+        }
     }
 }

+ 0 - 75
src/test/java/cz/senslog/analytics/utils/validator/OnChangeNotifyTriggerTest.java

@@ -1,75 +0,0 @@
-package cz.senslog.analytics.utils.validator;
-
-import cz.senslog.analytics.domain.*;
-import cz.senslog.analytics.domain.ComparisonOperator;
-import org.junit.jupiter.api.Test;
-
-import java.util.List;
-
-class OnChangeNotifyTriggerTest {
-
-    @Test
-    void value_true_time_true() {
-        Threshold th = new Threshold(1, 1, true, true, true,null, List.of(
-                ThresholdDimension.of(1, NotifyTrigger.Mode.ON_CHANGE, AttributeType.VAL, List.of(
-                        ThresholdDimensionRule.of(ComparisonOperator.GE, 25)
-                )),
-                ThresholdDimension.of(2, NotifyTrigger.Mode.INSTANT, AttributeType.TIME, List.of(
-                        ThresholdDimensionRule.of(ComparisonOperator.GE, 43200),
-                        ThresholdDimensionRule.of(ComparisonOperator.LE, 54000)
-                ))
-        ));
-
-/*
-        NotifyTrigger notifyTrigger = th.notifyTriggerMode().createInstance();
-
-        ValidationResult vr1 = new ValidationResult(AttributeType.VAL, 25.1);
-        vr1.addViolatedRule(ComparisonOperator.GE, 25);
-
-        ValidationResult vr2 = new ValidationResult(AttributeType.TIME, 43200);
-        vr2.addViolatedRule(ComparisonOperator.GE, 43200);
-        vr2.addViolatedRule(ComparisonOperator.LE, 54000);
-
-        notifyTrigger.accept(th, vr1, vr2);
-
-        assertTrue(notifyTrigger.shouldNotify());
-
-        ValidationReport validationReport = notifyTrigger.resultsToNotify();
-        assertFalse(validationReport.isEmpty());
-        assertTrue(Strings.isBlank(validationReport.message()));
-
-        assertEquals(validationReport.validationResults().length, 2);
-
- */
-    }
-
-    @Test
-    void value_false_time_true() {
-        Threshold th = new Threshold(1, 1, true, true, true, null, List.of(
-                ThresholdDimension.of(1, NotifyTrigger.Mode.ON_CHANGE, AttributeType.VAL, List.of(
-                        ThresholdDimensionRule.of(ComparisonOperator.GE, 25)
-                )),
-                ThresholdDimension.of(2, NotifyTrigger.Mode.INSTANT, AttributeType.TIME, List.of(
-                        ThresholdDimensionRule.of(ComparisonOperator.GE, 43200),
-                        ThresholdDimensionRule.of(ComparisonOperator.LE, 54000)
-                ))
-        ));
-
-
-        /*
-        NotifyTrigger notifyTrigger = th.notifyTriggerMode().createInstance();
-
-        ValidationResult vr1 = new ValidationResult(AttributeType.VAL, 24.9);
-
-        ValidationResult vr2 = new ValidationResult(AttributeType.TIME, 43200);
-        vr2.addViolatedRule(ComparisonOperator.GE, 43200);
-        vr2.addViolatedRule(ComparisonOperator.LE, 54000);
-
-        notifyTrigger.accept(th, vr1, vr2);
-
-        assertFalse(notifyTrigger.shouldNotify());
-        assertTrue(notifyTrigger.resultsToNotify().isEmpty());
-
-         */
-    }
-}

+ 28 - 30
src/test/java/cz/senslog/analytics/utils/newvalidator/ThresholdCheckerTest.java → src/test/java/cz/senslog/analytics/utils/validator/ThresholdCheckerTest.java

@@ -1,4 +1,4 @@
-package cz.senslog.analytics.utils.newvalidator;
+package cz.senslog.analytics.utils.validator;
 
 import cz.senslog.analytics.domain.*;
 import org.junit.jupiter.api.BeforeAll;
@@ -12,13 +12,14 @@ import java.util.stream.Stream;
 import static cz.senslog.analytics.domain.AttributeType.TIME;
 import static cz.senslog.analytics.domain.AttributeType.VAL;
 import static cz.senslog.analytics.domain.ComparisonOperator.*;
-import static cz.senslog.analytics.utils.newvalidator.NotifyTrigger.Mode.INSTANT;
-import static cz.senslog.analytics.utils.newvalidator.NotifyTrigger.Mode.ON_CHANGE;
+import static cz.senslog.analytics.utils.validator.NotifyTrigger.Mode.INSTANT;
+import static cz.senslog.analytics.utils.validator.NotifyTrigger.Mode.ON_CHANGE;
 import static org.junit.jupiter.api.Assertions.*;
 
 class ThresholdCheckerTest {
 
-    private static final Sensor SENSOR = new Sensor(100L, 1000L, 1001L);
+    private static final Sensor SENSOR = new Sensor(100L, 1000L, 1001L, "unit(1000)/sensor(1001)");
+    private static final String MODULE_NAME = "JUNIT";
 
     private static Validator<Observation> validator;
 
@@ -37,20 +38,18 @@ class ThresholdCheckerTest {
 
 
         ThresholdChecker<Observation> checker = ThresholdChecker.create(
-                "testModule",
-                List.of(
+                List.of(new DataSource(SENSOR.id(), SENSOR.name(), MODULE_NAME, List.of(
                         new Threshold(1, SENSOR.id(), false, true, "$val", List.of(
-                                new Threshold.Dimension(VAL, INSTANT, List.of(
-                                                new Threshold.Dimension.Rule(GT, 25)
-                                        )
-                                ),
-                                new Threshold.Dimension(TIME, INSTANT, List.of(
-                                            new Threshold.Dimension.Rule(GE, 43200),
-                                            new Threshold.Dimension.Rule(LE, 54000)
-                                        )
+                            new Threshold.Dimension(VAL, INSTANT, List.of(
+                                        new Threshold.Dimension.Rule(GT, 25)
                                 )
-                        ))
-                ),
+                        ),
+                        new Threshold.Dimension(TIME, INSTANT, List.of(
+                                    new Threshold.Dimension.Rule(GE, 43200),
+                                    new Threshold.Dimension.Rule(LE, 54000)
+                                )
+                        )))
+                ))),
                 validator,
                 (alert) -> {
                     assertEquals(25.3, Double.parseDouble(alert.message()));
@@ -78,11 +77,11 @@ class ThresholdCheckerTest {
         final OffsetDateTime startInterval = OffsetDateTime.of(2024, 1, 1, 12, 0, 0, 0, ZoneOffset.ofHours(2));
         final OffsetDateTime endInterval = OffsetDateTime.of(2024, 1, 1, 15, 0, 0, 0, ZoneOffset.ofHours(2));
 
+        final String messagePattern = "$val(%.2f) >= 25 & 12h <= $timestamp(HH:mm) <= 15h v čase $timestamp.";
 
         ThresholdChecker<Observation> checker = ThresholdChecker.create(
-                "testModule",
-                List.of(
-                        new Threshold(1, SENSOR.id(), false, true, "$val(%.2f) >= 25 & 12h <= $timestamp(HH:mm) <= 15h v čase $timestamp.", List.of(
+                List.of(new DataSource(SENSOR.id(), SENSOR.name(), MODULE_NAME, List.of(
+                        new Threshold(1, SENSOR.id(), false, true, messagePattern, List.of(
                                 new Threshold.Dimension(VAL, ON_CHANGE, List.of(
                                         new Threshold.Dimension.Rule(GT, 25)
                                 )
@@ -90,7 +89,7 @@ class ThresholdCheckerTest {
                                 new Threshold.Dimension(TIME, INSTANT, List.of(
                                         new Threshold.Dimension.Rule(GE, 43200), // 12h
                                         new Threshold.Dimension.Rule(LE, 54000) // 15h
-                                )
+                                    )
                                 )
                         )),
                         new Threshold(2, SENSOR.id(), false, true, null, List.of(
@@ -104,7 +103,7 @@ class ThresholdCheckerTest {
                                     )
                                 )
                         ))
-                ),
+                ))),
                 validator,
                 (alert) -> {
                     System.out.println(alert.message()+"\n");
@@ -135,9 +134,8 @@ class ThresholdCheckerTest {
 
 
         ThresholdChecker<Observation> checker = ThresholdChecker.create(
-                "testModule",
-                List.of(
-                        new Threshold(1, SENSOR.id(), false, true, null, List.of(
+                List.of(new DataSource(SENSOR.id(), SENSOR.name(), MODULE_NAME, List.of(
+                        new Threshold(1, SENSOR.id(), false, true, "$val", List.of(
                                 new Threshold.Dimension(VAL, ON_CHANGE, List.of(
                                         new Threshold.Dimension.Rule(GT, 25)
                                     )
@@ -159,10 +157,10 @@ class ThresholdCheckerTest {
                                     )
                                 )
                         ))
-                ),
+                ))),
                 validator,
                 (alert) -> {
-                    System.out.println(alert.message()+"\n");
+                    System.out.println(alert.message());
                     assertEquals(SENSOR.id(), alert.dataSourceId());
                     assertTrue(alert.timestamp().isEqual(startInterval) || alert.timestamp().isEqual(endInterval));
                 },
@@ -170,12 +168,12 @@ class ThresholdCheckerTest {
         );
 
         List<Observation> passedObservations = Stream.of(
-                /* pass | none  */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 25.3, OffsetDateTime.of(2024, 1, 1, 11, 59, 59, 0, ZoneOffset.ofHours(2)))),
+                /* pass | none  */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 25.1, OffsetDateTime.of(2024, 1, 1, 11, 59, 59, 0, ZoneOffset.ofHours(2)))),
                 /* fail | alert */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 25.3, OffsetDateTime.of(2024, 1, 1, 12, 0, 0, 0, ZoneOffset.ofHours(2)))),
-                /* fail | none  */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 25.3, OffsetDateTime.of(2024, 1, 1, 12, 0, 1, 0, ZoneOffset.ofHours(2)))),
-                /* fail | none  */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 25.3, OffsetDateTime.of(2024, 1, 1, 14, 59, 59, 0, ZoneOffset.ofHours(2)))),
+                /* fail | none  */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 25.1, OffsetDateTime.of(2024, 1, 1, 12, 0, 1, 0, ZoneOffset.ofHours(2)))),
+                /* fail | none  */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 25.1, OffsetDateTime.of(2024, 1, 1, 14, 59, 59, 0, ZoneOffset.ofHours(2)))),
                 /* pass | alert */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 24.3, OffsetDateTime.of(2024, 1, 1, 15, 0, 0, 0, ZoneOffset.ofHours(2)))),
-                /* pass | none  */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 24.3, OffsetDateTime.of(2024, 1, 1, 15, 0, 1, 0, ZoneOffset.ofHours(2))))
+                /* pass | none  */ Observation.of(SENSOR, RawObservation.of(SENSOR.unitId(), SENSOR.sensorId(), 24.1, OffsetDateTime.of(2024, 1, 1, 15, 0, 1, 0, ZoneOffset.ofHours(2))))
         ).filter(checker::check).toList();
 
         assertEquals(3, passedObservations.size());