Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
151 commits
Select commit Hold shift + click to select a range
7d85db0
feat: make predict retrain-frequency default to planning horizon then…
BelhsanHmida Feb 17, 2026
39e64ec
refactor: simplify end_date calculation to use predict_period instea…
BelhsanHmida Feb 17, 2026
9d83911
feat: default max_forecast_horizon to predict_period
BelhsanHmida Feb 17, 2026
7543395
feat: add validation for max_forecast_horizon to ensure it does not e…
BelhsanHmida Feb 17, 2026
da4b943
feat: set default forecast_frequency based on min of planning_horizon…
BelhsanHmida Feb 17, 2026
43d8d9d
fix: add planning horizon from config
BelhsanHmida Feb 17, 2026
b3dd12d
dev: uncomment out tests that were failing
BelhsanHmida Feb 17, 2026
24f1569
fix: fix test cae forecast_frequency expectation it should be 12 hour…
BelhsanHmida Feb 17, 2026
9c5d677
fix: tests should expect 5 cycles. the test passes when we expect 1cycle
BelhsanHmida Feb 17, 2026
3642c9a
feat: add duration to schema
Flix6x Feb 17, 2026
a448bf5
feat: pass original data to `resolve_config` so we can check whether …
Flix6x Feb 17, 2026
3456ffd
feat: move end_date calculation up
BelhsanHmida Feb 17, 2026
a3c63a4
feat: fix max-forecast-horizon and forecast freq default calculation
BelhsanHmida Feb 17, 2026
acacac8
Merge remote-tracking branch 'origin/dev/new-forecasting-defaults' in…
Flix6x Feb 17, 2026
2c6e846
dev: remove breakpoint
BelhsanHmida Feb 17, 2026
b4d261c
feat: throw ValidationError in case start, end and duration are all p…
Flix6x Feb 17, 2026
cffe37e
Merge remote-tracking branch 'origin/dev/new-forecasting-defaults' in…
Flix6x Feb 17, 2026
32e06af
fix: remove unneeded validation
BelhsanHmida Feb 17, 2026
f3c31ab
style: run pre-commit
BelhsanHmida Feb 17, 2026
2a805f0
refactor: move check to pre_load
Flix6x Feb 17, 2026
dda8485
Merge remote-tracking branch 'origin/dev/new-forecasting-defaults' in…
Flix6x Feb 17, 2026
9e7acc9
dev: comment out tests cases that pass
BelhsanHmida Feb 17, 2026
6a5591c
fix: fix calculation for retrain_freq
BelhsanHmida Feb 17, 2026
e4833df
Revert "dev: comment out tests cases that pass"
BelhsanHmida Feb 17, 2026
6db140d
style: run pre-commit
BelhsanHmida Feb 17, 2026
a2819ed
chore: regenerate openapi-spec.json
BelhsanHmida Feb 17, 2026
930cf1c
refactor: move parametrized cases next to case descriptions
Flix6x Feb 17, 2026
e8fab7e
dev: remove breakpoint
Flix6x Feb 17, 2026
875ad1a
fix: check predict period
Flix6x Feb 17, 2026
cb80192
fix: case 1
Flix6x Feb 17, 2026
72ff44f
feat: improve error message for failing test cases
Flix6x Feb 17, 2026
90bb7cf
dev: case 2 needs further investigation
Flix6x Feb 17, 2026
d498436
fix: case 4
Flix6x Feb 17, 2026
a5128e7
fix: partially fix case 5
Flix6x Feb 17, 2026
4926de7
dev: case 5 needs further investigation
Flix6x Feb 17, 2026
bcf1d40
fix: case 6
Flix6x Feb 17, 2026
1334308
dev: comment out test cases that need further investigation, and pref…
Flix6x Feb 17, 2026
847b182
Merge remote-tracking branch 'origin/dev/new-forecasting-defaults' in…
Flix6x Feb 17, 2026
79dec91
docs: move the documented defaults and choices for timing parameters …
Flix6x Feb 17, 2026
415dc0c
fix: correctly set retrain_frequency_in_hours
BelhsanHmida Feb 17, 2026
a217df0
fix: streamline job metadata handling in run method to prevent undefi…
BelhsanHmida Feb 17, 2026
8dd8ecb
refactor: rename parameter name to match field name
Flix6x Feb 17, 2026
27a1d11
Merge remote-tracking branch 'origin/dev/new-forecasting-defaults' in…
Flix6x Feb 17, 2026
cea8afc
fix: stop mixing up retrain-frequency and predict-period
Flix6x Feb 17, 2026
b98d897
fix: false variable name
Flix6x Feb 17, 2026
0832819
style: black
Flix6x Feb 17, 2026
d16ad4e
fix: cap retrain-frequency to not exceed predict-period
Flix6x Feb 17, 2026
25b49a3
fix: incomplete schema renaming
Flix6x Feb 17, 2026
dcddd64
fix: exclude CLI-specific fields from API schema
Flix6x Feb 17, 2026
0f9a4c1
docs: clarify what happens to the source ID if you change the forecas…
Flix6x Feb 17, 2026
e2f0128
fix: change target_sensor reference from target to sensor
BelhsanHmida Feb 17, 2026
fb8a9cb
fix: update test case 4 comment and expectations. we expect 4 cycles …
BelhsanHmida Feb 17, 2026
0914a96
fix: update cycle frequency calculation to use retrain_frequency inst…
BelhsanHmida Feb 17, 2026
79420d3
fix: search sensor forecasts (the ones computed directly not via api)…
BelhsanHmida Feb 18, 2026
8c57313
fix: adjust event end date calculation in forecast belief search to e…
BelhsanHmida Feb 18, 2026
075bf4e
refactor: move cycle_frequency variable outside for loop
BelhsanHmida Feb 18, 2026
05f1b1c
fix: update predict_end calculation to use cycle_frequency instead of…
BelhsanHmida Feb 18, 2026
651b069
fix: use default value for probabilistic in ForecasterParametersSchem…
BelhsanHmida Feb 18, 2026
d6cbeea
chore: remove unused import and run pre-commit
BelhsanHmida Feb 18, 2026
95be4f4
feat(test): update test case to only one day of prediction
BelhsanHmida Feb 18, 2026
4809f26
fix: add forecast_frequency to test params
BelhsanHmida Feb 18, 2026
169d019
chore: remove old commented out test case
BelhsanHmida Feb 18, 2026
1c4da97
dev: uncomment out test cases
BelhsanHmida Feb 18, 2026
6d1d2ac
chore: remove default value for probabilistic when calling with get
BelhsanHmida Feb 19, 2026
243f374
docs: update test case comment
BelhsanHmida Feb 20, 2026
ce811ad
feat: calculate pred start date from end date and duration
BelhsanHmida Feb 20, 2026
10fdb65
feat: remove planning horizon from forecast frequency calculation and…
BelhsanHmida Feb 20, 2026
5cbc5f4
fix(tests): updates test cases
BelhsanHmida Feb 20, 2026
02331b8
docs: annotate case 7
Flix6x Feb 20, 2026
f6ca2b7
fix(test): update forecast_frequency in tests
BelhsanHmida Feb 20, 2026
0a64291
docs: annotate case 8
Flix6x Feb 20, 2026
58c7ded
Merge remote-tracking branch 'origin/dev/new-forecasting-defaults' in…
Flix6x Feb 20, 2026
06773c3
docs: enumerate remaining test cases
Flix6x Feb 20, 2026
3a9790d
fix(tests): add start-predict-date to case 3
BelhsanHmida Feb 20, 2026
3d9410a
docs: clarify case 0
Flix6x Feb 20, 2026
d8b46e4
Merge remote-tracking branch 'refs/remotes/origin/dev/new-forecasting…
Flix6x Feb 20, 2026
632cd84
docs: add comment
BelhsanHmida Feb 20, 2026
7bc7130
docs: check retraining-frequency in case 1
Flix6x Feb 20, 2026
0fcc6cd
docs: enumerate defaults and choices
Flix6x Feb 20, 2026
a03452c
docs: add docstring
BelhsanHmida Feb 20, 2026
f942e5c
fix: case 2
Flix6x Feb 20, 2026
c472fff
docs: explain case 3 slightly better
Flix6x Feb 20, 2026
bc38e7a
feat: set load_default for the retrain-frequency and make it independ…
Flix6x Feb 20, 2026
6427951
feat: base the number of cycles on the retrain-frequency and the fore…
Flix6x Feb 20, 2026
1322ce0
refactor: // guarantees an int already
Flix6x Feb 20, 2026
98a8274
delete: validator no longer appropriate
Flix6x Feb 20, 2026
f1f085d
feat: raise in case of explicitly setting inconsistent variables that…
Flix6x Feb 20, 2026
11f89e1
feat: check retrain-frequency explicitly
Flix6x Feb 20, 2026
784df43
docs: explain number to devs
Flix6x Feb 20, 2026
d13f3d4
style: black
Flix6x Feb 20, 2026
3e1503e
feat: update test expectations and add another test case
Flix6x Feb 20, 2026
57d5887
fix: only update default predict-period in case a forecast-frequency …
Flix6x Feb 20, 2026
187f1b3
delete: obsolete variable
Flix6x Feb 20, 2026
acfa1ba
fix: we are now guaranteed one cycle, and it is allowed to be smaller…
Flix6x Feb 20, 2026
b82f0ef
fix: counter in train_predict_params starts at 1
Flix6x Feb 20, 2026
8ac9666
dev: better error messages
Flix6x Feb 20, 2026
d0f0bf6
fix: update test case that tries to get two cycles out of the API
Flix6x Feb 20, 2026
048eb73
fix: expose forecast-frequency to API users
Flix6x Feb 20, 2026
99b48e7
docs: update forecast-frequency default description
Flix6x Feb 20, 2026
a2e69eb
docs: add comment
BelhsanHmida Feb 20, 2026
89f46c9
docs: add docstring
BelhsanHmida Feb 20, 2026
a1b230f
docs: update comment for selecting a default predict-period
Flix6x Feb 20, 2026
4bbe0bd
Merge remote-tracking branch 'origin/dev/new-forecasting-defaults' in…
Flix6x Feb 20, 2026
cab3c77
fix: remove code after merge conflict
Flix6x Feb 20, 2026
f7dd443
style: move flake8 noqa
Flix6x Feb 20, 2026
b682c0e
test(docs) update test comments
BelhsanHmida Feb 20, 2026
638517d
feat: move retrain-frequency to config
Flix6x Feb 20, 2026
74cde4f
Merge remote-tracking branch 'refs/remotes/origin/dev/new-forecasting…
Flix6x Feb 20, 2026
732469e
feat(test): uncomment and fix dates params
BelhsanHmida Feb 20, 2026
b507d79
feat: ensure a retrain-frequency of at least 1 hour
Flix6x Feb 20, 2026
b12f2df
fix: n_cycles now determined outside of schemas
Flix6x Feb 20, 2026
ae24461
fix: update test coverage of ForecasterParametersSchema
Flix6x Feb 20, 2026
61329d1
dev: partial fix for failing test
Flix6x Feb 20, 2026
0d75075
Merge remote-tracking branch 'origin/dev/new-forecasting-defaults' in…
Flix6x Feb 20, 2026
027913a
fix: test_missing_data_logs_warning
Flix6x Feb 20, 2026
f7e8c75
fix: test_train_period_capped_logs_warning
Flix6x Feb 20, 2026
a689ad4
fix: test_trigger_and_fetch_forecasts
Flix6x Feb 20, 2026
ff98cc3
fix: remove sensor from documented payload (it's in the URI path alre…
Flix6x Feb 20, 2026
ea67caa
feat: move training timing fields from parameters to config
Flix6x Feb 21, 2026
3eba171
refactor: do not store any forecaster parameters by default
Flix6x Feb 21, 2026
342d6e5
refactor: move derivation of training period into class method, and a…
Flix6x Feb 21, 2026
c3291ae
refactor: simplify logic for deriving the training period
Flix6x Feb 21, 2026
de2256e
style: flake8
Flix6x Feb 21, 2026
ca193dd
chore: update openapi-specs.json
Flix6x Feb 23, 2026
6ba606a
fix: remove CLI-only fields from nested config, too
Flix6x Feb 23, 2026
6b08dfe
feat: remove end-date and training timing fields from API docs
Flix6x Feb 23, 2026
e1dd3fb
refactor: rename start-predict-date to start
BelhsanHmida Feb 23, 2026
0f31621
refactor: change start-date to train-start
BelhsanHmida Feb 23, 2026
a355761
refactor: change end-date to end
BelhsanHmida Feb 23, 2026
4f8eb27
refactor: update forecasting job JSON keys to match new naming conven…
BelhsanHmida Feb 23, 2026
7630e12
refactor: update forecasting trigger schema keys to match new naming …
BelhsanHmida Feb 23, 2026
fdcd8ca
refactor: update job metadata keys to match new naming conventions
BelhsanHmida Feb 23, 2026
2ddec9a
refactor: update forecasting job metadata keys to match new naming co…
BelhsanHmida Feb 23, 2026
8287355
refactor: update forecasting parameter keys to match new naming conve…
BelhsanHmida Feb 23, 2026
4976e37
refactor(test): update forecast job payload and job metadata keys to …
BelhsanHmida Feb 23, 2026
bfedd0d
doc: fix comment typo
BelhsanHmida Feb 23, 2026
b38f6eb
refactor(tests): update test cases to use 'end' and 'train-start' keys
BelhsanHmida Feb 23, 2026
57dd7de
chore: update field names in comments
BelhsanHmida Feb 23, 2026
073e9a8
refactor(tests): update date keys in test cases to match new naming c…
BelhsanHmida Feb 23, 2026
e374f63
style: black
Flix6x Feb 23, 2026
36b9a15
refactor(doc): update forecasting job example to use 'duration' inste…
BelhsanHmida Feb 23, 2026
4d58440
refactor(api): update forecasting trigger example to use 'duration' i…
BelhsanHmida Feb 23, 2026
9f16d56
feat: remove max-forecast-horizon field from API docs
Flix6x Feb 23, 2026
1054e3d
docs: fix typo
Flix6x Feb 23, 2026
ebe85fa
chore: update openapi-specs.json
Flix6x Feb 23, 2026
93c25e0
docs: clarify mention of planning horizon in forecast duration field
Flix6x Feb 23, 2026
6570332
docs: clarify use case for forecast-frequency field
Flix6x Feb 23, 2026
28a4883
feat: expose duration field to the CLI
Flix6x Feb 23, 2026
fd0736f
docs: capitalize start of sentence
Flix6x Feb 23, 2026
a81acb7
fix: (CLI) description of end field
Flix6x Feb 23, 2026
39d250e
style: flake8
Flix6x Feb 23, 2026
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: 2 additions & 3 deletions documentation/tut/forecasting_scheduling.rst
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,8 @@ There are two ways to queue a forecasting job:
.. code-block:: json

{
"start_date": "2025-01-01T00:00:00+00:00",
"start_predict_date": "2025-01-04T00:00:00+00:00",
"end_date": "2025-01-04T04:00:00+00:00"
"start": "2025-01-04T00:00:00+00:00",
"duration": "PT4H"
}

Example response:
Expand Down
22 changes: 19 additions & 3 deletions flexmeasures/api/common/schemas/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,18 @@
from marshmallow import Schema, fields

from flexmeasures.utils.doc_utils import rst_to_openapi
from flexmeasures.data.schemas.forecasting.pipeline import ForecastingTriggerSchema
from flexmeasures.data.schemas.forecasting.pipeline import (
ForecastingTriggerSchema,
TrainPredictPipelineConfigSchema,
)
from flexmeasures.data.schemas.sensors import (
SensorReferenceSchema,
VariableQuantityField,
VariableQuantityOpenAPISchema,
)


def make_openapi_compatible(schema_cls: Type[Schema]) -> Type[Schema]:
def make_openapi_compatible(schema_cls: Type[Schema]) -> Type[Schema]: # noqa: C901
"""
Create an OpenAPI-compatible version of a Marshmallow schema.

Expand All @@ -28,11 +31,24 @@ def make_openapi_compatible(schema_cls: Type[Schema]) -> Type[Schema]:
new_fields = {}
for name, field in schema_cls._declared_fields.items():

if schema_cls == ForecastingTriggerSchema:
if schema_cls in (ForecastingTriggerSchema, TrainPredictPipelineConfigSchema):
if "cli" in field.metadata and field.metadata["cli"].get(
"cli-exclusive", False
):
continue
if isinstance(field, fields.Nested):
nested_schema_cls = type(field.schema)
if nested_schema_cls is TrainPredictPipelineConfigSchema:
field_copy = fields.Nested(
make_openapi_compatible(nested_schema_cls),
metadata=field.metadata,
data_key=field.data_key,
many=field.many,
required=field.required,
allow_none=field.allow_none,
)
new_fields[name] = field_copy
continue

# Copy metadata, but sanitize description for OpenAPI
metadata = dict(getattr(field, "metadata", {}))
Expand Down
2 changes: 1 addition & 1 deletion flexmeasures/api/v3_0/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ def create_openapi_specs(app: Flask):
# Explicitly register OpenAPI-compatible schemas
schemas = [
("FlexContextOpenAPISchema", flex_context_schema_openAPI),
("forecaster_parameters_schema_openAPI", forecasting_trigger_schema_openAPI),
("forecasting_trigger_schema_openAPI", forecasting_trigger_schema_openAPI),
("UserAPIQuerySchema", UserAPIQuerySchema),
("AssetAPIQuerySchema", AssetAPIQuerySchema),
("AssetSchema", AssetSchema),
Expand Down
30 changes: 21 additions & 9 deletions flexmeasures/api/v3_0/sensors.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,17 @@
partial_sensor_schema = SensorSchema(partial=True, exclude=["generic_asset_id"])

# Create ForecasterParametersSchema OpenAPI compatible schema
forecasting_trigger_schema_openAPI = make_openapi_compatible(ForecastingTriggerSchema)
EXCLUDED_FORECASTING_FIELDS = [
# todo: hide these in the config schema instead
# "train_period",
# "max_training_period",
"sensor_to_save",
]
forecasting_trigger_schema_openAPI = make_openapi_compatible(ForecastingTriggerSchema)(
# partial=True,
exclude=EXCLUDED_FORECASTING_FIELDS
+ ["sensor"],
)


class SensorKwargsSchema(Schema):
Expand Down Expand Up @@ -1524,7 +1534,10 @@ def get_status(self, id, sensor):

@route("/<id>/forecasts/trigger", methods=["POST"])
@use_args(
ForecastingTriggerSchema(),
ForecastingTriggerSchema(
# partial=True,
exclude=EXCLUDED_FORECASTING_FIELDS,
),
location="combined_sensor_data_description",
as_kwargs=True,
)
Expand Down Expand Up @@ -1558,11 +1571,10 @@ def trigger_forecast(self, id: int, **params):
required: true
content:
application/json:
schema: forecaster_parameters_schema_openAPI
schema: forecasting_trigger_schema_openAPI
example:
start-date: "2026-01-01T00:00:00+01:00"
start-predict-date: "2026-01-15T00:00:00+01:00"
end-date: "2026-01-17T00:00:00+01:00"
start: "2026-01-15T00:00:00+01:00"
duration: "P2D"
responses:
200:
description: PROCESSED
Expand Down Expand Up @@ -1609,7 +1621,7 @@ def trigger_forecast(self, id: int, **params):
forecaster = get_data_generator(
source=None,
model=model,
config={},
config=parameters.pop("config", {}),
save_config=True,
data_generator_type=Forecaster,
)
Expand Down Expand Up @@ -1780,8 +1792,8 @@ def get_forecast(self, id: int, uuid: str, sensor: Sensor, job_id: str):
data_source = get_data_source_for_job(job, type="forecasting")

forecasts = sensor.search_beliefs(
event_starts_after=job.meta.get("start_predict_date"),
event_ends_before=job.meta.get("end_date"),
event_starts_after=job.meta.get("start"),
event_ends_before=job.meta.get("end"),
source=data_source,
most_recent_beliefs_only=True,
use_latest_version_per_event=True,
Expand Down
30 changes: 14 additions & 16 deletions flexmeasures/api/v3_0/tests/test_forecasting_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
import isodate
import pytest
from flask import url_for
from flexmeasures.data.services.scheduling import (
get_data_source_for_job,
)

from rq.job import Job
from flexmeasures.utils.job_utils import work_on_rq
from flexmeasures.api.tests.utils import get_auth_token
Expand Down Expand Up @@ -35,18 +33,21 @@ def test_trigger_and_fetch_forecasts(

# Trigger job
payload = {
"start-date": "2025-01-01T00:00:00+00:00",
"start-predict-date": "2025-01-05T00:00:00+00:00",
"end-date": "2025-01-05T02:00:00+00:00",
"start": "2025-01-05T00:00:00+00:00",
"end": "2025-01-05T02:00:00+00:00",
"max-forecast-horizon": "PT1H",
"retrain-frequency": "PT1H",
"forecast-frequency": "PT1H",
"config": {
"train-start": "2025-01-01T00:00:00+00:00",
"retrain-frequency": "PT1H",
},
}

trigger_url = url_for("SensorAPI:trigger_forecast", id=sensor_0.id)
trigger_res = client.post(
trigger_url, json=payload, headers={"Authorization": token}
)
assert trigger_res.status_code == 200
assert trigger_res.status_code == 200, trigger_res.json

trigger_json = trigger_res.get_json()
wrap_up_job = app.queues["forecasting"].fetch_job(trigger_json["forecast"])
Expand Down Expand Up @@ -77,15 +78,15 @@ def test_trigger_and_fetch_forecasts(
payload["sensor"] = sensor_1.id

# Run pipeline manually to compute expected forecasts
pipeline = TrainPredictPipeline()
pipeline = TrainPredictPipeline(config=payload.pop("config", {}))
pipeline.compute(parameters=payload)

# Fetch forecasts for each job
for job_id in job_ids:

fetch_url = url_for("SensorAPI:get_forecast", id=sensor_0.id, uuid=job_id)
res = client.get(fetch_url, headers={"Authorization": token})
assert res.status_code == 200
assert res.status_code == 200, res.json

data = res.get_json()

Expand All @@ -103,14 +104,11 @@ def test_trigger_and_fetch_forecasts(
assert isinstance(api_forecasts, list)
assert len(api_forecasts) > 0

# Identify which data source wrote these beliefs
data_source = get_data_source_for_job(job, type="forecasting")

# Load only the latest belief per event_start
forecasts_df = sensor_1.search_beliefs(
event_starts_after=job.meta.get("start_predict_date"),
event_ends_before=job.meta.get("end_date") + sensor_1.event_resolution,
source=data_source,
event_starts_after=job.meta.get("start"),
event_ends_before=job.meta.get("end"),
source_types=["forecaster"],
most_recent_beliefs_only=True,
use_latest_version_per_event=True,
).reset_index()
Expand Down
3 changes: 2 additions & 1 deletion flexmeasures/data/models/forecasting/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@ def _clean_parameters(self, parameters: dict) -> dict:
"output-path",
"sensor-to-save",
"as-job",
"n_cycles", # Computed internally, still uses snake_case
"m_viewpoints", # Computed internally, still uses snake_case
"sensor",
]

for field in fields_to_remove:
Expand Down
Loading