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

first working version of a charts/power view; #39

Merged
merged 13 commits into from Feb 25, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 10 additions & 2 deletions flexmeasures/api/common/schemas.py
Expand Up @@ -4,6 +4,7 @@
from marshmallow import fields, ValidationError
import isodate
from isodate.isoerror import ISO8601Error
import pandas as pd


class DurationField(fields.Str):
Expand Down Expand Up @@ -43,8 +44,15 @@ def ground_from(
converting to a datetime.timedelta is not possible (no obvious
number of days). In this case, `_deserialize` returned an
`isodate.Duration`. We can derive the timedelta by grounding to an
actual time span, for which we require a start datetime.
actual time span, for which we require a timezone-aware start datetime.
"""
if isinstance(duration, isodate.Duration) and start:
return (start + duration) - start
years = duration.years
months = duration.months
days = duration.days
seconds = duration.tdelta.seconds
offset = pd.DateOffset(
years=years, months=months, days=days, seconds=seconds
)
return (pd.Timestamp(start) + offset).to_pydatetime() - start
return duration
15 changes: 13 additions & 2 deletions flexmeasures/api/common/tests/test_schemas.py
@@ -1,6 +1,7 @@
from datetime import datetime, timedelta

import pytest
import pytz
import isodate
from marshmallow import ValidationError

Expand Down Expand Up @@ -30,6 +31,12 @@ def test_duration_field_straightforward(duration_input, exp_deserialization):
("P1M", isodate.Duration(months=1), timedelta(days=29)),
("PT24H", isodate.Duration(hours=24), timedelta(hours=24)),
("P2D", isodate.Duration(hours=48), timedelta(hours=48)),
# following are calendar periods including a transition to daylight saving time (DST)
("P2M", isodate.Duration(months=2), timedelta(days=60) - timedelta(hours=1)),
# ("P8W", isodate.Duration(days=7*8), timedelta(weeks=8) - timedelta(hours=1)),
# ("P100D", isodate.Duration(days=100), timedelta(days=100) - timedelta(hours=1)),
# following is a calendar period with transitions to DST and back again
("P1Y", isodate.Duration(years=1), timedelta(days=366)),
],
)
def test_duration_field_nominal_grounded(
Expand All @@ -38,12 +45,16 @@ def test_duration_field_nominal_grounded(
"""Nominal durations are tricky:
https://en.wikipedia.org/wiki/Talk:ISO_8601/Archive_2#Definition_of_Duration_is_incorrect
We want to test if we can ground them as expected.
We use a particular datetime to ground, in a leap year february.
We use a particular datetime to ground, in a leap year February.
For the Europe/Amsterdam timezone, daylight saving time started on March 29th 2020.
# todo: the commented out tests would work if isodate.parse_duration would have the option to stop coercing ISO 8601 days into datetime.timedelta days
nhoening marked this conversation as resolved.
Show resolved Hide resolved
"""
df = DurationField()
deser = df.deserialize(duration_input, None, None)
assert deser == exp_deserialization
dummy_time = datetime(2020, 2, 22, 18, 7)
dummy_time = pytz.timezone("Europe/Amsterdam").localize(
datetime(2020, 2, 22, 18, 7)
)
grounded = DurationField.ground_from(deser, dummy_time)
assert grounded == grounded_timedelta

Expand Down