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
5 changes: 5 additions & 0 deletions internal/server/postgres/dataserverimpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -1417,13 +1417,18 @@ func (s *DataPlatformDataServiceServerImpl) GetForecastAsTimeseries(
return nil, status.Error(codes.InvalidArgument, "Invalid time window.")
}

if req.PivotTimestampUtc == nil {
req.PivotTimestampUtc = timestamppb.New(time.Now().UTC().Truncate(time.Minute))
}

lpprms := db.ListPredictionsForLocationParams{
GeometryUuid: dbSource.GeometryUuid,
ForecasterID: dbExistingForecaster.ForecasterID,
SourceTypeID: dbSource.SourceTypeID,
HorizonMins: int32(req.HorizonMins),
StartTimestampUtc: start,
EndTimestampUtc: end,
PivotTimestamp: pgtype.Timestamp{Time: req.PivotTimestampUtc.AsTime(), Valid: true},
}

dbValues, err := querier.ListPredictionsForLocation(ctx, lpprms)
Expand Down
36 changes: 28 additions & 8 deletions internal/server/postgres/dataserverimpl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -746,9 +746,12 @@ func TestGetForecastAsTimeseries(t *testing.T) {
testcases := []struct {
name string
horizonMins int32
pivotTime time.Time
expectedValues []float32
}{
{
name: "Should return expected values for horizon 0 mins",
horizonMins: 0,
// For horizon 0, we should get all the values from the latest forecast,
// plus the values from the previous forecasts that have the lowest horizon
// for each target time.
Expand All @@ -760,8 +763,6 @@ func TestGetForecastAsTimeseries(t *testing.T) {
// 0, 8, 16, 24, 32, 40 (horizons 0 to 25 minutes from forecast 3)
// Then the same from forecast 2, as it's horizon is smaller - likewise then forecast 1
// 0, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88 (horizons 0 to 55 minutes from forecast 0)
name: "Should return expected values for horizon 0 mins",
horizonMins: 0,
expectedValues: []float32{
0.00, 0.08, 0.16, 0.24, 0.32, 0.40,
0.00, 0.08, 0.16, 0.24, 0.32, 0.40,
Expand All @@ -770,10 +771,10 @@ func TestGetForecastAsTimeseries(t *testing.T) {
},
},
{
// For horizon of 14 minutes, anything with a lesser horizon should not be included.
// So the value for 0, 5, and 10 minutes should not be included.
name: "Should return expected values for horizon 14 mins",
horizonMins: 14,
// For horizon of 14 minutes, anything with a lesser horizon should not be included.
// So the value for 0, 5, and 10 minutes should not be included.
expectedValues: []float32{
0.24, 0.32, 0.40, 0.48, 0.56, 0.64,
0.24, 0.32, 0.40, 0.48, 0.56, 0.64,
Expand All @@ -795,15 +796,34 @@ func TestGetForecastAsTimeseries(t *testing.T) {
name: "Shouldn't return successfully for horizon 60 mins",
horizonMins: 60,
},
{
name: "Should return expected values for horizon 14 minutes with pivot time",
horizonMins: 14,
pivotTime: pivotTime.Add(-15 * time.Minute),
// For horizon of 14 minutes and a pivot time of 15 minutes before the latest,
// we should expect the same as for the 14 minute horizon no pivot time case,
// only this time the latest forecast should not be included at all.
// Hence we only see data for three forecasts.
expectedValues: []float32{
0.24, 0.32, 0.40, 0.48, 0.56, 0.64,
0.24, 0.32, 0.40, 0.48, 0.56, 0.64,
0.24, 0.32, 0.40, 0.48, 0.56, 0.64, 0.72, 0.80, 0.88,
},
},
}

for _, tc := range testcases {
t.Run(fmt.Sprintf("Horizon %d mins", tc.horizonMins), func(t *testing.T) {
if tc.pivotTime.Equal((time.Time{})) {
tc.pivotTime = pivotTime
}

resp, err := dc.GetForecastAsTimeseries(t.Context(), &pb.GetForecastAsTimeseriesRequest{
LocationUuid: siteResp.LocationUuid,
HorizonMins: uint32(tc.horizonMins),
Forecaster: forecasterResp.Forecaster,
EnergySource: pb.EnergySource_ENERGY_SOURCE_SOLAR,
LocationUuid: siteResp.LocationUuid,
HorizonMins: uint32(tc.horizonMins),
Forecaster: forecasterResp.Forecaster,
EnergySource: pb.EnergySource_ENERGY_SOURCE_SOLAR,
PivotTimestampUtc: timestamppb.New(tc.pivotTime),
TimeWindow: &pb.TimeWindow{
StartTimestampUtc: timestamppb.New(pivotTime.Add(-time.Hour * 48)),
EndTimestampUtc: timestamppb.New(pivotTime.Add(time.Hour * 36)),
Expand Down
1 change: 1 addition & 0 deletions internal/server/postgres/sql/queries/predictions.sql
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ WITH relevant_forecasts AS (
WHERE f.geometry_uuid = $1
AND f.source_type_id = $2
AND f.forecaster_id = $3
AND f.init_time_utc <= sqlc.arg(pivot_timestamp)::TIMESTAMP
AND f.target_period && TSRANGE(
sqlc.arg(start_timestamp_utc)::TIMESTAMP,
sqlc.arg(end_timestamp_utc)::TIMESTAMP,
Expand Down
6 changes: 6 additions & 0 deletions proto/ocf/dp/dp-data.messages.proto
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ message GetForecastAsTimeseriesRequest {
Forecaster forecaster = 5 [
(buf.validate.field).required = true
];
/* The time to search backwards from to find forecasts.
* If not specified, the current time will be used.
*/
optional google.protobuf.Timestamp pivot_timestamp_utc = 6 [
(buf.validate.field).timestamp = { gt: { seconds: 112000000}, lt_now: true }
];
}

message GetForecastAsTimeseriesResponse {
Expand Down