Skip to content

Commit 18b678c

Browse files
authored
feat(api): Add pivot_timestamp to timeseries route (#103)
1 parent f3ffd1f commit 18b678c

File tree

4 files changed

+40
-8
lines changed

4 files changed

+40
-8
lines changed

internal/server/postgres/dataserverimpl.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1417,13 +1417,18 @@ func (s *DataPlatformDataServiceServerImpl) GetForecastAsTimeseries(
14171417
return nil, status.Error(codes.InvalidArgument, "Invalid time window.")
14181418
}
14191419

1420+
if req.PivotTimestampUtc == nil {
1421+
req.PivotTimestampUtc = timestamppb.New(time.Now().UTC().Truncate(time.Minute))
1422+
}
1423+
14201424
lpprms := db.ListPredictionsForLocationParams{
14211425
GeometryUuid: dbSource.GeometryUuid,
14221426
ForecasterID: dbExistingForecaster.ForecasterID,
14231427
SourceTypeID: dbSource.SourceTypeID,
14241428
HorizonMins: int32(req.HorizonMins),
14251429
StartTimestampUtc: start,
14261430
EndTimestampUtc: end,
1431+
PivotTimestamp: pgtype.Timestamp{Time: req.PivotTimestampUtc.AsTime(), Valid: true},
14271432
}
14281433

14291434
dbValues, err := querier.ListPredictionsForLocation(ctx, lpprms)

internal/server/postgres/dataserverimpl_test.go

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -746,9 +746,12 @@ func TestGetForecastAsTimeseries(t *testing.T) {
746746
testcases := []struct {
747747
name string
748748
horizonMins int32
749+
pivotTime time.Time
749750
expectedValues []float32
750751
}{
751752
{
753+
name: "Should return expected values for horizon 0 mins",
754+
horizonMins: 0,
752755
// For horizon 0, we should get all the values from the latest forecast,
753756
// plus the values from the previous forecasts that have the lowest horizon
754757
// for each target time.
@@ -760,8 +763,6 @@ func TestGetForecastAsTimeseries(t *testing.T) {
760763
// 0, 8, 16, 24, 32, 40 (horizons 0 to 25 minutes from forecast 3)
761764
// Then the same from forecast 2, as it's horizon is smaller - likewise then forecast 1
762765
// 0, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88 (horizons 0 to 55 minutes from forecast 0)
763-
name: "Should return expected values for horizon 0 mins",
764-
horizonMins: 0,
765766
expectedValues: []float32{
766767
0.00, 0.08, 0.16, 0.24, 0.32, 0.40,
767768
0.00, 0.08, 0.16, 0.24, 0.32, 0.40,
@@ -770,10 +771,10 @@ func TestGetForecastAsTimeseries(t *testing.T) {
770771
},
771772
},
772773
{
773-
// For horizon of 14 minutes, anything with a lesser horizon should not be included.
774-
// So the value for 0, 5, and 10 minutes should not be included.
775774
name: "Should return expected values for horizon 14 mins",
776775
horizonMins: 14,
776+
// For horizon of 14 minutes, anything with a lesser horizon should not be included.
777+
// So the value for 0, 5, and 10 minutes should not be included.
777778
expectedValues: []float32{
778779
0.24, 0.32, 0.40, 0.48, 0.56, 0.64,
779780
0.24, 0.32, 0.40, 0.48, 0.56, 0.64,
@@ -795,15 +796,34 @@ func TestGetForecastAsTimeseries(t *testing.T) {
795796
name: "Shouldn't return successfully for horizon 60 mins",
796797
horizonMins: 60,
797798
},
799+
{
800+
name: "Should return expected values for horizon 14 minutes with pivot time",
801+
horizonMins: 14,
802+
pivotTime: pivotTime.Add(-15 * time.Minute),
803+
// For horizon of 14 minutes and a pivot time of 15 minutes before the latest,
804+
// we should expect the same as for the 14 minute horizon no pivot time case,
805+
// only this time the latest forecast should not be included at all.
806+
// Hence we only see data for three forecasts.
807+
expectedValues: []float32{
808+
0.24, 0.32, 0.40, 0.48, 0.56, 0.64,
809+
0.24, 0.32, 0.40, 0.48, 0.56, 0.64,
810+
0.24, 0.32, 0.40, 0.48, 0.56, 0.64, 0.72, 0.80, 0.88,
811+
},
812+
},
798813
}
799814

800815
for _, tc := range testcases {
801816
t.Run(fmt.Sprintf("Horizon %d mins", tc.horizonMins), func(t *testing.T) {
817+
if tc.pivotTime.Equal((time.Time{})) {
818+
tc.pivotTime = pivotTime
819+
}
820+
802821
resp, err := dc.GetForecastAsTimeseries(t.Context(), &pb.GetForecastAsTimeseriesRequest{
803-
LocationUuid: siteResp.LocationUuid,
804-
HorizonMins: uint32(tc.horizonMins),
805-
Forecaster: forecasterResp.Forecaster,
806-
EnergySource: pb.EnergySource_ENERGY_SOURCE_SOLAR,
822+
LocationUuid: siteResp.LocationUuid,
823+
HorizonMins: uint32(tc.horizonMins),
824+
Forecaster: forecasterResp.Forecaster,
825+
EnergySource: pb.EnergySource_ENERGY_SOURCE_SOLAR,
826+
PivotTimestampUtc: timestamppb.New(tc.pivotTime),
807827
TimeWindow: &pb.TimeWindow{
808828
StartTimestampUtc: timestamppb.New(pivotTime.Add(-time.Hour * 48)),
809829
EndTimestampUtc: timestamppb.New(pivotTime.Add(time.Hour * 36)),

internal/server/postgres/sql/queries/predictions.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ WITH relevant_forecasts AS (
189189
WHERE f.geometry_uuid = $1
190190
AND f.source_type_id = $2
191191
AND f.forecaster_id = $3
192+
AND f.init_time_utc <= sqlc.arg(pivot_timestamp)::TIMESTAMP
192193
AND f.target_period && TSRANGE(
193194
sqlc.arg(start_timestamp_utc)::TIMESTAMP,
194195
sqlc.arg(end_timestamp_utc)::TIMESTAMP,

proto/ocf/dp/dp-data.messages.proto

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ message GetForecastAsTimeseriesRequest {
6969
Forecaster forecaster = 5 [
7070
(buf.validate.field).required = true
7171
];
72+
/* The time to search backwards from to find forecasts.
73+
* If not specified, the current time will be used.
74+
*/
75+
optional google.protobuf.Timestamp pivot_timestamp_utc = 6 [
76+
(buf.validate.field).timestamp = { gt: { seconds: 112000000}, lt_now: true }
77+
];
7278
}
7379

7480
message GetForecastAsTimeseriesResponse {

0 commit comments

Comments
 (0)