Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get resampled data #458

Merged
merged 12 commits into from Jul 12, 2022
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>`_]
* 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
25 changes: 25 additions & 0 deletions flexmeasures/api/common/schemas/tests/test_sensor_data_schema.py
@@ -1,13 +1,38 @@
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(
Copy link
Contributor

Choose a reason for hiding this comment

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

So here we're testing that timely beliefs changes the representation of the field.
Is there no need to use some data to test resolution adaptations? Do we already test that someplace else (testing the search function?

I noticed that in the API level we are not testing GETting sensor data, just posting...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not timely-beliefs. This checks whether the given marshmallow schema in FlexMeasures parses these particular ISO durations as python timedeltas. I'd love to extend this with some nominal durations, but unfortunately timely-beliefs only works with absolute durations for now. I'll add this info to the test.

Low level tests for resampling data should happen in timely-beliefs imo.

deserialization_input,
exp_deserialization_output,
):
"""Testing straightforward cases"""
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