Skip to content

Commit

Permalink
Get resampled data (#458)
Browse files Browse the repository at this point in the history
Introduce the "resolution" field to /sensors/data (GET) to obtain data in a given resolution.


* Allow GETting resampled data

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

* Add docstring comment to schema

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

* Add missing type annotation

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

* Validate resolution field

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

* Add resolution field to schema

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

* Field is optional

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

* Use resampled resolution when reindexing

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

* Add test for deserializing the resolution field

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

* API changelog entry

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

* Add resolution field to example

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

* Changelog entry

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

* Improve docstring

Signed-off-by: F.N. Claessen <felix@seita.nl>
  • Loading branch information
Flix6x committed Jul 12, 2022
1 parent dc554a1 commit 6e134bd
Show file tree
Hide file tree
Showing 7 changed files with 47 additions and 2 deletions.
5 changes: 5 additions & 0 deletions documentation/api/change_log.rst
Expand Up @@ -6,6 +6,11 @@ API change log
.. note:: The FlexMeasures API follows its own versioning scheme. This is also reflected in the URL, allowing developers to upgrade at their own pace.


v3.0-2 | 2022-07-08
"""""""""""""""""""

- Introduced the "resolution" field to `/sensors/data` (GET) to obtain data in a given resolution.

v3.0-1 | 2022-05-08
"""""""""""""""""""

Expand Down
1 change: 1 addition & 0 deletions documentation/changelog.rst
Expand Up @@ -10,6 +10,7 @@ New features
* Individual sensor charts show available annotations [see `PR #428 <http://www.github.com/FlexMeasures/flexmeasures/pull/428>`_]
* Collapsible sidepanel (hover/swipe) used for date selection on sensor charts, and various styling improvements [see `PR #447 <http://www.github.com/FlexMeasures/flexmeasures/pull/447>`_ and `PR #448 <http://www.github.com/FlexMeasures/flexmeasures/pull/448>`_]
* Switched from 12-hour AM/PM to 24-hour clock notation for time series chart axis labels [see `PR #446 <http://www.github.com/FlexMeasures/flexmeasures/pull/446>`_]
* Get data in a given resolution [see `PR #458 <http://www.github.com/FlexMeasures/flexmeasures/pull/458>`_]

Bugfixes
-----------
Expand Down
7 changes: 6 additions & 1 deletion flexmeasures/api/common/schemas/sensor_data.py
Expand Up @@ -97,6 +97,8 @@ def check_schema_unit_against_sensor_unit(self, data, **kwargs):

class GetSensorDataSchema(SensorDataDescriptionSchema):

resolution = DurationField(required=False)

# Optional field that can be used for extra validation
type = fields.Str(
required=False,
Expand Down Expand Up @@ -141,12 +143,14 @@ def dump_bdf(self, sensor_data_description: dict, **kwargs) -> dict:
- converts to a single deterministic belief per event
- ensures the response respects the requested time frame
- converts values to the requested unit
- converts values to the requested resolution
"""
sensor: Sensor = sensor_data_description["sensor"]
start = sensor_data_description["start"]
duration = sensor_data_description["duration"]
end = sensor_data_description["start"] + duration
unit = sensor_data_description["unit"]
resolution = sensor_data_description.get("resolution")

# Post-load configuration of belief timing against message type
horizons_at_least = sensor_data_description.get("horizon", None)
Expand All @@ -169,13 +173,14 @@ def dump_bdf(self, sensor_data_description: dict, **kwargs) -> dict:
horizons_at_most=horizons_at_most,
beliefs_before=sensor_data_description.get("prior", None),
one_deterministic_belief_per_event=True,
resolution=resolution,
as_json=False,
)
)

# Convert to desired time range
index = pd.date_range(
start=start, end=end, freq=sensor.event_resolution, closed="left"
start=start, end=end, freq=df.event_resolution, closed="left"
)
df = df.reindex(index)

Expand Down
30 changes: 30 additions & 0 deletions flexmeasures/api/common/schemas/tests/test_sensor_data_schema.py
@@ -1,13 +1,43 @@
from datetime import timedelta
import pytest

from marshmallow import ValidationError

from flexmeasures.api.common.schemas.sensor_data import (
SingleValueField,
PostSensorDataSchema,
GetSensorDataSchema,
)


@pytest.mark.parametrize(
"deserialization_input, exp_deserialization_output",
[
(
"PT1H",
timedelta(hours=1),
),
(
"PT15M",
timedelta(minutes=15),
),
],
)
def test_resolution_field_deserialization(
deserialization_input,
exp_deserialization_output,
):
"""Check parsing the resolution field of the GetSensorDataSchema schema.
These particular ISO durations are expected to be parsed as python timedeltas.
"""
# todo: extend test cases with some nominal durations when timely-beliefs supports these
# see https://github.com/SeitaBV/timely-beliefs/issues/13
vf = GetSensorDataSchema._declared_fields["resolution"]
deser = vf.deserialize(deserialization_input)
assert deser == exp_deserialization_output


@pytest.mark.parametrize(
"deserialization_input, exp_deserialization_output",
[
Expand Down
3 changes: 2 additions & 1 deletion flexmeasures/api/dev/sensors.py
Expand Up @@ -10,7 +10,7 @@
from flexmeasures.auth.policy import ADMIN_ROLE, ADMIN_READER_ROLE
from flexmeasures.auth.decorators import permission_required_for_context
from flexmeasures.data.schemas.sensors import SensorIdField
from flexmeasures.data.schemas.times import AwareDateTimeField
from flexmeasures.data.schemas.times import AwareDateTimeField, DurationField
from flexmeasures.data.models.time_series import Sensor
from flexmeasures.data.services.annotations import prepare_annotations_for_chart

Expand Down Expand Up @@ -63,6 +63,7 @@ def get_chart(self, id: int, sensor: Sensor, **kwargs):
"event_ends_before": AwareDateTimeField(format="iso", required=False),
"beliefs_after": AwareDateTimeField(format="iso", required=False),
"beliefs_before": AwareDateTimeField(format="iso", required=False),
"resolution": DurationField(required=False),
},
location="query",
)
Expand Down
1 change: 1 addition & 0 deletions flexmeasures/api/v3_0/sensors.py
Expand Up @@ -173,6 +173,7 @@ def get_data(self, response: dict):
"sensor": "ea1.2021-01.io.flexmeasures:fm1.1",
"start": "2021-06-07T00:00:00+02:00",
"duration": "PT1H",
"resolution": "PT15M",
"unit": "m³/h"
}
Expand Down
2 changes: 2 additions & 0 deletions flexmeasures/data/models/time_series.py
Expand Up @@ -292,6 +292,7 @@ def search_beliefs(
most_recent_events_only: bool = False,
most_recent_only: bool = None, # deprecated
one_deterministic_belief_per_event: bool = False,
resolution: Union[str, timedelta] = None,
as_json: bool = False,
) -> Union[tb.BeliefsDataFrame, str]:
"""Search all beliefs about events for this sensor.
Expand Down Expand Up @@ -331,6 +332,7 @@ def search_beliefs(
most_recent_beliefs_only=most_recent_beliefs_only,
most_recent_events_only=most_recent_events_only,
one_deterministic_belief_per_event=one_deterministic_belief_per_event,
resolution=resolution,
)
if as_json:
df = bdf.reset_index()
Expand Down

0 comments on commit 6e134bd

Please sign in to comment.