Run reports via CLI and API - #2298
Conversation
Automations are recurring tasks (for now: computing forecasts) defined per asset. The recurrence is defined by a cron string, and the work to be done is defined by a data generator (linked through a data source) together with the parameters to call it with. Includes a migration for the new table, and new dependencies on croniter (cron matching/validation) and cron-descriptor (natural-language recurrence descriptions). Part of #2288 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rbix8k1JfeUWNXEmHEZVpX
- `flexmeasures add automation` creates an automation (active by default), validating the forecast parameters with the forecast parameter schema and storing the forecaster config on a data source. - `flexmeasures edit automation` edits the name, recurrence (cron string) or activation status. - `flexmeasures delete automation` deletes an automation. - All three record their events in the asset's audit log. - `flexmeasures jobs run-automations` queues jobs for all automations due this minute (to be run once per minute, e.g. via cron), with a Redis-based guard against duplicate runs within the same minute. Part of #2288 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rbix8k1JfeUWNXEmHEZVpX
Data generators can now be told how their queued jobs got triggered (via the CLI, the API or an automation), and the train-predict pipeline stores this on the jobs as meta data. The asset's status page shows it in a new 'Created Via' column of the jobs table. Part of #2288 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rbix8k1JfeUWNXEmHEZVpX
GET /api/v3_0/assets/<id>/automations lists the automations defined on an asset (without generator and parameters details). GET /api/v3_0/assets/<id>/automations/<automation_id> additionally provides the parameters, data generator info and counts of recently created jobs per job status. Both are documented in the OpenAPI specs. Part of #2288 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rbix8k1JfeUWNXEmHEZVpX
/assets/<id>/automations shows the asset's automations in a tabbed view (schedules and reports tabs are prepared but deactivated), with per-row details (parameters, data generator, job counts) loaded asynchronously into a modal. The page is linked in the breadcrumbs dropdown and links to the status page, where recent jobs are listed. Part of #2288 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rbix8k1JfeUWNXEmHEZVpX
Part of #2288 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rbix8k1JfeUWNXEmHEZVpX
Part of #2288 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rbix8k1JfeUWNXEmHEZVpX
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rbix8k1JfeUWNXEmHEZVpX
CI runners have no locale set (POSIX), which made cron-descriptor render 'At 06:00' while dev environments with an en_US-style locale rendered 'At 06:00 AM'. Request 24-hour format explicitly so the description is deterministic across environments. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pxkeq64jtENY7fiWjwUsVS
- Escape automation names (and other user-controlled strings) in the Automations page and the status page's jobs table, closing two stored HTML/script injection sinks. - Wipe parameter state on the (possibly shared) cached data generator before each automation run, so automations sharing a generator data source don't pollute each other's runs. - Count automation job stats under the forecast target sensor(s) from the automation's parameters, which may belong to a different asset. - Release the per-minute Redis guard when a run fails, so a retry within the same minute can still queue jobs. - Return 404 (as documented) for nonexistent automation ids on the detail endpoint, and check permissions on the asset, so automation ids can no longer be enumerated across accounts via 403-vs-422 differences. - Use ondelete=SET NULL for the generator FK: deleting a data source no longer silently deletes automations. - Delegate Automation ACL to the asset's ACL instead of duplicating it. - Extract the config/parameters assembly shared by `add forecasts` and `add automation` into a helper (which no longer drops falsy config values). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rbix8k1JfeUWNXEmHEZVpX
Completes the previous commit, whose staged files were dropped by an interrupted pre-commit run: template escaping, shared-generator state reset, job stats under target sensors, Redis guard release on failure, 404 for nonexistent automations, SET NULL generator FK, ACL delegation, and the shared CLI config/parameters assembly helper. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rbix8k1JfeUWNXEmHEZVpX
The scheduling job creators accept an optional trigger dict (stored as job meta data), like the forecasting pipeline already does. The API trigger endpoint records origin API; the CLI and automations follow in the next commit. The status page's 'Created Via' column picks this up automatically. Part of #2288 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rbix8k1JfeUWNXEmHEZVpX
Automations now also support the 'schedules' type: - `flexmeasures add automation --type schedules` validates the parameters as a schedule trigger message (per the AssetTriggerSchema, as accepted by the API trigger endpoint, without the asset id). The schedule 'start' may be omitted, in which case each run schedules from the run time (floored to the message's resolution, if given) — a fixed start draws a warning. - The runner dispatches schedules automations to the same job creators as the API trigger endpoint (sequential or simultaneous), recording trigger meta data (origin automation) on the queued jobs; `flexmeasures add schedule --as-job` now records origin CLI. - Job stats for schedules automations are counted from the scheduling job cache (asset-level wrap-up jobs and per-sensor device jobs). - The UI automations page's Schedules tab is now enabled, with automations filtered by type per tab. Part of #2288 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rbix8k1JfeUWNXEmHEZVpX
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rbix8k1JfeUWNXEmHEZVpX
- New endpoints on assets: POST /automations (create, validating parameters by automation type), PATCH /automations/<id> (name, cron string, activation status) and DELETE /automations/<id>. Managing automations requires the same principals that may delete the asset (account admins and consultants). - The UI automations page gets a 'New automation' modal and per-row (de)activate and delete actions, shown to users with management rights. - Creation, update and deletion logic (incl. audit log records) moved into the automations service, shared by the CLI commands and the API endpoints. Part of #2288 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rbix8k1JfeUWNXEmHEZVpX
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rbix8k1JfeUWNXEmHEZVpX
- Activate the reporting queue (it was prepared but commented out), including worker help texts and queue cleanup. - Reporters accept as_job: a job is queued (with trigger meta data) that rebuilds the reporter from its data source, computes the report and saves the results to the database. - `flexmeasures add report --as-job` queues such a job; reporting jobs show up in the asset's jobs overview (status page and API). Part of #2288 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rbix8k1JfeUWNXEmHEZVpX
Automations can now compute reports on a recurring basis: - `flexmeasures add automation --type reports --reporter <class>` stores the reporter config on a data source (steady across runs, so all report results attribute to the same source) and validates the report parameters. - The report window resolves freshly on each run: 'start-offset'/'end-offset' fields (comma-separated Pandas offsets, applied to the run time in the first output sensor's timezone) express a rolling window, and without any timing fields the window defaults to the last cron period (from the previous cron fire time until the run time). Absolute start/end still work, but draw a warning. - The API creation field 'forecaster' is generalized to 'generator' (also accepting reporter classes), and the UI's New automation modal gains data generator and config fields; the Reports tab is now enabled. Part of #2288 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rbix8k1JfeUWNXEmHEZVpX
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rbix8k1JfeUWNXEmHEZVpX
Each automation run is recorded in Redis; a report automation without timing fields then reports on the period since its actual last run, falling back to the last cron period when no last run is known (e.g. on the first run, or after a Redis flush). This gives gapless coverage even when runs are missed. Part of #2288 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rbix8k1JfeUWNXEmHEZVpX
Consolidates the shared automations concept (model, lifecycle, runner deployment, provenance) into documentation/features/automations.rst, with the per-feature pages linking to it and keeping only their type-specific parameter semantics. Part of #2288 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rbix8k1JfeUWNXEmHEZVpX
Add POST /api/v3_0/assets/<id>/reports/trigger, which queues a one-off reporting job on the reporting queue, mirroring how forecasts and schedules are triggered via the API. The job status can be polled via the generic GET /api/v3_0/jobs/<uuid> endpoint. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rbix8k1JfeUWNXEmHEZVpX
Documentation build overview
132 files changed ·
|
- API automation creation now checks that the caller may read every sensor referenced in the parameters/config and record data on the sensors the automation writes to, closing a cross-account data read/write hole. - `add report --as-job` implies --save-config (the worker rebuilds the reporter from its data source, so jobs without stored config always crashed). - Report automation parameters are validated with the chosen reporter's own parameters schema, not the base schema. - Invalid start-offset/end-offset strings are rejected at creation instead of being silently skipped at run time (which yielded empty report windows). - run_report_job wipes the shared cached reporter's parameter state, like the automation runner already did, so consecutive jobs in one worker process don't pollute each other. - Default report windows now anchor to the end of the last *successfully* covered window, recorded by the reporting job upon success — failed jobs no longer create permanent reporting gaps, and the enqueue-time minute-rollover gap is gone (the recorded anchor is the window end itself). - The cron-period fallback window is computed in the platform timezone, matching how the runner decides when automations fire. - Job stats for schedules automations also scan flex-model device sensors (which may belong to child assets), so failed per-device jobs show up. - The trigger provenance kwarg is excluded from the job cache hash, so identical schedule requests from different origins dedupe again. Part of #2288 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rbix8k1JfeUWNXEmHEZVpX
The report trigger endpoint now applies the same sensor-access guard as automation creation: the caller must be able to read every input sensor and record data on every output sensor. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rbix8k1JfeUWNXEmHEZVpX
…essage format PR #2303 makes click report the validation message rather than the offending value, which changes the exact wording of this error. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rbix8k1JfeUWNXEmHEZVpX
Merge current main, resolve the shared forecasting and documentation changes, regenerate the lockfile, and move the automation migration after the current migration head. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Reject cron expressions with seconds, year fields, or aliases because the automation runner executes once per minute. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Test valid five-field expressions and reject unsupported seconds, year, and alias formats. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Describe per-automation timezone selection and the successful-coverage model used by recurring reports. The documentation now distinguishes the claimed cron occurrence from delayed runner time and explains how first runs, daylight-saving transitions, and out-of-order worker completions determine report windows. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Commit the fixture-owned sensor setup after rejected report automation requests so the shared API test database does not retain an idle transaction during teardown. This keeps the complete automation API module deterministic while preserving assertions that no unauthorized automation was created. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Context: - The report trigger branch was based on the pre-review report automation stack and no longer compiled after the centralized reporter sensor checks landed. Change: - Merge the updated report automation branch, preserve both changelog histories, and adapt the trigger endpoint to the shared sensor resolution and current asynchronous API conventions. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Context: - Report triggers can name inputs in both request parameters and reporter configuration, while output sensors must stay within the asset selected by the endpoint. Change: - Isolate report-trigger mutations with a fresh database and cover private parameter inputs, private ProfitOrLossReporter price sensors, sibling outputs, rejected transaction cleanup, queue side effects, and the canonical accepted-job response. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Context: - The report trigger endpoint now follows the same dependency and output-scope guarantees as report automations and returns the shared asynchronous job representation. Change: - Document input and output sensor authorization, the asset-subtree boundary, and the canonical accepted-job response for one-off report triggers. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
nhoening
left a comment
There was a problem hiding this comment.
This is nice, and my opinion is that it can be pulled ahead and already tested by itself - one-off reporting jobs are valuable on their own, for instance to make things like our HEMS script more useful. They also do not rely on db migrations, thus are an easy addition we might include in v1.0.0 (or v1.1.0) so we can test them in practice before automations are rolled out in a later version.
Automations might also be a complex feature which we'll be hesitant to roll out quickly - by prioritizing this PR, we de-couple this.
But: we'd need a few smaller things that are introduced in (currently) lower-stacked PRs.
So my proposal (next to some smaller suggestions, see below) is to extract a few things from these PRs into this PR, which are not automation-specific, but enable to run reporters as jobs. The scope of this PR then becomes: "one-off reports as background jobs, their API endpoint, and the authorization machinery needed to expose that safely".
All these changes are 1-2 lines or extracting helper functions (maybe removing a few automation logic bits in them):
- Register the
reportingRedis queue (#2297) - extract
create_reporting_job()andrun_report_job()(#2297) Reporter._compute(..., as_job=True)(#2297)DataGenerator.input_sensors,output_sensorsand_resolve_sensors()(#2290)asset_is_in_subtree()(#2290)check_sensor_access()(#2294)resolve_data_generator_sensors()(#2294)Reporter.input_sensorsandoutput_sensors(#2297)ProfitOrLossReporter.input_sensorsfor price dependencies in config (#2297)- Require non-empty report inputs and outputs (#2297)
- Worker/deployment documentation for the reporting queue in documentation/host/queues.rst (#2297)
The genuinely important integration points are:
- Persisting the reporter configuration before queueing so the worker can reconstruct it.
- Clearing cached parameters inside the worker.
- Checking config-based input sensors, not only parameter inputs.
- Starting a production worker for the reporting queue.
- Running an end-to-end test that confirms the worker stores report beliefs.
Next to this scope extension using exiting code, here are a few other small things I found wanting:
Rate-limit documentation is now inaccurate.
The endpoint uses @limit_triggers(), so reports consume the same shared trigger quota as schedules and forecasts. However, the plan CLI help and configuration docs still say “schedule or forecast.” Those should also mention reports. I would also add AssetAPI.trigger_report to the rate-limit registration test.
The PR description is stale.
It still describes 200 PROCESSED, a report response field, and output ownership as “not hard-enforced.” The implementation correctly returns 202 ACCEPTED with canonical job/job-url fields and does enforce the output subtree.
Optional test improvement.
The endpoint test stops after asserting that a job was queued. The base stack tests the reporting worker separately, so this is not a blocker, but one API-path test that runs the worker and verifies output beliefs would be much better to test what this PR really is doing, in effect.
Maybe we could Adapt test_run_report_automation from #2297; rewrite it without an automation
Context: - One-off reports were computed only in the CLI process, so the API could not safely hand expensive report work to a worker. Change: - Add reporter dependency metadata, a dedicated RQ queue and worker service, CLI queueing support, job provenance, status-page tracking and configurable reporting timeouts. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Context: - The report trigger endpoint must stand on main without the automation service while preventing callers from reading or writing sensors outside their permissions and requested asset subtree. Change: - Add the report request envelope, reusable data-generator authorization helpers, subtree validation and a rate-limited endpoint that validates and authorizes before persisting a reporter source or queueing work. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Context: - Report jobs cross API authorization, RQ serialization and worker persistence boundaries where code inspection alone cannot establish correctness. Change: - Cover CLI queueing, canonical API responses, trigger registration, queue timeouts, invalid requests, cross-organisation and config-sensor access, output subtree enforcement, side-effect-free rejection and API-to-worker belief storage. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Context: - Hosts and API users need to know how to queue, process and authorize reports after moving computation out of the request or CLI process. Change: - Document the reporting worker queue, CLI and API entry points, sensor permission model, shared trigger rate limit and generated OpenAPI contract. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Context: - The standalone report-job story adds both a public API endpoint and a CLI option in PR #2298. Change: - Record the new background-report capability and its canonical asynchronous response in the main, API and CLI changelogs. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Context: - Specialized reporter schemas accidentally made inherited input or output requirements optional, allowing malformed API and CLI requests to reach the reporting queue and persist unusable data sources. Change: - Preserve required dataflow fields in specialized schemas and validate the canonical reporter input/output contract before accessing or committing a reporting data source. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Context: - Missing specialized reporter inputs or outputs previously produced an API 500, queued doomed jobs, or left reporter data sources behind. Change: - Reproduce malformed Aggregator and Profit-or-loss requests through schemas, the API and CLI, and assert that service-level validation runs before persistence or queueing. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Context: - The report trigger work was rebuilt on main, while the protected remote branch retained its former stacked history and could not be rewritten. Change: - Join the histories without restoring the superseded stacked files, allowing the standalone implementation to be pushed through a normal fast-forward update. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Context: - Reporting queues expose the same configurable timeout mechanism as other job queues, but report jobs overrode it with a fixed one-hour value. Change: - Leave the job timeout unset during creation so RQ applies the reporting queue's configured default when it enqueues the job. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Context: - Asset status data labelled every failed job as a scheduling failure, including newly visible reporting jobs. Change: - Build the failure label from the job's queue so scheduling, forecasting and reporting failures are identified accurately. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Context: - The bundled Docker worker did not listen to the reporting queue, so accepted CLI and API report jobs could remain queued indefinitely in the standard setup. Change: - Add the reporting queue to the worker process started by Docker Compose. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Context: - The report trigger advertised a canonical asynchronous response, but its generated OpenAPI operation documented only the 202 status code. Change: - Describe the required status, message, job and job-url fields and provide a concrete accepted-response example in the generated API contract. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Context: - Host documentation omitted reporting from configurable timeout and installation guidance, and its combined worker example left shell pipe characters unquoted. Change: - Document reporting timeout configuration, include reporting in worker setup commands and quote the multi-queue value so the example executes as intended. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Context: - Queue registration tests did not prove that created report jobs inherited the configured timeout, and status tests did not exercise reporting failures. Change: - Assert the effective timeout on a CLI-created report job and verify that failed reporting jobs retain their queue-specific status label. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
|
@nhoening Thanks, I followed your proposal and made #2298 standalone on It now includes the reporting queue and worker, I also added defensive validation for non-empty reporter inputs and outputs before The I have also updated the PR description to reflect the 202 |
Description
Closes #2426.
This PR makes one-off reports available as background jobs, independently from report automations. It brings the non-automation-specific reporting infrastructure forward from #2297, #2290 and #2294 so the CLI and API functionality can be reviewed, released and tested on its own.
reportingRQ queue with configurable job timeouts and worker support.flexmeasures add report --as-jobfor queueing reports from the CLI.POST /api/v3_0/assets/<id>/reports/triggerfor queueing reports through the API.create-childrenaccess to every output sensor.documentation/changelog.rst.The API request envelope is validated first. The selected reporter then validates
configandparameterswith its concrete schemas. FlexMeasures resolves and authorizes all declared dependencies before creating the reporting job.Look & Feel
CLI
Queue a report instead of computing it in the CLI process:
Process queued reports with a reporting worker:
flexmeasures jobs run-worker --queue reporting--as-jobstores report results in the database and therefore cannot be combined with--dry-runor--output-file.API
The endpoint is included in the generated OpenAPI specification and can be tried interactively through Swagger UI:
http://127.0.0.1:5000/api/v3_0/docs.POST /api/v3_0/assets/{id}/reports/trigger.42.{ "reporter": "PandasReporter", "config": { "required_input": [{"name": "one"}, {"name": "two"}], "required_output": [{"name": "sum"}], "transformations": [ { "df_input": "one", "method": "add", "args": ["@two"], "df_output": "sum" } ] }, "parameters": { "input": [ {"name": "one", "sensor": 12}, {"name": "two", "sensor": 13} ], "output": [{"name": "sum", "sensor": 14}], "start": "2023-04-10T00:00:00+00:00", "end": "2023-04-10T10:00:00+00:00" } }Select Execute. Swagger UI shows the generated request and the server response. A valid request returns HTTP
202 Acceptedusing the canonical asynchronous-job response:{ "status": "ACCEPTED", "message": "Request has been accepted for processing.", "job": "<job UUID>", "job-url": "/api/v3_0/jobs/<job UUID>" }The response is defined by the generated OpenAPI documentation. The caller can follow
job-urlor useGET /api/v3_0/jobs/{uuid}in Swagger UI until the job finishes. The queued job records{"origin": "API"}in its metadata.How to test
Automated tests
The focused suite covers:
Latest local result:
119 passed, 22 warnings.Manual test
startandendtimes.flexmeasures add report ... --as-joband confirm a job is added to thereportingqueue.flexmeasures jobs run-worker --queue reportingand confirm the job finishes and beliefs are stored on the output sensor.jobandjob-url.GET /api/v3_0/jobs/<job UUID>and confirm the job moves from queued to finished.Further Improvements
Recurring report automations, automation UI work and prepared report templates remain outside this standalone PR and continue in their respective follow-up work.
Related Items
Sign-off