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

Let CLI package use custom Marshmallow Field definitions #125

Merged
2 changes: 1 addition & 1 deletion flexmeasures/api/__init__.py
Expand Up @@ -6,9 +6,9 @@
from flexmeasures import __version__ as flexmeasures_version
from flexmeasures.data.models.user import User
from flexmeasures.api.common.utils.args_parsing import (
FMValidationError,
validation_error_handler,
)
from flexmeasures.data.schemas.utils import FMValidationError

# The api blueprint. It is registered with the Flask app (see app.py)
flexmeasures_api = Blueprint("flexmeasures_api", __name__)
Expand Down
13 changes: 1 addition & 12 deletions flexmeasures/api/common/utils/args_parsing.py
@@ -1,4 +1,5 @@
from flask import jsonify
from flexmeasures.data.schemas.utils import FMValidationError
from webargs.multidictproxy import MultiDictProxy
from webargs import ValidationError
from webargs.flaskparser import parser
Expand All @@ -18,18 +19,6 @@ def handle_error(error, req, schema, *, error_status_code, error_headers):
raise error


class FMValidationError(ValidationError):
"""
Custom validation error class.
It differs from the classic validation error by having two
attributes, according to the USEF 2015 reference implementation.
Subclasses of this error might adjust the `status` attribute accordingly.
"""

result = "Rejected"
status = "UNPROCESSABLE_ENTITY"


def validation_error_handler(error: FMValidationError):
"""Handles errors during parsing.
Aborts the current HTTP request and responds with a 422 error.
Expand Down
2 changes: 1 addition & 1 deletion flexmeasures/api/common/utils/validators.py
Expand Up @@ -16,7 +16,7 @@

from webargs.flaskparser import parser

from flexmeasures.api.common.schemas.times import DurationField
from flexmeasures.data.schemas.times import DurationField
from flexmeasures.api.common.responses import ( # noqa: F401
required_info_missing,
invalid_horizon,
Expand Down
2 changes: 1 addition & 1 deletion flexmeasures/api/dev/sensors.py
Expand Up @@ -7,7 +7,7 @@
from webargs.flaskparser import use_kwargs
from werkzeug.exceptions import abort

from flexmeasures.api.common.schemas.times import AwareDateTimeField
from flexmeasures.data.schemas.times import AwareDateTimeField
from flexmeasures.data.models.time_series import Sensor


Expand Down
10 changes: 10 additions & 0 deletions flexmeasures/data/models/fields.py
@@ -0,0 +1,10 @@
import click
import marshmallow as ma


class MarshmallowClickMixin(click.ParamType):
def convert(self, value, param, ctx, **kwargs):
try:
return self.deserialize(value, **kwargs)
except ma.exceptions.ValidationError as e:
raise click.exceptions.BadParameter(e, ctx=ctx, param=param)
Expand Up @@ -4,7 +4,7 @@
import pytz
import isodate

from flexmeasures.api.common.schemas.times import DurationField, DurationValidationError
from flexmeasures.data.schemas.times import DurationField, DurationValidationError


@pytest.mark.parametrize(
Expand Down
Expand Up @@ -6,14 +6,15 @@
from isodate.isoerror import ISO8601Error
import pandas as pd

from flexmeasures.api.common.utils.args_parsing import FMValidationError
from flexmeasures.data.schemas.utils import FMValidationError
from flexmeasures.data.models.fields import MarshmallowClickMixin


class DurationValidationError(FMValidationError):
status = "INVALID_PERIOD" # USEF error status


class DurationField(fields.Str):
class DurationField(fields.Str, MarshmallowClickMixin):
"""Field that deserializes to a ISO8601 Duration
and serializes back to a string."""

Expand Down Expand Up @@ -64,7 +65,7 @@ def ground_from(
return duration


class AwareDateTimeField(fields.AwareDateTime):
class AwareDateTimeField(fields.AwareDateTime, MarshmallowClickMixin):
"""Field that deserializes to a timezone aware datetime
and serializes back to a string."""

Expand Down
13 changes: 13 additions & 0 deletions flexmeasures/data/schemas/utils.py
@@ -0,0 +1,13 @@
from marshmallow import ValidationError


class FMValidationError(ValidationError):
"""
Custom validation error class.
It differs from the classic validation error by having two
attributes, according to the USEF 2015 reference implementation.
Subclasses of this error might adjust the `status` attribute accordingly.
"""

result = "Rejected"
status = "UNPROCESSABLE_ENTITY"
2 changes: 1 addition & 1 deletion flexmeasures/ui/views/charts.py
Expand Up @@ -6,7 +6,7 @@

from flexmeasures.api.v2_0 import flexmeasures_api as flexmeasures_api_v2_0
from flexmeasures.api.v2_0.routes import v2_0_service_listing
from flexmeasures.api.common.schemas.times import DurationField
from flexmeasures.data.schemas.times import DurationField
from flexmeasures.data.queries.analytics import get_power_data
from flexmeasures.ui.views.analytics import make_power_figure

Expand Down
2 changes: 1 addition & 1 deletion flexmeasures/ui/views/sensors.py
Expand Up @@ -5,7 +5,7 @@
from marshmallow import fields
from webargs.flaskparser import use_kwargs

from flexmeasures.api.common.schemas.times import AwareDateTimeField
from flexmeasures.data.schemas.times import AwareDateTimeField
from flexmeasures.api.dev.sensors import SensorAPI
from flexmeasures.ui.utils.view_utils import render_flexmeasures_template

Expand Down