Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added
- Enhanced check for invalid field names in sources [#1383](https://github.com/ie3-institute/PowerSystemDataModel/issues/1383)
- Enhancing value retrieval in `TimeSeriesSource` [1280](https://github.com/ie3-institute/PowerSystemDataModel/issues/1280)

### Fixed

Expand Down
24 changes: 24 additions & 0 deletions src/main/java/edu/ie3/datamodel/io/source/TimeSeriesSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,22 @@ public abstract IndividualTimeSeries<V> getTimeSeries(ClosedInterval<ZonedDateTi

public abstract Optional<V> getValue(ZonedDateTime time);

/**
* Method to retrieve the value of the given time or the last timestamp before the given time.
*
* @param time given time
* @return an option for a value
*/
public Optional<V> getValueOrLast(ZonedDateTime time) {
Optional<V> value = getValue(time);

if (value.isEmpty()) {
return getPreviousTimeBasedValue(time).map(TimeBasedValue::getValue);
}

return value;
}

public abstract Optional<TimeBasedValue<V>> getPreviousTimeBasedValue(ZonedDateTime time);

/**
Expand All @@ -61,4 +77,12 @@ public abstract IndividualTimeSeries<V> getTimeSeries(ClosedInterval<ZonedDateTi
* @return a list of time keys
*/
public abstract List<ZonedDateTime> getTimeKeysAfter(ZonedDateTime time);

/**
* Method to return all last known time keys before a given timestamp.
*
* @param time given time
* @return an option for the time key
*/
public abstract Optional<ZonedDateTime> getLastTimeKeyBefore(ZonedDateTime time);
}
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,11 @@ public List<ZonedDateTime> getTimeKeysAfter(ZonedDateTime time) {
return timeSeries.getTimeKeysAfter(time);
}

@Override
public Optional<ZonedDateTime> getLastTimeKeyBefore(ZonedDateTime time) {
return timeSeries.getPreviousDateTime(time);
}

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,15 @@ public List<ZonedDateTime> getTimeKeysAfter(ZonedDateTime time) {
.toList();
}

@Override
public Optional<ZonedDateTime> getLastTimeKeyBefore(ZonedDateTime time) {
return dataSource
.executeQuery(
queryForValueBefore, ps -> ps.setTimestamp(1, Timestamp.from(time.toInstant())))
.map(valueFactory::extractTime)
.max(ZonedDateTime::compareTo);
}

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

/** Creates a set of TimeBasedValues from database */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public Optional<TimeBasedValue<R>> getTimeBasedValue(ZonedDateTime time) {
* @param time Reference in time
* @return The next earlier known time instant
*/
protected abstract Optional<ZonedDateTime> getPreviousDateTime(ZonedDateTime time);
public abstract Optional<ZonedDateTime> getPreviousDateTime(ZonedDateTime time);

/**
* Get the next later known time instant
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ public Optional<V> getValue(ZonedDateTime time) {
}

@Override
protected Optional<ZonedDateTime> getPreviousDateTime(ZonedDateTime time) {
public Optional<ZonedDateTime> getPreviousDateTime(ZonedDateTime time) {
return timeToValue.keySet().stream()
.filter(valueTime -> valueTime.compareTo(time) < 0)
.max(Comparator.naturalOrder());
.filter(valueTime -> valueTime.isBefore(time))
.max(ZonedDateTime::compareTo);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public Set<LoadProfileEntry<V>> getEntries() {
}

@Override
protected Optional<ZonedDateTime> getPreviousDateTime(ZonedDateTime time) {
public Optional<ZonedDateTime> getPreviousDateTime(ZonedDateTime time) {
return Optional.of(time.minusMinutes(15));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,19 @@ class CsvTimeSeriesSourceTest extends Specification implements CsvTestDataMeta {
actual.data.get() == expected
}

def "The csv time series source returns the last value, if there is no current value"() {
given:
def factory = new TimeBasedSimpleValueFactory(EnergyPriceValue)
def source = new CsvTimeSeriesSource(";", timeSeriesFolderPath, new FileNamingStrategy(), UUID.fromString("2fcb3e53-b94a-4b96-bea4-c469e499f1a1"), Path.of("its_c_2fcb3e53-b94a-4b96-bea4-c469e499f1a1"), EnergyPriceValue, factory)
def time = TimeUtil.withDefaults.toZonedDateTime("2020-01-01T00:13:00Z")

when:
def actual = source.getValueOrLast(time)

then:
actual == Optional.of(new EnergyPriceValue(Quantities.getQuantity(100.0, ENERGY_PRICE)))
}

def "The csv time series source returns the time keys after a given key correctly"() {
given:
def factory = new TimeBasedSimpleValueFactory(EnergyPriceValue)
Expand All @@ -63,6 +76,27 @@ class CsvTimeSeriesSourceTest extends Specification implements CsvTestDataMeta {
]
}

def "The csv time series source returns the time key before a given key correctly"() {
given:
def factory = new TimeBasedSimpleValueFactory(EnergyPriceValue)
def source = new CsvTimeSeriesSource(";", timeSeriesFolderPath, new FileNamingStrategy(), UUID.fromString("2fcb3e53-b94a-4b96-bea4-c469e499f1a1"), Path.of("its_c_2fcb3e53-b94a-4b96-bea4-c469e499f1a1"), EnergyPriceValue, factory)

when:
def time = TimeUtil.withDefaults.toZonedDateTime(timeKey)

def actual = source.getLastTimeKeyBefore(time)

then:
actual == expectedKey

where:
timeKey | expectedKey
"2019-12-31T23:59:59Z" | Optional.empty()
"2020-01-01T00:00:00Z" | Optional.empty()
"2020-01-01T00:15:00Z" | Optional.of(TimeUtil.withDefaults.toZonedDateTime("2020-01-01T00:00:00Z"))
"2020-01-03T00:00:00Z" | Optional.of(TimeUtil.withDefaults.toZonedDateTime("2020-01-01T00:15:00Z"))
}

def "The factory method in csv time series source refuses to build time series with unsupported column type"() {
given:
def metaInformation = new CsvIndividualTimeSeriesMetaInformation(UUID.fromString("8bc9120d-fb9b-4484-b4e3-0cdadf0feea9"), ColumnScheme.WEATHER, Path.of("its_weather_8bc9120d-fb9b-4484-b4e3-0cdadf0feea9"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package edu.ie3.datamodel.io.source.sql

import static edu.ie3.test.common.TimeSeriesSourceTestData.*
import static edu.ie3.util.quantities.PowerSystemUnits.KILOWATT

import edu.ie3.datamodel.exceptions.SourceException
import edu.ie3.datamodel.io.connectors.SqlConnector
Expand All @@ -23,6 +24,7 @@ import org.testcontainers.spock.Testcontainers
import org.testcontainers.utility.MountableFile
import spock.lang.Shared
import spock.lang.Specification
import tech.units.indriya.quantity.Quantities

import java.time.format.DateTimeFormatter

Expand Down Expand Up @@ -124,6 +126,17 @@ class SqlTimeSeriesSourceIT extends Specification implements TestContainerHelper
value.get() == P_VALUE_00MIN
}

def "The cSqlTimeSeriesSource returns the last value, if there is no current value"() {
given:
def time = TimeUtil.withDefaults.toZonedDateTime("2020-01-01T00:13:00Z")

when:
def actual = pSource.getValueOrLast(time)

then:
actual == Optional.of(new PValue(Quantities.getQuantity(1000.0, KILOWATT)))
}

def "A SqlTimeSeriesSource is able to return the previous value for a given time"() {
when:
def actual = pSource.getPreviousTimeBasedValue(time)
Expand Down Expand Up @@ -173,4 +186,21 @@ class SqlTimeSeriesSourceIT extends Specification implements TestContainerHelper
TimeUtil.withDefaults.toZonedDateTime("2020-01-01T00:15:00Z")
]
}

def "The SqlTimeSeriesSource returns the time key before a given key correctly"() {
when:
def time = TimeUtil.withDefaults.toZonedDateTime(timeKey)

def actual = pSource.getLastTimeKeyBefore(time)

then:
actual == expectedKey

where:
timeKey | expectedKey
"2019-12-31T23:59:59Z" | Optional.empty()
"2020-01-01T00:00:00Z" | Optional.empty()
"2020-01-01T00:15:00Z" | Optional.of(TimeUtil.withDefaults.toZonedDateTime("2020-01-01T00:00:00Z"))
"2020-01-03T00:00:00Z" | Optional.of(TimeUtil.withDefaults.toZonedDateTime("2020-01-01T00:15:00Z"))
}
}