From 4f8d25e56e07304261e90ce98efe48e70d755e38 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Mon, 20 Jun 2022 23:45:40 +0400 Subject: [PATCH] #58 -- add tests, add time series mapper --- build.gradle | 1 + .../ru/ulstu/extractor/model/TimeSeries.java | 13 ++- .../extractor/model/TimeSeriesValue.java | 13 ++- .../extractor/ts/TimeSeriesDateMapper.java | 59 ++++++++++ .../java/ru/ulstu/TimeSeriesMapperTest.java | 103 ++++++++++++++++++ 5 files changed, 186 insertions(+), 3 deletions(-) create mode 100644 src/main/java/ru/ulstu/extractor/ts/TimeSeriesDateMapper.java create mode 100644 src/test/java/ru/ulstu/TimeSeriesMapperTest.java diff --git a/build.gradle b/build.gradle index 1d713ee..04deaca 100644 --- a/build.gradle +++ b/build.gradle @@ -61,6 +61,7 @@ dependencies { implementation group: 'commons-io', name: 'commons-io', version: '2.6' implementation group: 'net.sourceforge.htmlunit', name: 'htmlunit', version: '2.35.0' implementation group: 'com.github.javaparser', name: 'javaparser-core', version: '3.20.2' + implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.12.0' implementation group: 'io.springfox', name: 'springfox-boot-starter', version: '3.0.0' diff --git a/src/main/java/ru/ulstu/extractor/model/TimeSeries.java b/src/main/java/ru/ulstu/extractor/model/TimeSeries.java index 2d90523..d04db07 100644 --- a/src/main/java/ru/ulstu/extractor/model/TimeSeries.java +++ b/src/main/java/ru/ulstu/extractor/model/TimeSeries.java @@ -3,7 +3,11 @@ package ru.ulstu.extractor.model; import org.hibernate.annotations.Fetch; import org.hibernate.annotations.FetchMode; -import javax.persistence.*; +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.JoinColumn; +import javax.persistence.OneToMany; import java.util.ArrayList; import java.util.List; @@ -15,11 +19,18 @@ public class TimeSeries extends BaseEntity { @Fetch(FetchMode.SUBSELECT) private List values = new ArrayList<>(); + public TimeSeries() { + } public TimeSeries(String name) { this.name = name; } + public TimeSeries(String name, List values) { + this.name = name; + this.values = values; + } + public String getName() { return name; } diff --git a/src/main/java/ru/ulstu/extractor/model/TimeSeriesValue.java b/src/main/java/ru/ulstu/extractor/model/TimeSeriesValue.java index f4daaf4..3efd621 100644 --- a/src/main/java/ru/ulstu/extractor/model/TimeSeriesValue.java +++ b/src/main/java/ru/ulstu/extractor/model/TimeSeriesValue.java @@ -12,6 +12,15 @@ public class TimeSeriesValue extends BaseEntity { @ManyToOne(fetch = FetchType.LAZY) private TimeSeries timeSeries; + public TimeSeriesValue() { + } + + public TimeSeriesValue(Date date, Integer value) { + this.timeSeries = timeSeries; + this.date = date; + this.value = value; + } + public TimeSeriesValue(TimeSeries timeSeries, Date date, Integer value) { this.timeSeries = timeSeries; this.date = date; @@ -34,11 +43,11 @@ public class TimeSeriesValue extends BaseEntity { this.value = value; } - public TimeSeries getTimeSeriesType() { + public TimeSeries getTimeSeries() { return timeSeries; } - public void setTimeSeriesType(TimeSeries timeSeries) { + public void setTimeSeries(TimeSeries timeSeries) { this.timeSeries = timeSeries; } } diff --git a/src/main/java/ru/ulstu/extractor/ts/TimeSeriesDateMapper.java b/src/main/java/ru/ulstu/extractor/ts/TimeSeriesDateMapper.java new file mode 100644 index 0000000..d192b53 --- /dev/null +++ b/src/main/java/ru/ulstu/extractor/ts/TimeSeriesDateMapper.java @@ -0,0 +1,59 @@ +package ru.ulstu.extractor.ts; + +import org.apache.commons.lang3.time.DateUtils; +import ru.ulstu.extractor.model.TimeSeries; +import ru.ulstu.extractor.model.TimeSeriesValue; + +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Класс для регулировки дискретности временного ряда. + * Можно создать временной ряд с точками с секундными отметками, затем суммировать значения, + * применив одно из значений TimeSeriesInterval + */ +public class TimeSeriesDateMapper { + + public TimeSeries mapTimeSeriesToInterval(TimeSeriesInterval timeSeriesInterval, TimeSeries timeSeries) { + List trimmedTimeSeriesValues = timeSeries.getValues() + .stream() + .map(timeSeriesValue -> new TimeSeriesValue(trimTo(timeSeriesInterval, timeSeriesValue.getDate()), + timeSeriesValue.getValue())) + .collect(Collectors.toList()); + Map groupedTimeSeriesValues = trimmedTimeSeriesValues + .stream() + .collect(Collectors.groupingBy(TimeSeriesValue::getDate, + Collectors.summingInt(TimeSeriesValue::getValue))); + + return new TimeSeries(timeSeries.getName(), groupedTimeSeriesValues.entrySet() + .stream() + .map(e -> new TimeSeriesValue(e.getKey(), e.getValue())) + .collect(Collectors.toList())); + } + + private Date trimTo(TimeSeriesInterval timeSeriesInterval, Date date) { + return DateUtils.truncate(date, timeSeriesInterval.calendarField); + } + + /** + * Интервальность временного ряда при преобразовании + */ + public enum TimeSeriesInterval { + SECOND(Calendar.SECOND), + MINUTE(Calendar.MINUTE), + HOUR(Calendar.HOUR_OF_DAY), + DAY(Calendar.DAY_OF_MONTH), + WEEK(Calendar.WEEK_OF_MONTH), + MONTH(Calendar.MONTH), + YEAR(Calendar.YEAR); + + private final int calendarField; + + TimeSeriesInterval(int calendarField) { + this.calendarField = calendarField; + } + } +} diff --git a/src/test/java/ru/ulstu/TimeSeriesMapperTest.java b/src/test/java/ru/ulstu/TimeSeriesMapperTest.java new file mode 100644 index 0000000..91030f9 --- /dev/null +++ b/src/test/java/ru/ulstu/TimeSeriesMapperTest.java @@ -0,0 +1,103 @@ +package ru.ulstu; + +import org.junit.Assert; +import org.junit.Test; +import ru.ulstu.extractor.model.TimeSeries; +import ru.ulstu.extractor.model.TimeSeriesValue; +import ru.ulstu.extractor.ts.TimeSeriesDateMapper; + +import java.util.Arrays; +import java.util.Calendar; +import java.util.GregorianCalendar; + +public class TimeSeriesMapperTest { + @Test + public void testMappingByDay() { + Calendar c1 = GregorianCalendar.getInstance(); + c1.set(2020, 5, 1, 1, 1, 1); + Calendar c2 = GregorianCalendar.getInstance(); + c2.set(2020, 5, 2, 2, 1, 1); + TimeSeries timeSeries = new TimeSeries("Тестовый", + Arrays.asList(new TimeSeriesValue(c1.getTime(), 10), + new TimeSeriesValue(c2.getTime(), 10))); + TimeSeriesDateMapper mapper = new TimeSeriesDateMapper(); + timeSeries = mapper.mapTimeSeriesToInterval(TimeSeriesDateMapper.TimeSeriesInterval.MONTH, timeSeries); + Assert.assertEquals(1, timeSeries.getValues().size()); + Assert.assertEquals(Integer.valueOf(20), timeSeries.getValues().get(0).getValue()); + } + + @Test + public void testMappingByDayDifferent() { + Calendar c1 = GregorianCalendar.getInstance(); + c1.set(2020, 5, 1, 1, 1, 1); + Calendar c2 = GregorianCalendar.getInstance(); + c2.set(2020, 5, 2, 1, 1, 1); + TimeSeries timeSeries = new TimeSeries("Тестовый", + Arrays.asList(new TimeSeriesValue(c1.getTime(), 10), + new TimeSeriesValue(c2.getTime(), 10))); + TimeSeriesDateMapper mapper = new TimeSeriesDateMapper(); + timeSeries = mapper.mapTimeSeriesToInterval(TimeSeriesDateMapper.TimeSeriesInterval.MONTH, timeSeries); + Assert.assertEquals(1, timeSeries.getValues().size()); + Assert.assertEquals(Integer.valueOf(20), timeSeries.getValues().get(0).getValue()); + } + + @Test + public void testMappingByMonth() { + Calendar c1 = GregorianCalendar.getInstance(); + c1.set(2020, 5, 1, 1, 1, 1); + Calendar c2 = GregorianCalendar.getInstance(); + c2.set(2020, 5, 2, 1, 1, 1); + TimeSeries timeSeries = new TimeSeries("Тестовый", + Arrays.asList(new TimeSeriesValue(c1.getTime(), 10), + new TimeSeriesValue(c2.getTime(), 10))); + TimeSeriesDateMapper mapper = new TimeSeriesDateMapper(); + timeSeries = mapper.mapTimeSeriesToInterval(TimeSeriesDateMapper.TimeSeriesInterval.MONTH, timeSeries); + Assert.assertEquals(1, timeSeries.getValues().size()); + Assert.assertEquals(Integer.valueOf(20), timeSeries.getValues().get(0).getValue()); + } + + @Test + public void testMappingByMonthDifferent() { + Calendar c1 = GregorianCalendar.getInstance(); + c1.set(2020, 5, 1, 1, 1, 1); + Calendar c2 = GregorianCalendar.getInstance(); + c2.set(2020, 6, 2, 1, 1, 1); + TimeSeries timeSeries = new TimeSeries("Тестовый", + Arrays.asList(new TimeSeriesValue(c1.getTime(), 10), + new TimeSeriesValue(c2.getTime(), 10))); + TimeSeriesDateMapper mapper = new TimeSeriesDateMapper(); + timeSeries = mapper.mapTimeSeriesToInterval(TimeSeriesDateMapper.TimeSeriesInterval.MONTH, timeSeries); + Assert.assertEquals(2, timeSeries.getValues().size()); + Assert.assertEquals(Integer.valueOf(10), timeSeries.getValues().get(0).getValue()); + } + + @Test + public void testMappingByYear() { + Calendar c1 = GregorianCalendar.getInstance(); + c1.set(2020, 5, 1, 1, 1, 1); + Calendar c2 = GregorianCalendar.getInstance(); + c2.set(2020, 5, 2, 1, 1, 1); + TimeSeries timeSeries = new TimeSeries("Тестовый", + Arrays.asList(new TimeSeriesValue(c1.getTime(), 10), + new TimeSeriesValue(c2.getTime(), 10))); + TimeSeriesDateMapper mapper = new TimeSeriesDateMapper(); + timeSeries = mapper.mapTimeSeriesToInterval(TimeSeriesDateMapper.TimeSeriesInterval.YEAR, timeSeries); + Assert.assertEquals(1, timeSeries.getValues().size()); + Assert.assertEquals(Integer.valueOf(20), timeSeries.getValues().get(0).getValue()); + } + + @Test + public void testMappingByYearDifferent() { + Calendar c1 = GregorianCalendar.getInstance(); + c1.set(2020, 5, 1, 1, 1, 1); + Calendar c2 = GregorianCalendar.getInstance(); + c2.set(2021, 5, 2, 1, 1, 1); + TimeSeries timeSeries = new TimeSeries("Тестовый", + Arrays.asList(new TimeSeriesValue(c1.getTime(), 10), + new TimeSeriesValue(c2.getTime(), 10))); + TimeSeriesDateMapper mapper = new TimeSeriesDateMapper(); + timeSeries = mapper.mapTimeSeriesToInterval(TimeSeriesDateMapper.TimeSeriesInterval.YEAR, timeSeries); + Assert.assertEquals(2, timeSeries.getValues().size()); + Assert.assertEquals(Integer.valueOf(10), timeSeries.getValues().get(0).getValue()); + } +}