Skip to content

Run reports via CLI and API - #2298

Open
Flix6x wants to merge 166 commits into
mainfrom
feat/2288-report-trigger-api
Open

Run reports via CLI and API#2298
Flix6x wants to merge 166 commits into
mainfrom
feat/2288-report-trigger-api

Conversation

@Flix6x

@Flix6x Flix6x commented Jul 11, 2026

Copy link
Copy Markdown
Member

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.

  • Register a dedicated reporting RQ queue with configurable job timeouts and worker support.
  • Add flexmeasures add report --as-job for queueing reports from the CLI.
  • Add POST /api/v3_0/assets/<id>/reports/trigger for queueing reports through the API.
  • Persist the reporter configuration before queueing so a worker can reconstruct the reporter.
  • Store report results as beliefs and expose the job through the existing job-status and asset-status functionality.
  • Resolve reporter input and output sensors through the data generator itself, including configuration-based dependencies such as Profit-or-loss price sensors.
  • Require read access to every input and configuration sensor and create-children access to every output sensor.
  • Require every output sensor to belong to the asset in the URL or one of its descendants.
  • Validate and authorize the request before persisting the reporter data source or queueing work.
  • Apply the shared schedule/forecast/report trigger rate limit.
  • Add CLI, API, worker, authorization and end-to-end regression tests.
  • Document the reporting worker queue, API endpoint and permission model.
  • Added changelog item in documentation/changelog.rst.

The API request envelope is validated first. The selected reporter then validates config and parameters with 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:

flexmeasures add report \
  --reporter PandasReporter \
  --config report-config.json \
  --parameters report-parameters.json \
  --start 2023-04-10T00:00:00+00:00 \
  --end 2023-04-10T10:00:00+00:00 \
  --as-job

Process queued reports with a reporting worker:

flexmeasures jobs run-worker --queue reporting

--as-job stores report results in the database and therefore cannot be combined with --dry-run or --output-file.

API

The endpoint is included in the generated OpenAPI specification and can be tried interactively through Swagger UI:

  1. Start FlexMeasures and sign in with a user who can access the target asset.
  2. Open http://127.0.0.1:5000/api/v3_0/docs.
  3. Under Assets, expand POST /api/v3_0/assets/{id}/reports/trigger.
  4. Select Try it out.
  5. Enter the target asset ID, for example 42.
  6. Paste the following request body, replacing the sensor IDs with sensors available to your user.
{
  "reporter": "PandasReporter",
  "config": {
    "required_input": [
      {"name": "one", "unit": "kW"},
      {"name": "two", "unit": "kW"}
    ],
    "required_output": [
      {"name": "sum", "unit": "kW"}
    ],
    "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 Accepted using 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-url or use GET /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

python -m pytest -q \
  flexmeasures/api/v3_0/tests/test_report_trigger_api.py \
  flexmeasures/api/v3_0/tests/test_rate_limiting.py \
  flexmeasures/cli/tests/test_data_add.py \
  flexmeasures/data/schemas/tests/test_reporting.py \
  flexmeasures/data/models/reporting/tests \
  flexmeasures/data/tests/test_generic_assets.py \
  flexmeasures/data/tests/test_reporting_service.py \
  flexmeasures/utils/tests/test_job_utils.py

The focused suite covers:

  • CLI queueing and reporter reconstruction from the stored data source.
  • The canonical HTTP 202 response and job-status URL.
  • API-to-worker execution and persisted output beliefs.
  • Anonymous, cross-organisation and sensor-level permission failures.
  • Source-filtered inputs and configuration-based price-sensor dependencies.
  • Output subtree enforcement.
  • Missing or invalid reporter inputs and outputs without database or queue side effects.
  • Shared trigger rate-limit registration.
  • Reporting queue configuration and timeout handling.

Latest local result: 119 passed, 22 warnings.

Manual test

  1. Choose two input sensors containing historical data and an output sensor below the asset used in the API URL.
  2. Create a valid Pandas reporter configuration and parameters file using fixed historical start and end times.
  3. Run flexmeasures add report ... --as-job and confirm a job is added to the reporting queue.
  4. Start flexmeasures jobs run-worker --queue reporting and confirm the job finishes and beliefs are stored on the output sensor.
  5. Submit the equivalent API request and confirm it returns HTTP 202 with job and job-url.
  6. Poll GET /api/v3_0/jobs/<job UUID> and confirm the job moves from queued to finished.
  7. Repeat with an unreadable input/configuration sensor and confirm HTTP 403 with no reporter data source or job created.
  8. Repeat with an output outside the URL asset subtree and confirm HTTP 422 with no reporter data source or job created.
  9. Repeat without required inputs or outputs and confirm HTTP 422 with no persistence or queue side effects.

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

  • I agree to contribute to the project under Apache 2 License.
  • To the best of my knowledge, the proposed patch is not based on code under GPL or another incompatible license.

Flix6x and others added 22 commits July 11, 2026 15:06
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
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
Flix6x and others added 7 commits July 11, 2026 22:20
- 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>
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>
@BelhsanHmida

Copy link
Copy Markdown
Contributor

@nhoening Thanks, I followed your proposal and made #2298 standalone on main.

It now includes the reporting queue and worker, flexmeasures add report --as-job, the shared
data-generator sensor-resolution and authorization helpers, reporter input/output
dependencies, output-subtree enforcement, rate-limit documentation/tests, and the
API-to-worker end-to-end test that verifies stored beliefs.

I also added defensive validation for non-empty reporter inputs and outputs before
the reporter data source is persisted.

The ReportTriggerSchema docstring has been updated to clarify that it validates
only the request envelope, while the selected reporter validates its concrete
configuration and parameters.

I have also updated the PR description to reflect the 202 job/job-url response
and the enforced permission model. Ready for another review once CI finishes.

nhoening and others added 3 commits September 1, 2026 12:18
Signed-off-by: Nicolas Höning <nicolas@seita.nl>
Context:
- #2290 was squash-merged into main. Unlike the branches stacked under it, this one does not contain #2290's commits, so this is a real merge rather than a formality.

Change:
- Took main's version of every conflict, each being a superset of what this branch had: set_job_trigger now records the automation an job came from, input_sensors and output_sensors explain what they are for, _resolve_sensors documents how it treats sensor references and no longer reads a bool as a sensor ID, and the upward tree walk moved into the shared asset_and_ancestor_ids helper.
- Kept this branch's API changelog entry for the report trigger endpoint, under the section underline main corrected.
- Regenerated the OpenAPI specs. The migration lineages joined without a new head.

Signed-off-by: F.N. Claessen <felix@seita.nl>
Context:
- Merging main left this import twice, once from each side, which flake8 rejects as F811. The file merged without a conflict, and I had only linted the files I resolved by hand.

Change:
- Kept one import.

Signed-off-by: F.N. Claessen <felix@seita.nl>

@nhoening nhoening left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking pretty good. I have comments, but only small ones.

I did try the manual testing via SwaggerUI. It went okay, only I believe we are not failing when we should when computing the job.

I used the PandasReporter example from the PR description, but my input sensors have different units (EUR and kW, outut sensor has kW). I expected an error on this.

Instead, the worker shows no error, and the job result (via the job status endpoint) looks like this:

{
  "ended-at": "2026-09-01T16:33:56.598447+00:00",
  "ended_at": "2026-09-01T16:33:56.598447+00:00",
  "enqueued-at": "2026-09-01T16:32:45.590679+00:00",
  "enqueued_at": "2026-09-01T16:32:45.590679+00:00",
  "exc-info": null,
  "exc_info": null,
  "func-name": "flexmeasures.data.services.reporting.run_report_job",
  "func_name": "flexmeasures.data.services.reporting.run_report_job",
  "message": "Reporting job has finished.",
  "origin": "reporting",
  "result": [
    {
      "n_rows": 194,
      "sensor_id": 49348
    }
  ],
  "started-at": "2026-09-01T16:33:56.339937+00:00",
  "started_at": "2026-09-01T16:33:56.339937+00:00",
  "status": "FINISHED"
}

I checked and sensor 49348 has no data.

I am not sure if what I saw is something that belongs in this PR as a fix or not, but it would be good to investigate.

Comment thread documentation/api/change_log.rst
Comment thread documentation/cli/change_log.rst
Comment thread documentation/changelog.rst Outdated
Comment thread flexmeasures/api/v3_0/tests/test_report_trigger_api.py Outdated
Comment thread flexmeasures/api/v3_0/tests/test_report_trigger_api.py
Comment thread flexmeasures/data/models/reporting/__init__.py Outdated
Comment thread flexmeasures/data/models/data_sources.py Outdated
reporter._data_source = db.session.merge(reporter.data_source)
db.session.flush()
data_source_id = reporter._data_source.id
db.session.commit()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is a commit needed here? I can't what changrd on reporter, as data_source was on it before already.

If we need to commit, why also do a flush right before?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a comment for the first one.

The commit is needed because reporter.data_source is a lazy property (data_sources.py:245) that can create a brand-new source row. The worker is a separate process and our views don't auto-commit, so without it the worker's lookup by id fails. No test catches this work_on_rq runs SimpleWorker in-process on the same session which is why the comment seemed worth adding.

On the flush: you're right, it's redundant given the commit right after. It only matters because the id is read before the commit; moving that read below would let it go. Left it for consistency with the same block in train_predict.py:454-458 happy to drop it in both in a follow-up commit.

Comment thread flexmeasures/data/services/reporting.py
Comment thread flexmeasures/ui/static/openapi-specs.json Outdated
BelhsanHmida and others added 17 commits September 1, 2026 20:35
Co-authored-by: Nicolas Höning <nicolas@seita.nl>
Signed-off-by: Mohamed Belhsan Hmida <149331360+BelhsanHmida@users.noreply.github.com>
Co-authored-by: Nicolas Höning <nicolas@seita.nl>
Signed-off-by: Mohamed Belhsan Hmida <149331360+BelhsanHmida@users.noreply.github.com>
Context:
- The newly added return annotation referenced Sensor only through a function-local import, so flake8 reported an undefined name at module scope.

Change:
- Import Sensor under TYPE_CHECKING so the annotation remains cycle-safe and lintable.

Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Context:
- FlexMeasures v1.0 was released before the report trigger endpoint, so documenting it under v3.0-32 incorrectly placed it in the previous API release.

Change:
- Move the report trigger changelog entry to the existing v3.0-33 section.

Signed-off-by: Mohamed Belhsan Hmida <mohamedbelhsanhmida@gmail.com>
Co-authored-by: Nicolas Höning <nicolas@seita.nl>
Signed-off-by: Mohamed Belhsan Hmida <149331360+BelhsanHmida@users.noreply.github.com>
Log which data source ran and how many values landed on which sensors.
@BelhsanHmida

Copy link
Copy Markdown
Contributor

This is looking pretty good. I have comments, but only small ones.

I did try the manual testing via SwaggerUI. It went okay, only I believe we are not failing when we should when computing the job.

I used the PandasReporter example from the PR description, but my input sensors have different units (EUR and kW, outut sensor has kW). I expected an error on this.

Instead, the worker shows no error, and the job result (via the job status endpoint) looks like this:

{
  "ended-at": "2026-09-01T16:33:56.598447+00:00",
  "ended_at": "2026-09-01T16:33:56.598447+00:00",
  "enqueued-at": "2026-09-01T16:32:45.590679+00:00",
  "enqueued_at": "2026-09-01T16:32:45.590679+00:00",
  "exc-info": null,
  "exc_info": null,
  "func-name": "flexmeasures.data.services.reporting.run_report_job",
  "func_name": "flexmeasures.data.services.reporting.run_report_job",
  "message": "Reporting job has finished.",
  "origin": "reporting",
  "result": [
    {
      "n_rows": 194,
      "sensor_id": 49348
    }
  ],
  "started-at": "2026-09-01T16:33:56.339937+00:00",
  "started_at": "2026-09-01T16:33:56.339937+00:00",
  "status": "FINISHED"
}

I checked and sensor 49348 has no data.

I am not sure if what I saw is something that belongs in this PR as a fix or not, but it would be good to investigate.

I verified this locally. The example did not declare expected units, so PandasReporter did not attempt unit conversion. The inputs also did not align on source and belief time, producing all-NaN rows that were dropped before persistence while the job incorrectly reported 194 rows.

I updated the example to declare kW units, added coverage confirming that a EUR input makes the job fail, and fixed the job result to report zero persistable rows instead of counting dropped NaN rows.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Run one-off reports as background jobs via CLI and API

3 participants