Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
6184940
feat: reports can run as background jobs
Flix6x Jul 11, 2026
79e1609
feat: reports as automations
Flix6x Jul 11, 2026
5eac468
docs: changelog entry for reports as jobs and automations
Flix6x Jul 11, 2026
791fcf4
feat: anchor default report windows to the automation's actual last run
Flix6x Jul 11, 2026
0dca8fc
docs: add an Automations concept page
Flix6x Jul 11, 2026
176d800
fix: address stack code review findings
Flix6x Jul 11, 2026
881c9bd
feat(automations): integrate reports with the CRUD stack
BelhsanHmida Aug 12, 2026
292a0fa
fix(automations): authorize reporter sensor dependencies
BelhsanHmida Aug 12, 2026
8b76815
fix(reporting): require report inputs and outputs
BelhsanHmida Aug 12, 2026
82cb3bb
fix(automations): constrain report output scope
BelhsanHmida Aug 12, 2026
3e6c2ec
fix(automations): require generators for reports
BelhsanHmida Aug 12, 2026
601f951
fix(automations): anchor reports to claimed occurrences
BelhsanHmida Aug 12, 2026
f0c3fce
fix(automations): advance report coverage monotonically
BelhsanHmida Aug 12, 2026
2350b68
test(ui): cover report automation listings
BelhsanHmida Aug 12, 2026
77c6c2d
docs(automations): document report occurrence semantics
BelhsanHmida Aug 12, 2026
95b6187
test(api): close rejected report transactions
BelhsanHmida Aug 12, 2026
2e6fe75
Merge branch 'feat/2288-automations-crud' into feat/2288-report-autom…
Flix6x Sep 1, 2026
b8b8db5
Merge remote-tracking branch 'origin/feat/2288-automations-crud' into…
Flix6x Sep 1, 2026
2701717
Merge remote-tracking branch 'origin/feat/2288-automations-crud' into…
Flix6x Sep 1, 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
1 change: 1 addition & 0 deletions documentation/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ New features
* In the UI, the full record of the data source selected on a sensor page can be inspected, backed by a new API endpoint (``[GET] /sources/(id)``) [see `PR #2290 <https://www.github.com/FlexMeasures/flexmeasures/pull/2290>`_]
* Automations can also compute schedules on a recurring basis (``flexmeasures add automation --type schedules``), with the schedule start defaulting to each run's time [see `PR #2293 <https://www.github.com/FlexMeasures/flexmeasures/pull/2293>`_]
* Automations can be created, edited and deleted in the UI and through new API endpoints (``[POST|PATCH|DELETE] /assets/(id)/automations``), by organisation admins and consultants, with their recurrence expressed in a selectable IANA timezone, and only involving sensors they can access themselves (read access to the sensors an automation reads, and permission to record data on the sensors it writes to) [see `PR #2294 <https://www.github.com/FlexMeasures/flexmeasures/pull/2294>`_]
* Reports can run as background jobs (``flexmeasures add report --as-job``, processed by workers of the new ``reporting`` queue) and be computed on a recurring basis by automations, with a rolling report window expressed as Pandas offsets or defaulting to the last cron period [see `PR #2297 <https://www.github.com/FlexMeasures/flexmeasures/pull/2297>`_]
* ``flexmeasures show data-sources`` now shows which organisation a data source belongs to, and can list the sensors holding data recorded by a given source [see `PR #2401 <https://www.github.com/FlexMeasures/flexmeasures/pull/2401>`_]
* The flex-context can now define multiple commodities, each specifying their own prices and grid capacities [see `PR #1946 <https://www.github.com/FlexMeasures/flexmeasures/pull/1946>`_, `PR #2172 <https://www.github.com/FlexMeasures/flexmeasures/pull/2172>`_, `PR #2235 <https://www.github.com/FlexMeasures/flexmeasures/pull/2235>`_, `PR #2271 <https://www.github.com/FlexMeasures/flexmeasures/pull/2271>`_, `PR #2355 <https://www.github.com/FlexMeasures/flexmeasures/pull/2355>`_ and `PR #2380 <https://www.github.com/FlexMeasures/flexmeasures/pull/2380>`_]
* Support multiple feeders to a shared storage [see `PR #2001 <https://www.github.com/FlexMeasures/flexmeasures/pull/2001>`_, `PR #2321 <https://www.github.com/FlexMeasures/flexmeasures/pull/2321>`_, `PR #2322 <https://www.github.com/FlexMeasures/flexmeasures/pull/2322>`_, `PR #2325 <https://www.github.com/FlexMeasures/flexmeasures/pull/2325>`_ and `PR #2431 <https://www.github.com/FlexMeasures/flexmeasures/pull/2431>`_]
Expand Down
5 changes: 3 additions & 2 deletions documentation/cli/change_log.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ since v1.0.0 | August 11, 2026
* Add ``flexmeasures add plan``, ``flexmeasures show plans`` and ``flexmeasures edit plan``, to manage the rate limits and quotas which apply to the accounts on a plan.
* Add ``flexmeasures edit secret`` to store an encrypted secret on an account or asset.
* Add ``flexmeasures delete secret`` to remove an encrypted secret from an account or asset.
* Add ``flexmeasures add automation``, ``flexmeasures edit automation`` and ``flexmeasures delete automation`` to manage automations (recurring tasks on an asset, computing forecasts or schedules). Each automation carries its own IANA timezone (``--timezone``), in which its cron expression is interpreted.
* Add ``flexmeasures jobs run-automations`` to queue jobs for all automations that are due to run this minute from standard five-field cron expressions. Run this command once per minute. It makes at most one queueing attempt per automation per minute, including when an attempt fails after partially queueing jobs. Runs missed while the runner was down are caught up once, with several missed forecast runs coalesced into the latest useful forecast, and a run at a skipped or repeated daylight-saving-time hour happens exactly once.
* Add ``flexmeasures add automation``, ``flexmeasures edit automation`` and ``flexmeasures delete automation`` to manage automations (recurring tasks on an asset, computing forecasts, schedules or reports). Each automation carries its own IANA timezone (``--timezone``), in which its cron expression is interpreted.
* Add an ``--as-job`` flag to ``flexmeasures add report``, to queue a reporting job (processed by workers of the new ``reporting`` queue) instead of computing directly.
* Add ``flexmeasures jobs run-automations`` to queue jobs for all automations that are due to run this minute from standard five-field cron expressions. Run this command once per minute. It makes at most one queueing attempt per automation per minute, including when an attempt fails after partially queueing jobs. Runs missed while the runner was down are caught up once, with several missed runs coalesced into the latest useful one, and a run at a skipped or repeated daylight-saving-time hour happens exactly once.
* ``flexmeasures delete sensor`` now warns which automations read from or write to a sensor before it is deleted, as an automation refers to its sensors by ID and would fail on its next run.
* ``flexmeasures show data-sources`` now shows the account a data source belongs to, and lists the sensors holding data recorded by a single source with ``--show-sensors``.

Expand Down
2 changes: 1 addition & 1 deletion documentation/cli/commands.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ of which some are referred to in this documentation.
``flexmeasures add annotation`` Add annotation to accounts, assets and/or sensors.
``flexmeasures add toy-account`` Create a toy account, for tutorials and trying things.
``flexmeasures add report`` Create a report.
``flexmeasures add automation`` Add an automation: a recurring task (computing forecasts or schedules) on an asset, with its own cron timezone.
``flexmeasures add automation`` Add an automation: a recurring task (computing forecasts, schedules or reports) on an asset, with its own cron timezone.
================================================= =======================================


Expand Down
76 changes: 36 additions & 40 deletions documentation/features/automations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,65 +3,61 @@
Automations
============

An **automation** is a recurring task defined on an asset.
For now, an automation computes forecasts; automating schedules and reports is planned.
Hosts and users often want the three main FlexMeasures features — :ref:`forecasting`, :ref:`scheduling` and :ref:`reporting` — to run on a recurring basis, across larger numbers of sites.
*Automations* make that a first-class concept: an automation is a recurring task defined on an asset, and each time it runs, it queues jobs.

On each run, the automation queues jobs (so make sure a worker is processing the ``forecasting`` queue, see :ref:`redis-queue`).
The parameters of the task were stored when the automation was created, and validated with the same schema that the CLI and API use.
Timing parameters are resolved on each run — for instance, the forecast start defaults to the time the automation runs, so each run produces fresh forecasts.
An automation consists of:

Creating an automation
----------------------
- a **type**: ``forecasts``, ``schedules`` or ``reports``;
- a **recurrence**: a cron string (e.g. ``"0 6 * * *"`` for daily at 6 AM), interpreted in the automation's own IANA timezone;
- a **data generator** (for forecasts and reports): the forecaster or reporter class and its configuration, stored on a data source.
The data source stays the same across runs, so all results the automation produces attribute to one steady source;
- **parameters**: what to compute on each run, validated by the same schema the CLI and API use for one-off runs.
Timing parameters are resolved freshly on each run, so a recurring automation always computes fresh periods
(see the type-specific sections below for the exact rules);
- an **activation status**: only active automations run.

Here is how you create an automation in the CLI, asking for daily (at 6 AM) forecasts of sensor 12:
Managing automations
--------------------

.. code-block:: bash

flexmeasures add automation --asset 3 --name "Daily PV forecasts" --type forecasts \
--cron "0 6 * * *" --timezone Europe/Amsterdam --sensor 12

``--type`` says what the automation computes, and defaults to ``forecasts``.
The remaining options are the ones the task itself needs: a forecast automation accepts everything `flexmeasures add forecast` accepts, such as ``--forecaster`` to pick the forecaster and ``--config`` to configure it (see :ref:`forecasting`).
The forecaster and its configuration are stored on a data source, so you can also pass ``--source`` to reuse the data source of an existing forecaster, in which case ``--forecaster`` and ``--config`` (and the individual configuration options) are not needed — the data source already determines them.
That data source is required while the automation exists, so it cannot be deleted until the automation is removed.

The recurrence is defined by a standard five-field cron string (minute, hour, day of month, month, and day of week), which defaults to ``"0 0 * * *"`` (daily at midnight).
It is interpreted in the automation's IANA timezone.
If ``--timezone`` is omitted, the current ``FLEXMEASURES_TIMEZONE`` value is copied to the automation.
Changing that configuration later does not change existing automations.
Cron aliases and optional seconds or year fields are not supported.
Automations can be managed in three ways:

Automations are active by default (use ``--inactive`` to create them in deactivated state).
Use ``flexmeasures edit automation`` to rename, re-schedule (``--cron``), change the timezone, activate or deactivate an automation, and ``flexmeasures delete automation`` to remove one.
These changes are recorded in the asset's audit log.
- **CLI**: ``flexmeasures add automation``, ``flexmeasures edit automation`` (name, cron string, timezone and activation status) and ``flexmeasures delete automation``.
- **API**: list and inspect with ``[GET] /assets/(id)/automations`` and ``[GET] /assets/(id)/automations/(automation_id)``;
create, update and delete with ``[POST|PATCH|DELETE]`` on the same paths (see the `API documentation <../api/v3_0.html>`_).
- **UI**: each asset has an *Automations* page (in the breadcrumbs dropdown), with a tab per automation type.
It lists each automation's recurrence and recent job counts, and lets you create, edit, (de)activate and delete automations.

For forecast automations, the sensor on which forecasts are saved (``sensor-to-save``, falling back to ``sensor``) must belong to the automation's asset or one of its descendants.
This relationship is checked both when the automation is created and immediately before each run.
Creating, updating and deleting automations requires account admin or consultant rights, and is recorded in the asset's audit log.

Running automations
-------------------
--------------------

For automations to actually run, let a cron job execute the following command once per minute:
An automation is due whenever its cron string matches the current minute in its configured timezone. To actually run due automations, let a cron job execute the following command once per minute:

.. code-block:: bash

* * * * * flexmeasures jobs run-automations

Each due automation then queues its jobs.
Each due automation then queues its jobs — so make sure workers are processing the relevant queues (``forecasting``, ``scheduling`` and/or ``reporting``, see :ref:`redis-queue`).
Each scheduled run receives at most one automatic queueing attempt, so the command is safe to run more than once within a minute.
If the process crashes, or queueing fails after creating some jobs, that run is not retried automatically, because a retry could duplicate partial work.

If the runner misses runs, because it was down or overloaded, it catches up when it resumes: it queues only the latest missed run of each automation, rather than replaying stale ones.
Timing parameters that default to the run time are resolved when that catch-up run is queued, so it produces a current forecast.
Timing parameters that default to the run time are resolved when that catch-up run is queued, so it produces a current result.

Each scheduled run receives at most one automatic queueing attempt.
If the process crashes, or queueing fails after creating some jobs, that run is not retried automatically, because a retry could duplicate partial work.
Jobs record how they were created (via the CLI, the API or an automation), which is shown in the *Created Via* column
of the jobs table on the asset's status page, where recent jobs are listed.

The jobs record how they were created, which is shown on the asset's status page (UI), where recent jobs are listed.
Automating each feature
-----------------------

Viewing automations
-------------------
The parameters stored on an automation follow the same schemas as one-off CLI/API calls, with type-specific rules for resolving timing on each run:

Automations defined on an asset can be viewed on the asset's *Automations* page in the UI, and listed with the API endpoint `[GET] /assets/(id)/automations <../api/v3_0.html#get--api-v3_0-assets-id-automations>`_.
An automation's details show the sensors it reads from and writes to, linking to each sensor's page.
Conversely, a sensor's page lists the automations that write data to it.
- :ref:`automating_forecasts` — forecast parameters; the forecast start defaults to the run time.
- :ref:`automating_schedules` — a schedule trigger message; omit ``start`` to schedule from the run time.
- :ref:`automating_reports` — report parameters; use ``start-offset``/``end-offset`` (Pandas offsets) for a rolling window,
or omit timing fields to report on the period since the last successfully covered report window.

.. _automation_cursor:

Expand Down
20 changes: 17 additions & 3 deletions documentation/features/forecasting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,20 @@ Usage:
Automating forecasts
--------------------

Instead of asking for forecasts one at a time, you can set up an *automation*: a recurring task defined on an asset, which queues forecasting jobs on a cron schedule.
See :ref:`automations`.
Schedules can be automated in the same way — see :ref:`automating_schedules`.
Instead of asking for forecasts one at a time, you can set up an *automation*: a recurring task defined on an asset (see :ref:`automations` for the full concept, including how to manage and run automations).
On each run, the automation queues forecasting jobs (so make sure a worker is processing the ``forecasting`` queue, see :ref:`redis-queue`).
When the automation was created, its forecast parameters (see above) were stored, and validated with the same schema that the CLI and API use.
Timing parameters are resolved on each run — for instance, the forecast start defaults to the time the automation runs, so each run produces fresh forecasts.
The sensor on which forecasts are saved (``sensor-to-save``, falling back to ``sensor``) must belong to the automation's asset or one of its descendants.
This relationship is checked both when the automation is created and immediately before each run.

Here is how you create a forecast automation in the CLI, asking for daily (at 6 AM) forecasts of sensor 12:

.. code-block:: bash

flexmeasures add automation --asset 3 --name "Daily PV forecasts" --type forecasts \
--cron "0 6 * * *" --timezone Europe/Amsterdam --sensor 12

A forecast automation accepts everything ``flexmeasures add forecast`` accepts, such as ``--forecaster`` to pick the forecaster and ``--config`` to configure it.
The forecaster and its configuration are stored on a data source, so you can also pass ``--source`` to reuse the data source of an existing forecaster, in which case ``--forecaster`` and ``--config`` (and the individual configuration options) are not needed — the data source already determines them.
That data source is required while the automation exists, so it cannot be deleted until the automation is removed.
27 changes: 26 additions & 1 deletion documentation/features/reporting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,29 @@ The input sensor stores the power/energy flow, and the output sensor will store
Here, the ``ProfitOrLossReporter`` used as source (with Id 6) is the one we configured above.
With the offsets, we control the timing ― we indicate that we want the new report to encompass the day of tomorrow (see Pandas offset strings).

The report sensor will now store all costs which we know will be made tomorrow by the schedule.
The report sensor will now store all costs which we know will be made tomorrow by the schedule.

.. _automating_reports:

Automating reports
--------------------

Reports can be queued as background jobs (add ``--as-job`` to ``flexmeasures add report``, and let a worker process the ``reporting`` queue, see :ref:`redis-queue`),
and computed on a recurring basis by an *automation* defined on the asset (see :ref:`automations` for the full concept, including how to manage and run automations).

The reporter and its configuration are stored on a data source (steady across runs, so all report results attribute to the same source),
while the report parameters are stored on the automation itself and their timing is resolved freshly on each run:

- Use ``start-offset`` and/or ``end-offset`` fields (comma-separated Pandas offsets, like the CLI options above) for a rolling window relative to the claimed cron occurrence,
in the timezone of the first output sensor. For instance, ``"start-offset": "-1D,DB"`` with ``"end-offset": "DB"`` reports on the whole previous day.
- Omit timing fields entirely to report from the end of the latest successfully completed report window through the claimed cron occurrence.
When no completed window is known, such as on the first run, the start falls back to the previous cron occurrence in the automation's timezone.
The completion marker only moves forward, so concurrent reporting workers that finish out of order cannot reopen an already covered period.
- Absolute ``start``/``end`` fields are also accepted, but draw a warning, as each run would then compute the same period.

For example, this automation computes a report over each past day, every morning at 1 AM:

.. code-block:: bash

flexmeasures add automation --asset 3 --name "Daily aggregation report" --cron "0 1 * * *" --type reports \
--reporter PandasReporter --config reporter-config.yml --parameters report-parameters.yml
2 changes: 1 addition & 1 deletion documentation/host/queues.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Here is how to run one worker for each kind of job (in separate terminals):

.. code-block:: bash

$ flexmeasures jobs run-worker --name our-only-worker --queue forecasting|scheduling|ingestion
$ flexmeasures jobs run-worker --name our-only-worker --queue forecasting|scheduling|ingestion|reporting

Running multiple workers in parallel might be a great idea.

Expand Down
2 changes: 1 addition & 1 deletion flexmeasures/api/v3_0/assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -1661,7 +1661,7 @@ def post_automation(self, id: int, asset: GenericAsset):
automation_type=automation_data["type"],
active=automation_data["active"],
parameters=automation_data["parameters"],
forecaster_class=automation_data["forecaster"],
generator_class=automation_data["generator"],
config=automation_data["config"],
origin="API",
check_permissions=True,
Expand Down
Loading
Loading