Automations - first roundtrip for forecasts - #2290
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
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rbix8k1JfeUWNXEmHEZVpX
Documentation build overview
111 files changed ·
|
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
…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>
Keep the per-minute Redis guard after failures because an attempt may already have queued some forecast jobs. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Verify that retrying a failed partial queueing attempt does not create duplicate jobs. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Convert YAML dates to ISO strings, accept empty files, and report non-object config or parameter files as usage errors. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Test YAML dates and timestamps, empty files, and invalid top-level list values for automation options. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Hide automation names and IDs from asset job responses when the current user cannot read the source automation. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Verify inaccessible automation provenance is redacted while authorized callers still receive the full identity. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Show a persistent API error instead of presenting failed automation requests as an empty list. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Check that the automations page renders the warning target and hides the table when loading fails. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Avoid interpreting cron wildcard asterisks as RST italic markup when generating OpenAPI documentation. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
|
I checked the current behavior and considered the trade-offs.
The current runner evaluates cron expressions in the global I recommend storing an IANA timezone per automation, defaulted at creation to This requires persistent scheduling state, so I suggest handling it in a follow-up issue.
Retrying immediately is unsafe because an exception may occur after some jobs have already been queued. Until runs and jobs have durable occurrence IDs and idempotency protection, the current at-most-once guard is the safer milestone-one behavior. I suggest a follow-up issue for durable automation-run records, partial-failure states and safe retries.
The current CLI only warns about a target sensor on another asset. I recommend rejecting output sensors that do not belong to the automation asset or one of its descendants. Arbitrary cross-organisation output targets should not be allowed. Regressors are inputs and could remain cross-asset when readable, but output sensors need the appropriate write boundary. Run statistics should primarily belong to the automation asset; individual jobs can remain visible under their target sensor subject to normal permissions. I think this validation belongs in the current PR because it protects a permission boundary.
I verified that beliefs and annotations reference their Automations currently differ: I recommend matching the existing belief/annotation policy: make Proposed scope: address target authorization and generator deletion integrity here; create follow-ups for timezone/catch-up and safe retry semantics. Does that split look right? |
Yes, that looks right. For 1 and 2, please open the issues and cross-reference them. For 3 and 4, remarks below. 3.
|
Bring the forecast automation branch up to date with current main while preserving both branches' CLI, API, documentation, dependency lock, and generated OpenAPI changes. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Require every automation to reference a data generator and prevent deleting a data source while an automation still depends on it, matching the retention policy for belief and annotation sources. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Verify referenced generators cannot be deleted, generator references cannot be cleared, and automation API fixtures always use valid generators. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Allow forecast output only on the automation asset or its descendants and revalidate that relationship before every scheduled run, including explicit sensor-to-save targets. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Cover same-asset, child, grandchild, ancestor, and unrelated output targets, explicit sensor-to-save behavior, and runtime revalidation after an asset is moved. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Document output-sensor scope, runtime relationship checks, and the requirement to retain a generator while its automation exists. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Join the automation and main Alembic branches so installations have a single database upgrade target. Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
|
Thanks for the detailed response, I pushed the changes addressing the agreed current-PR scope. Follow-up issuesI created separate follow-up issues for:
Until safe retries have durable occurrence IDs and idempotency protection, the runner retains its at-most-once guard after a failed attempt. Output target scopeThe pushed changes now:
One ownership question remains. Manual testing confirmed that an automation owned by organisation A can currently target a descendant sensor owned by unrelated organisation B, even when A has no consultancy relationship with B. The topology check passes because the target is a descendant, but topology itself grants no permissions. For unattended execution, should authority come from:
I recommend the first option. The automation belongs to the asset/organisation and should not depend on whether its original creator remains employed or keeps the same role. Current consultancy relationships should be checked again on every run, so revoking access immediately prevents subsequent writes. Does that match the intended permissions model? Once confirmed, I will add the corresponding service-level permission check and cross-organisation regression test. Generator lifecycleThe pushed changes also make generator handling consistent with beliefs and annotations:
|
Context: - Review of #2290 asked for a data generator property listing the sensors it reads from and writes to, so that automations can link to those sensors (and, later, check the creating user's permissions on them) Change: - Added input_sensors and output_sensors to DataGenerator (empty by default), implemented for Forecaster from its regressors and target sensor - Added the same properties to Automation, resolved from its data generator configured with the automation's own parameters - Added get_automations_feeding_sensor to look up automations by output sensor Signed-off-by: F.N. Claessen <felix@seita.nl>
…unning it Context: - 'flexmeasures jobs run-automations' logged 'Starting Train-Predict Pipeline' for every automation, while it only queues the cycles as jobs Change: - Log that line at debug level when running with as_job, where the workers running the cycles log their own start Signed-off-by: F.N. Claessen <felix@seita.nl>
…hat --source already determines Context: - Review of #2290: --cron should not be required, and --forecaster/--config are redundant with --source, whose data generator attributes already hold both (get_data_generator silently ignores them when a source is given) Change: - Default --cron to '0 0 * * *' (daily at midnight) - Abort when --source is combined with --forecaster, --config or any of the forecaster configuration options, naming the conflicting options Signed-off-by: F.N. Claessen <felix@seita.nl>
Context: - The automation details modal should link to the sensors an automation feeds Change: - Added input_sensors and output_sensors (id and name each) to GET /assets/<id>/automations/<automation_id> Signed-off-by: F.N. Claessen <felix@seita.nl>
Context: - The sensor page should be able to show the full record of a data source, including the attributes in which data generators store their configuration Change: - Added GET /sources/<id>, with the same access rules as listing sources - Let _serialize_source optionally include the attributes and unset fields Signed-off-by: F.N. Claessen <felix@seita.nl>
Context: - The specs are generated from the endpoint docstrings by a pre-commit hook Change: - Regenerated after adding the data source endpoint and the automation's input and output sensors Signed-off-by: F.N. Claessen <felix@seita.nl>
… sortable Context: - Review of #2290: the details modal should link to the sensors an automation feeds, with its data source pre-selected there, and the listing was not sortable Change: - Show the input and output sensors in the details modal, linking to /sensors/<id>?source=<generator id> - Enabled ordering, sorting the rendered columns on separate values (the ISO timestamp, the activation status and the cron string), newest first Signed-off-by: F.N. Claessen <felix@seita.nl>
Context: - Review of #2290: the sensor page should be able to show all details of a data source, and list the automations that write data to the sensor Change: - Added an info button next to the source selector, opening a modal with the full data source record - Pre-select the source given in the source query parameter, so that links from an automation land on its own source - List the automations feeding the sensor (those the user may read), linking to the automations page of their asset - Added user_can_read to the UI's permission helpers Signed-off-by: F.N. Claessen <felix@seita.nl>
Context: - New behaviour from the #2290 review needs regression coverage Change: - CLI: the daily default recurrence, the --source conflict, and an automation's input and output sensors - API: an automation without a data generator reports no sensors; the new data source endpoint, its access rules and its 404 - UI: the source query parameter reaches the page, and automations feeding a sensor are listed on it Signed-off-by: F.N. Claessen <felix@seita.nl>
Context: - The #2290 review changed user-facing CLI, API and UI behaviour Change: - Documented the daily default recurrence and reusing a forecaster via --source - Documented the links between automations and the sensors they feed - Added API change log entries for the automations and data source endpoints - Extended the changelog entry of #2290 Signed-off-by: F.N. Claessen <felix@seita.nl>
Signed-off-by: F.N. Claessen <felix@seita.nl> # Conflicts: # documentation/api/change_log.rst # documentation/changelog.rst # documentation/features/forecasting.rst # flexmeasures/ui/templates/assets/asset_automations.html
Context: - The merge combined endpoint docstring changes from both sides Change: - Regenerated the specs Signed-off-by: F.N. Claessen <felix@seita.nl>
…--source Context: - The guard added on this branch compared against the assembled config, which always holds the schema defaults of the list-valued options, so any use of --source was rejected Change: - Detect the conflicting options from click's parameter sources, so --source on its own works again while explicitly given configuration options still abort - Name the conflicting options in the error message Signed-off-by: F.N. Claessen <felix@seita.nl>
Context: - Listing the automations feeding a sensor sets up a data generator per candidate, which does not need to happen for every automation in the database Change: - Narrowed the candidates to automations on the sensor's asset or an ancestor, which is where an automation writing to it must live Signed-off-by: F.N. Claessen <felix@seita.nl>
Context: - Automations now always have a data generator, and the --source guard also covers the forecaster configuration options Change: - Assert the sensors an automation with a generator reads from and writes to - Cover a configuration option conflicting with --source Signed-off-by: F.N. Claessen <felix@seita.nl>
…elves Context: - Review of #2290 asked that automations administered through the UI (and hence the API) may only involve sensors the creating user has access to; account admin rights on the asset should not grant access to another account's sensors Change: - Work out the sensors an automation would read from and write to (forecasts: the sensor to forecast plus its regressors, and the sensor to save to; schedules: the flex-model's device sensors, and whatever the parameters refer to) - Require read access to the former and create-children (the permission for recording data through the API) on the latter, when creating via the API - The CLI creates automations without a user, and stays unrestricted Signed-off-by: F.N. Claessen <felix@seita.nl>
|
@BelhsanHmida — Felix went through this PR and we picked up his review points, so here is what changed since you last saw it. Could you review these commits, and then take the PR over again? What we contributed (on top of your branch, after merging it in):
One bug fix worth a closer look: the Note on the timezone review point: it is already covered by #2396 (which also adds the scheduling cursor, catch-up and DST handling), so we deliberately left it out here to avoid a second, conflicting migration. Heads-up for #2396: it will hit small conflicts in 🤖 Generated with Claude Code |
Description
First roundtrip for automations (milestone 1 of #2288): recurring tasks defined per asset — for now, computing forecasts.
Automationmodel (id,created_at,asset_id,type,name,cronstr,active,generator_id,parametersJSONB), with an__acl__that lets account admins and consultants update/delete, and a migration. New dependencies:croniter(cron matching/validation) andcron-descriptor(natural-language recurrence descriptions).flexmeasures add automation(active by default; validates parameters with the forecast parameter schema; stores the forecaster config on a data source),flexmeasures edit automation(name, cron string, activation status) andflexmeasures delete automation. All record to the asset's audit log.flexmeasures jobs run-automationsqueues forecasting jobs for all automations due this minute — to be run once per minute, e.g. via cron. A Redis-based guard prevents duplicate runs within the same minute.GET /assets/<id>/automations(list, without generator/parameters) andGET /assets/<id>/automations/<automation_id>(details incl. parameters, generator info and job counts per status), documented in the OpenAPI specs./assets/<id>/automationspage with a tabbed view (Schedules and Reports tabs prepared but deactivated), listing Name, Created At (human-readable, ISO string in title tag), Active, Recurrence (human-readable, cron string in title tag), plus per-row job counts and a Details modal loaded asynchronously. Added to the breadcrumbs dropdown; links to the status page.Look & feel
See the new Automations page under an asset's breadcrumbs dropdown.
How to test
Tests:
pytest flexmeasures/cli/tests/test_automations.py flexmeasures/api/v3_0/tests/test_automations_api.py flexmeasures/ui/tests/test_asset_crud.pyFurther Improvements
Per #2288: schedules and reports as automations, CRUD for automations in the UI (POST/PATCH/DELETE endpoints).
Closes #2288
🤖 Generated with Claude Code
https://claude.ai/code/session_01Rbix8k1JfeUWNXEmHEZVpX