chore: adapt deprecated endpoints to 15-minutes predictions (#1195)
Bump Version / Bump Version Workflow (push) Canceled after 0s
CodeQL Advanced / Analyze (actions) (push) Canceled after 0s
CodeQL Advanced / Analyze (python) (push) Canceled after 0s
docker-build / platform-excludes (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Run Pytest on Pull Request / test (push) Canceled after 0s
docker-build / build (push) Canceled after 0s
docker-build / merge (push) Canceled after 0s

Ensure the deprecated endpoints get /strompreis, post /gesamtlast, get /gesamtlast_simple,
get /pvforecast to work on 1-hour intervalls even if the prediction provides 15-minutes
intervall data. This keeps the interface compliant to the legacy functionality.

Besides this adaptations several other improvements and fixes are included in this PR.

* feat: extend data management method key_to array by resample_method

  One can now define the resample method on how to aggregate the values in an interval
  for resampling. Three methods are provided:

  - "first": Use the first value in each interval.
  - "mean": Compute the arithmetic mean of all samples in each interval.
  - "interval_mean": Compute the time-weighted mean assuming each value remains valid
     until the next timestamp (piecewise-constant signal).

* feat: extend the get /v1/prediction/dataframe endpoint with resampling parameters

  Make all parameters for resampling available at the endpoint.

* feat: extend the get /v1/prediction/list endpoint with resampling parameters

  Make all parameters for resampling available at the endpoint.

* feat: add new delete /v1/prediction/range endpoint

  The endpoint allows to delete prediction values for a given time span.

* fix: adapt for PVForecastAkkudoktor server side cache handling

  /api.akkudoktor/forecast does it's own caching on requests. Call it with slightly
  randomized request values to avoid getting cached values in the case we need fresh
  data. The requests are anyway rate limited to one request per hour on our side.

* chore: add core.types

  This module centralizes reusable type definitions shared across multiple
  packages. Defining common types here avoids duplication of complex type
  annotations (such as Literal aliases), ensures consistent typing across the
  code base, and helps prevent circular import dependencies between modules.

* chore: extend cache testing

* chore: add system test for deprecated /strompreis endpoint

* chore: add unit test module for server endpoints

  Add a new test module to do unit tests on server endpoints. First test added
  for deprecated get /strompreis endpoint.

Signed-off-by: Bobby Noelte <b0661n0e17e@gmail.com>
This commit is contained in:
Bobby Noelte
2026-07-30 19:54:05 +02:00
committed by GitHub
parent 52fe489d4e
commit 8344974c16
16 changed files with 1232 additions and 95 deletions
+262 -7
View File
@@ -8,7 +8,7 @@
"name": "Apache 2.0",
"url": "https://www.apache.org/licenses/LICENSE-2.0.html"
},
"version": "v0.3.0.dev2607290944830210"
"version": "v0.3.0.dev2607301181843779"
},
"paths": {
"/v1/admin/cache/clear": {
@@ -1470,7 +1470,7 @@
"prediction"
],
"summary": "Fastapi Prediction Dataframe Get",
"description": "Get prediction for given key within given date range as series.\n\nArgs:\n key (str): Prediction key\n start_datetime (Optional[str]): Starting datetime (inclusive).\n Defaults to start datetime of latest prediction.\n end_datetime (Optional[str]: Ending datetime (exclusive).\n\nDefaults to end datetime of latest prediction.",
"description": "Get prediction for given keys within given date range as dataframe.\n\nArgs:\n key (list[str]): Prediction keys\n start_datetime (Optional[str]): Starting datetime (inclusive).\n Defaults to start datetime of latest prediction.\n end_datetime (Optional[str]: Ending datetime (exclusive).\n Defaults to end datetime of latest prediction.\n interval (Optional[str]): Time duration for each interval.\n Defaults to 1 hour.\n fill_method (str): Method to handle missing values during resampling.\n\n - 'linear': Linearly interpolate missing values (for numeric data only).\n - 'time': Interpolate missing values (for numeric data only).\n - 'ffill': Forward fill missing values.\n - 'bfill': Backward fill missing values.\n - Defaults to 'linear' for numeric values, otherwise 'ffill'.\n\n resample_method (str):\n Method used to aggregate values within a resampling interval.\n\n - \"first\": Use the first value in each interval.\n - \"mean\": Compute the arithmetic mean of all samples in each interval.\n - \"interval_mean\": Compute the time-weighted mean assuming each\n value remains valid until the next timestamp (piecewise-constant\n signal).\n\n dropna: (bool, optional): Whether to drop NAN/ None values before processing.\n Defaults to True.\n boundary (Literal[\"strict\", \"context\"]): resampling boundary\n \"strict\" \u2192 only values inside [start, end)\n \"context\" \u2192 include one value before and after for proper resampling\n align_to_interval (bool): When True, snap the resample origin to the nearest\n UTC epoch-aligned boundary of ``interval`` before resampling. This ensures\n that bucket timestamps always fall on wall-clock-round times regardless of\n when ``start_datetime`` falls:\n\n - 15-minute interval \u2192 buckets on :00, :15, :30, :45\n - 1-hour interval \u2192 buckets on the hour\n\n When False (default), the origin is ``query_start`` (or ``\"start_day\"`` when\n no start is given), preserving the existing behaviour where buckets are\n aligned to the query window rather than the clock.\n\n Set to True when storing compacted records back to the database so that the\n resulting timestamps are predictable and human-readable. Leave False for\n forecast or reporting queries where alignment to the exact query window is\n more important than clock-round boundaries.",
"operationId": "fastapi_prediction_dataframe_get_v1_prediction_dataframe_get",
"parameters": [
{
@@ -1540,6 +1540,93 @@
"title": "Interval"
},
"description": "Time duration for each interval. Defaults to 1 hour."
},
{
"name": "fill_method",
"in": "query",
"required": false,
"schema": {
"anyOf": [
{
"enum": [
"linear",
"time",
"ffill",
"bfill"
],
"type": "string"
},
{
"type": "null"
}
],
"description": "Method to handle missing values during resampling.",
"title": "Fill Method"
},
"description": "Method to handle missing values during resampling."
},
{
"name": "resample_method",
"in": "query",
"required": false,
"schema": {
"enum": [
"first",
"mean",
"interval_mean"
],
"type": "string",
"description": "Method used to aggregate values within a resampling interval.",
"default": "mean",
"title": "Resample Method"
},
"description": "Method used to aggregate values within a resampling interval."
},
{
"name": "dropna",
"in": "query",
"required": false,
"schema": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"description": "Drop NAN/ None values before processing.",
"title": "Dropna"
},
"description": "Drop NAN/ None values before processing."
},
{
"name": "boundary",
"in": "query",
"required": false,
"schema": {
"enum": [
"strict",
"context"
],
"type": "string",
"description": "Resampling boundary mode.",
"default": "context",
"title": "Boundary"
},
"description": "Resampling boundary mode."
},
{
"name": "align_to_interval",
"in": "query",
"required": false,
"schema": {
"type": "boolean",
"description": "Snap resample origin to the nearest UTC epoch-aligned boundary of interval.",
"default": false,
"title": "Align To Interval"
},
"description": "Snap resample origin to the nearest UTC epoch-aligned boundary of interval."
}
],
"responses": {
@@ -1572,7 +1659,7 @@
"prediction"
],
"summary": "Fastapi Prediction List Get",
"description": "Get prediction for given key within given date range as value list.\n\nArgs:\n key (str): Prediction key\n start_datetime (Optional[str]): Starting datetime (inclusive).\n Defaults to start datetime of latest prediction.\n end_datetime (Optional[str]: Ending datetime (exclusive).\n Defaults to end datetime of latest prediction.\n interval (Optional[str]): Time duration for each interval.\n Defaults to 1 hour.",
"description": "Get prediction for given key within given date range as value list.\n\nArgs:\n key (str): Prediction key\n start_datetime (Optional[str]): Starting datetime (inclusive).\n Defaults to start datetime of latest prediction.\n end_datetime (Optional[str]: Ending datetime (exclusive).\n Defaults to end datetime of latest prediction.\n interval (Optional[str]): Time duration for each interval.\n Defaults to 1 hour.\n fill_method (str): Method to handle missing values during resampling.\n\n - 'linear': Linearly interpolate missing values (for numeric data only).\n - 'time': Interpolate missing values (for numeric data only).\n - 'ffill': Forward fill missing values.\n - 'bfill': Backward fill missing values.\n - Defaults to 'linear' for numeric values, otherwise 'ffill'.\n\n resample_method (str):\n Method used to aggregate values within a resampling interval.\n\n - \"first\": Use the first value in each interval.\n - \"mean\": Compute the arithmetic mean of all samples in each interval.\n - \"interval_mean\": Compute the time-weighted mean assuming each\n value remains valid until the next timestamp (piecewise-constant\n signal).\n\n dropna: (bool, optional): Whether to drop NAN/ None values before processing.\n Defaults to True.\n boundary (Literal[\"strict\", \"context\"]): resampling boundary\n \"strict\" \u2192 only values inside [start, end)\n \"context\" \u2192 include one value before and after for proper resampling\n align_to_interval (bool): When True, snap the resample origin to the nearest\n UTC epoch-aligned boundary of ``interval`` before resampling. This ensures\n that bucket timestamps always fall on wall-clock-round times regardless of\n when ``start_datetime`` falls:\n\n - 15-minute interval \u2192 buckets on :00, :15, :30, :45\n - 1-hour interval \u2192 buckets on the hour\n\n When False (default), the origin is ``query_start`` (or ``\"start_day\"`` when\n no start is given), preserving the existing behaviour where buckets are\n aligned to the query window rather than the clock.\n\n Set to True when storing compacted records back to the database so that the\n resulting timestamps are predictable and human-readable. Leave False for\n forecast or reporting queries where alignment to the exact query window is\n more important than clock-round boundaries.",
"operationId": "fastapi_prediction_list_get_v1_prediction_list_get",
"parameters": [
{
@@ -1639,6 +1726,93 @@
"title": "Interval"
},
"description": "Time duration for each interval. Defaults to 1 hour."
},
{
"name": "fill_method",
"in": "query",
"required": false,
"schema": {
"anyOf": [
{
"enum": [
"linear",
"time",
"ffill",
"bfill"
],
"type": "string"
},
{
"type": "null"
}
],
"description": "Method to handle missing values during resampling.",
"title": "Fill Method"
},
"description": "Method to handle missing values during resampling."
},
{
"name": "resample_method",
"in": "query",
"required": false,
"schema": {
"enum": [
"first",
"mean",
"interval_mean"
],
"type": "string",
"description": "Method used to aggregate values within a resampling interval.",
"default": "mean",
"title": "Resample Method"
},
"description": "Method used to aggregate values within a resampling interval."
},
{
"name": "dropna",
"in": "query",
"required": false,
"schema": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"description": "Drop NAN/ None values before processing.",
"title": "Dropna"
},
"description": "Drop NAN/ None values before processing."
},
{
"name": "boundary",
"in": "query",
"required": false,
"schema": {
"enum": [
"strict",
"context"
],
"type": "string",
"description": "Resampling boundary mode.",
"default": "context",
"title": "Boundary"
},
"description": "Resampling boundary mode."
},
{
"name": "align_to_interval",
"in": "query",
"required": false,
"schema": {
"type": "boolean",
"description": "Snap resample origin to the nearest UTC epoch-aligned boundary of interval.",
"default": false,
"title": "Align To Interval"
},
"description": "Snap resample origin to the nearest UTC epoch-aligned boundary of interval."
}
],
"responses": {
@@ -1891,6 +2065,87 @@
}
}
},
"/v1/prediction/range": {
"delete": {
"tags": [
"prediction"
],
"summary": "Fastapi Prediction Range Delete",
"description": "Delete prediction values for a key within a datetime range.",
"operationId": "fastapi_prediction_range_delete_v1_prediction_range_delete",
"parameters": [
{
"name": "key",
"in": "query",
"required": true,
"schema": {
"type": "string",
"description": "Prediction key.",
"title": "Key"
},
"description": "Prediction key."
},
{
"name": "start_datetime",
"in": "query",
"required": false,
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Start datetime.",
"title": "Start Datetime"
},
"description": "Start datetime."
},
{
"name": "end_datetime",
"in": "query",
"required": false,
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "End datetime.",
"title": "End Datetime"
},
"description": "End datetime."
}
],
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PydanticDateTimeSeries"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
},
"/v1/energy-management/optimization/solution": {
"get": {
"tags": [
@@ -1992,7 +2247,7 @@
"prediction"
],
"summary": "Fastapi Strompreis",
"description": "Deprecated: Electricity Market Price Prediction per Wh [amount/Wh].\n\nElectricity prices start at 00.00.00 today and are provided for 48 hours.\nIf no prices are available the missing ones at the start of the series are\nfilled with the first available price.\n\nNote:\n Electricity price charges are added.\n\nNote:\n Set ElecPriceAkkudoktor as provider, then update data with\n '/v1/prediction/update'\n and then request data with\n '/v1/prediction/list?key=elecprice_marketprice_wh' or\n '/v1/prediction/list?key=elecprice_marketprice_kwh' instead.",
"description": "Deprecated: Electricity Market Price Prediction per Wh [amount/Wh].\n\nElectricity prices start at 00.00.00 today and are provided for 48 hours\nin 1-hour intervals. If no prices are available the missing ones at the\nstart of the series are filled with the first available price.\n\nNote:\n Electricity price charges are added.\n\nNote:\n Set ElecPriceAkkudoktor as provider, then update data with\n '/v1/prediction/update'\n and then request data with\n '/v1/prediction/list?key=elecprice_marketprice_wh' or\n '/v1/prediction/list?key=elecprice_marketprice_kwh' instead.",
"operationId": "fastapi_strompreis_strompreis_get",
"responses": {
"200": {
@@ -2019,7 +2274,7 @@
"prediction"
],
"summary": "Fastapi Gesamtlast",
"description": "Deprecated: Total Load Prediction with adjustment.\n\nEndpoint to handle total load prediction adjusted by latest measured data.\n\nTotal load prediction starts at 00.00.00 today and is provided for 48 hours.\nIf no prediction values are available the missing ones at the start of the series are\nfilled with the first available prediction value.\n\nNote:\n Use '/v1/prediction/list?key=loadforecast_power_w' instead.\n Load energy meter readings to be added to EOS measurement by:\n '/v1/measurement/value' or\n '/v1/measurement/series' or\n '/v1/measurement/dataframe' or\n '/v1/measurement/data'",
"description": "Deprecated: Total Load Prediction with adjustment.\n\nEndpoint to handle total load prediction adjusted by latest measured data.\n\nTotal load prediction starts at 00.00.00 today and is provided for 48 hours\nin 1-hour intervals. If no prediction values are available the missing ones\nat the start of the series are filled with the first available prediction value.\n\nNote:\n Use '/v1/prediction/list?key=loadforecast_power_w' instead.\n Load energy meter readings to be added to EOS measurement by:\n '/v1/measurement/value' or\n '/v1/measurement/series' or\n '/v1/measurement/dataframe' or\n '/v1/measurement/data'",
"operationId": "fastapi_gesamtlast_gesamtlast_post",
"requestBody": {
"content": {
@@ -2066,7 +2321,7 @@
"prediction"
],
"summary": "Fastapi Gesamtlast Simple",
"description": "Deprecated: Total Load Prediction.\n\nEndpoint to handle total load prediction.\n\nTotal load prediction starts at 00.00.00 today and is provided for 48 hours.\nIf no prediction values are available the missing ones at the start of the series are\nfilled with the first available prediction value.\n\nArgs:\n year_energy (float): Yearly energy consumption in Wh.\n\nNote:\n Set LoadAkkudoktor as provider, then update data with\n '/v1/prediction/update'\n and then request data with\n '/v1/prediction/list?key=loadforecast_power_w' instead.",
"description": "Deprecated: Total Load Prediction.\n\nEndpoint to handle total load prediction.\n\nTotal load prediction starts at 00.00.00 today and is provided for 48 hours\nin 1-hour intervals. If no prediction values are available the missing ones\nat the start of the series are filled with the first available prediction value.\n\nArgs:\n year_energy (float): Yearly energy consumption in Wh.\n\nNote:\n Set LoadAkkudoktor as provider, then update data with\n '/v1/prediction/update'\n and then request data with\n '/v1/prediction/list?key=loadforecast_power_w' instead.",
"operationId": "fastapi_gesamtlast_simple_gesamtlast_simple_get",
"deprecated": true,
"parameters": [
@@ -2114,7 +2369,7 @@
"prediction"
],
"summary": "Fastapi Pvforecast",
"description": "Deprecated: PV Forecast Prediction.\n\nEndpoint to handle PV forecast prediction.\n\nPVForecast starts at 00.00.00 today and is provided for 48 hours.\nIf no forecast values are available the missing ones at the start of the series are\nfilled with the first available forecast value.\n\nNote:\n Set PVForecastAkkudoktor as provider, then update data with\n '/v1/prediction/update'\n and then request data with\n '/v1/prediction/list?key=pvforecast_ac_power' and\n '/v1/prediction/list?key=pvforecastakkudoktor_temp_air' instead.",
"description": "Deprecated: PV Forecast Prediction.\n\nEndpoint to handle PV forecast prediction.\n\nPVForecast starts at 00.00.00 today and is provided for 48 hours\nin 1-hour intervals. If no forecast values are available the missing ones\nat the start of the series are filled with the first available forecast value.\n\nNote:\n Set PVForecastAkkudoktor as provider, then update data with\n '/v1/prediction/update'\n and then request data with\n '/v1/prediction/list?key=pvforecast_ac_power' and\n '/v1/prediction/list?key=pvforecastakkudoktor_temp_air' instead.",
"operationId": "fastapi_pvforecast_pvforecast_get",
"responses": {
"200": {