diff --git a/src/main/java/de/ph87/data/series/AggregationWrapperDto.java b/src/main/java/de/ph87/data/series/AggregationWrapperDto.java new file mode 100644 index 0000000..004f5bf --- /dev/null +++ b/src/main/java/de/ph87/data/series/AggregationWrapperDto.java @@ -0,0 +1,23 @@ +package de.ph87.data.series; + +import lombok.*; + +import java.time.*; +import java.util.*; + +@Data +public class AggregationWrapperDto { + + public final Alignment alignment; + + public final ZonedDateTime date; + + public final List aggregations; + + public AggregationWrapperDto(final Alignment alignment, final ZonedDateTime date, final List aggregations) { + this.alignment = alignment; + this.date = date; + this.aggregations = aggregations; + } + +} diff --git a/src/main/java/de/ph87/data/series/Aligned.java b/src/main/java/de/ph87/data/series/Aligned.java index ab718d7..f66d0aa 100644 --- a/src/main/java/de/ph87/data/series/Aligned.java +++ b/src/main/java/de/ph87/data/series/Aligned.java @@ -19,8 +19,13 @@ public class Aligned { } @NonNull - public Aligned minus(final long offset) { - return new Aligned(alignment, alignment.plus.apply(date, -offset)); + public Aligned plus(final long amount) { + return new Aligned(alignment, alignment.plus(date, amount)); + } + + @NonNull + public Aligned minus(final long amount) { + return plus(-amount); } } diff --git a/src/main/java/de/ph87/data/series/Alignment.java b/src/main/java/de/ph87/data/series/Alignment.java index db5b288..047c8b5 100644 --- a/src/main/java/de/ph87/data/series/Alignment.java +++ b/src/main/java/de/ph87/data/series/Alignment.java @@ -7,27 +7,27 @@ import java.time.temporal.*; import java.util.function.*; public enum Alignment { - FIVE(Duration.ofMinutes(5), t -> t.truncatedTo(ChronoUnit.MINUTES).minusMinutes(t.getMinute() % 5), (t, a) -> t.plusMinutes(5 * a)), - HOUR(Duration.ofHours(1), t -> t.truncatedTo(ChronoUnit.HOURS), ZonedDateTime::plusHours), - DAY(Duration.ofDays(1), t -> t.truncatedTo(ChronoUnit.DAYS), ZonedDateTime::plusDays), - WEEK(Duration.ofDays(7), t -> t.truncatedTo(ChronoUnit.DAYS).minusDays(t.getDayOfWeek().getValue() - 1), ZonedDateTime::plusWeeks), - MONTH(Duration.ofDays(31), t -> t.truncatedTo(ChronoUnit.DAYS).minusDays(t.getDayOfMonth() - 1), ZonedDateTime::plusMonths), - YEAR(Duration.ofDays(366), t -> t.truncatedTo(ChronoUnit.DAYS).minusDays(t.getDayOfYear() - 1), ZonedDateTime::plusYears), + FIVE(t -> t.truncatedTo(ChronoUnit.MINUTES).minusMinutes(t.getMinute() % 5), Duration.ofMinutes(5), Duration.ofMinutes(5)), + HOUR(t -> t.truncatedTo(ChronoUnit.HOURS), Duration.ofHours(1), Duration.ofHours(1)), + DAY(t -> t.truncatedTo(ChronoUnit.DAYS), Duration.ofDays(1), Duration.ofDays(1)), + WEEK(t -> t.truncatedTo(ChronoUnit.DAYS).minusDays(t.getDayOfWeek().getValue() - 1), Period.ofWeeks(1), Duration.ofDays(7)), + MONTH(t -> t.truncatedTo(ChronoUnit.DAYS).minusDays(t.getDayOfMonth() - 1), Period.ofMonths(1), Duration.ofDays(31)), + YEAR(t -> t.truncatedTo(ChronoUnit.DAYS).minusDays(t.getDayOfYear() - 1), Period.ofYears(1), Duration.ofDays(366)), ; - @NonNull - public final Duration maxDuration; - @NonNull public final Function align; @NonNull - public final BiFunction plus; + public final TemporalAmount amount; - Alignment(@NonNull final Duration maxDuration, @NonNull final Function<@NonNull ZonedDateTime, @NonNull ZonedDateTime> align, @NonNull final BiFunction<@NonNull ZonedDateTime, @NonNull Long, @NonNull ZonedDateTime> plus) { + @NonNull + public final Duration maxDuration; + + Alignment(@NonNull final Function<@NonNull ZonedDateTime, @NonNull ZonedDateTime> align, @NonNull final TemporalAmount amount, @NonNull final Duration maxDuration) { this.maxDuration = maxDuration; this.align = align; - this.plus = plus; + this.amount = amount; } @NonNull @@ -35,4 +35,25 @@ public enum Alignment { return new Aligned(this, date); } + @NonNull + public ZonedDateTime plus(@NonNull final ZonedDateTime date, final long amount) { + return date.plus(multiplyAmount(amount)); + } + + @NonNull + @SuppressWarnings("unused") + public ZonedDateTime minus(@NonNull final ZonedDateTime date, final long amount) { + return plus(date, -amount); + } + + @NonNull + public TemporalAmount multiplyAmount(final long amount) { + if (this.amount instanceof final Duration duration) { + return duration.multipliedBy(amount); + } else if (this.amount instanceof final Period period) { + return period.multipliedBy((int) amount); + } + throw new RuntimeException(); + } + } diff --git a/src/main/java/de/ph87/data/series/IAggregationDto.java b/src/main/java/de/ph87/data/series/IAggregationDto.java new file mode 100644 index 0000000..cca3c1b --- /dev/null +++ b/src/main/java/de/ph87/data/series/IAggregationDto.java @@ -0,0 +1,10 @@ +package de.ph87.data.series; + +import lombok.*; + +public interface IAggregationDto { + + @NonNull + SeriesDto getSeries(); + +} diff --git a/src/main/java/de/ph87/data/series/SeriesController.java b/src/main/java/de/ph87/data/series/SeriesController.java new file mode 100644 index 0000000..1f82319 --- /dev/null +++ b/src/main/java/de/ph87/data/series/SeriesController.java @@ -0,0 +1,31 @@ +package de.ph87.data.series; + +import de.ph87.data.series.meter.*; +import de.ph87.data.series.varying.*; +import lombok.*; +import org.springframework.web.bind.annotation.*; + +import java.time.*; +import java.util.*; + +@RestController +@RequiredArgsConstructor +@RequestMapping("Series") +public class SeriesController { + + private final MeterService meterService; + + private final VaryingService varyingService; + + @NonNull + @GetMapping("agg/all/{alignmentName}/{offsetAmount}") + public AggregationWrapperDto agg(@PathVariable final String alignmentName, @PathVariable final long offsetAmount) { + final Alignment alignment = Alignment.valueOf(alignmentName); + final ZonedDateTime date = alignment.align(ZonedDateTime.now()).minus(offsetAmount).date; + final List aggregations = new ArrayList<>(); + aggregations.addAll(meterService.findAllAggregations(alignment, date)); + aggregations.addAll(varyingService.findAllAggregations(alignment, date)); + return new AggregationWrapperDto(alignment, date, aggregations); + } + +} diff --git a/src/main/java/de/ph87/data/series/SeriesService.java b/src/main/java/de/ph87/data/series/SeriesService.java index 5849846..51a046a 100644 --- a/src/main/java/de/ph87/data/series/SeriesService.java +++ b/src/main/java/de/ph87/data/series/SeriesService.java @@ -48,7 +48,7 @@ public class SeriesService { } @NonNull - private SeriesDto toDto(@NonNull final Series series) { + public SeriesDto toDto(@NonNull final Series series) { return new SeriesDto(series); } diff --git a/src/main/java/de/ph87/data/series/meter/MeterAggregation.java b/src/main/java/de/ph87/data/series/meter/MeterAggregation.java new file mode 100644 index 0000000..a90f3de --- /dev/null +++ b/src/main/java/de/ph87/data/series/meter/MeterAggregation.java @@ -0,0 +1,14 @@ +package de.ph87.data.series.meter; + +import de.ph87.data.series.*; +import lombok.*; + +@Data +public class MeterAggregation { + + @NonNull + public final Series series; + + public final double delta; + +} diff --git a/src/main/java/de/ph87/data/series/meter/MeterAggregationDto.java b/src/main/java/de/ph87/data/series/meter/MeterAggregationDto.java new file mode 100644 index 0000000..a8876af --- /dev/null +++ b/src/main/java/de/ph87/data/series/meter/MeterAggregationDto.java @@ -0,0 +1,19 @@ +package de.ph87.data.series.meter; + +import de.ph87.data.series.*; +import lombok.*; + +@Data +public class MeterAggregationDto implements IAggregationDto { + + @NonNull + public final SeriesDto series; + + public final double delta; + + public MeterAggregationDto(@NonNull final MeterAggregation meterAggregation, @NonNull final SeriesDto series) { + this.series = series; + this.delta = meterAggregation.delta; + } + +} diff --git a/src/main/java/de/ph87/data/series/meter/MeterService.java b/src/main/java/de/ph87/data/series/meter/MeterService.java index 95409ba..23a8bb1 100644 --- a/src/main/java/de/ph87/data/series/meter/MeterService.java +++ b/src/main/java/de/ph87/data/series/meter/MeterService.java @@ -14,6 +14,7 @@ import org.springframework.context.event.EventListener; import org.springframework.stereotype.*; import org.springframework.transaction.annotation.*; +import java.time.*; import java.util.*; import java.util.stream.*; @@ -75,14 +76,7 @@ public class MeterService { @NonNull public List getPoints(@NonNull final SeriesDto series, @NonNull final Aligned begin, @NonNull final Aligned end) { - final List graphPoints = switch (begin.alignment) { - case FIVE -> meterFiveRepository.findAllByIdMeterSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqualOrderByIdDate(series.id, begin.date, end.date); - case HOUR -> meterHourRepository.findAllByIdMeterSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqualOrderByIdDate(series.id, begin.date, end.date); - case DAY -> meterDayRepository.findAllByIdMeterSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqualOrderByIdDate(series.id, begin.date, end.date); - case WEEK -> meterWeekRepository.findAllByIdMeterSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqualOrderByIdDate(series.id, begin.date, end.date); - case MONTH -> meterMonthRepository.findAllByIdMeterSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqualOrderByIdDate(series.id, begin.date, end.date); - case YEAR -> meterYearRepository.findAllByIdMeterSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqualOrderByIdDate(series.id, begin.date, end.date); - }; + final List graphPoints = findRepository(begin.alignment).findAllByIdMeterSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqualOrderByIdDate(series.id, begin.date, end.date); final List points = graphPoints.stream().map(meterValue -> new GraphPoint(meterValue.getId().getDate(), meterValue.getMax() - meterValue.getMin())).collect(Collectors.toCollection(LinkedList::new)); for (int i = 0; i < points.size() - 1; i++) { if (points.get(i).date.compareTo(points.get(i + 1).date) == 0) { @@ -94,4 +88,26 @@ public class MeterService { return points; } + @NonNull + public List findAllAggregations(@NonNull final Alignment alignment, @NonNull final ZonedDateTime date) { + return findRepository(alignment).findAllDeltaSum(date).stream().map(this::toDto).toList(); + } + + @NonNull + private MeterAggregationDto toDto(@NonNull final MeterAggregation meterAggregation) { + return new MeterAggregationDto(meterAggregation, seriesService.toDto(meterAggregation.series)); + } + + @NonNull + private MeterValueRepository findRepository(@NonNull final Alignment alignment) { + return switch (alignment) { + case FIVE -> meterFiveRepository; + case HOUR -> meterHourRepository; + case DAY -> meterDayRepository; + case WEEK -> meterWeekRepository; + case MONTH -> meterMonthRepository; + case YEAR -> meterYearRepository; + }; + } + } diff --git a/src/main/java/de/ph87/data/series/meter/MeterValueRepository.java b/src/main/java/de/ph87/data/series/meter/MeterValueRepository.java index cfd632d..959713a 100644 --- a/src/main/java/de/ph87/data/series/meter/MeterValueRepository.java +++ b/src/main/java/de/ph87/data/series/meter/MeterValueRepository.java @@ -1,6 +1,7 @@ package de.ph87.data.series.meter; import lombok.*; +import org.springframework.data.jpa.repository.*; import org.springframework.data.repository.*; import java.time.*; @@ -11,4 +12,7 @@ public interface MeterValueRepository extends ListCrudRepo List findAllByIdMeterSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqualOrderByIdDate(long id, @NonNull ZonedDateTime begin, @NonNull ZonedDateTime end); + @Query("select new de.ph87.data.series.meter.MeterAggregation(m.id.meter.series, sum(m.max - m.min)) from #{#entityName} m where m.id.date = :date group by m.id.meter.series") + List findAllDeltaSum(@NonNull ZonedDateTime date); + } diff --git a/src/main/java/de/ph87/data/series/varying/VaryingAggregation.java b/src/main/java/de/ph87/data/series/varying/VaryingAggregation.java new file mode 100644 index 0000000..e4ef5ea --- /dev/null +++ b/src/main/java/de/ph87/data/series/varying/VaryingAggregation.java @@ -0,0 +1,18 @@ +package de.ph87.data.series.varying; + +import de.ph87.data.series.*; +import lombok.*; + +@Data +public class VaryingAggregation { + + @NonNull + public final Series series; + + public final double min; + + public final double avg; + + public final double max; + +} diff --git a/src/main/java/de/ph87/data/series/varying/VaryingAggregationDto.java b/src/main/java/de/ph87/data/series/varying/VaryingAggregationDto.java new file mode 100644 index 0000000..4b60fb2 --- /dev/null +++ b/src/main/java/de/ph87/data/series/varying/VaryingAggregationDto.java @@ -0,0 +1,25 @@ +package de.ph87.data.series.varying; + +import de.ph87.data.series.*; +import lombok.*; + +@Data +public class VaryingAggregationDto implements IAggregationDto { + + @NonNull + public final SeriesDto series; + + public final double min; + + public final double avg; + + public final double max; + + public VaryingAggregationDto(@NonNull final VaryingAggregation varyingAggregation, @NonNull final SeriesDto series) { + this.series = series; + this.min = varyingAggregation.min; + this.avg = varyingAggregation.avg; + this.max = varyingAggregation.max; + } + +} diff --git a/src/main/java/de/ph87/data/series/varying/VaryingRepository.java b/src/main/java/de/ph87/data/series/varying/VaryingRepository.java index aa0c9b7..7930f3a 100644 --- a/src/main/java/de/ph87/data/series/varying/VaryingRepository.java +++ b/src/main/java/de/ph87/data/series/varying/VaryingRepository.java @@ -1,6 +1,7 @@ package de.ph87.data.series.varying; import lombok.*; +import org.springframework.data.jpa.repository.*; import org.springframework.data.repository.*; import java.time.*; @@ -11,4 +12,7 @@ public interface VaryingRepository extends ListCrudRepository List findAllByIdSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqual(long id, @NonNull ZonedDateTime begin, @NonNull ZonedDateTime end); + @Query("select new de.ph87.data.series.varying.VaryingAggregation(m.id.series, m.min, m.avg, m.max) from #{#entityName} m where m.id.date = :date") + List findAllDeltaSum(@NonNull ZonedDateTime date); + } diff --git a/src/main/java/de/ph87/data/series/varying/VaryingService.java b/src/main/java/de/ph87/data/series/varying/VaryingService.java index ef5c268..6c12010 100644 --- a/src/main/java/de/ph87/data/series/varying/VaryingService.java +++ b/src/main/java/de/ph87/data/series/varying/VaryingService.java @@ -14,6 +14,7 @@ import org.springframework.context.event.EventListener; import org.springframework.stereotype.*; import org.springframework.transaction.annotation.*; +import java.time.*; import java.util.*; @Slf4j @@ -61,15 +62,33 @@ public class VaryingService { @NonNull public List getPoints(@NonNull final SeriesDto series, @NonNull final Aligned begin, @NonNull final Aligned end) { - final List graphPoints = switch (begin.alignment) { - case FIVE -> varyingFiveRepository.findAllByIdSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqual(series.id, begin.date, end.date); - case HOUR -> varyingHourRepository.findAllByIdSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqual(series.id, begin.date, end.date); - case DAY -> varyingDayRepository.findAllByIdSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqual(series.id, begin.date, end.date); - case WEEK -> varyingWeekRepository.findAllByIdSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqual(series.id, begin.date, end.date); - case MONTH -> varyingMonthRepository.findAllByIdSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqual(series.id, begin.date, end.date); - case YEAR -> varyingYearRepository.findAllByIdSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqual(series.id, begin.date, end.date); + return findRepository(begin.alignment) + .findAllByIdSeriesIdAndIdDateGreaterThanEqualAndIdDateLessThanEqual(series.id, begin.date, end.date) + .stream() + .map(v -> new GraphPoint(v.getId().getDate(), v.getAvg())) + .toList(); + } + + @NonNull + public List findAllAggregations(@NonNull final Alignment alignment, @NonNull final ZonedDateTime date) { + return findRepository(alignment).findAllDeltaSum(date).stream().map(this::toDto).toList(); + } + + @NonNull + private VaryingAggregationDto toDto(@NonNull final VaryingAggregation varyingAggregation) { + return new VaryingAggregationDto(varyingAggregation, seriesService.toDto(varyingAggregation.series)); + } + + @NonNull + private VaryingRepository findRepository(@NonNull final Alignment alignment) { + return switch (alignment) { + case FIVE -> varyingFiveRepository; + case HOUR -> varyingHourRepository; + case DAY -> varyingDayRepository; + case WEEK -> varyingWeekRepository; + case MONTH -> varyingMonthRepository; + case YEAR -> varyingYearRepository; }; - return graphPoints.stream().map(v -> new GraphPoint(v.getId().getDate(), v.getAvg())).toList(); } }