diff --git a/flexmeasures/api/__init__.py b/flexmeasures/api/__init__.py index ef838271c..32b008f51 100644 --- a/flexmeasures/api/__init__.py +++ b/flexmeasures/api/__init__.py @@ -105,9 +105,11 @@ def register_at(app: Flask): from flexmeasures.api.v1_2 import register_at as v1_2_register_at from flexmeasures.api.v1_3 import register_at as v1_3_register_at from flexmeasures.api.v2_0 import register_at as v2_0_register_at + from flexmeasures.api.dev import register_at as dev_register_at v1_register_at(app) v1_1_register_at(app) v1_2_register_at(app) v1_3_register_at(app) v2_0_register_at(app) + dev_register_at(app) diff --git a/flexmeasures/api/dev/__init__.py b/flexmeasures/api/dev/__init__.py new file mode 100644 index 000000000..c175be741 --- /dev/null +++ b/flexmeasures/api/dev/__init__.py @@ -0,0 +1,9 @@ +from flask import Flask + + +def register_at(app: Flask): + """This can be used to register FlaskViews.""" + + from flexmeasures.api.dev.sensors import SensorAPI + + SensorAPI.register(app, route_prefix="/api/dev") diff --git a/flexmeasures/api/dev/sensors.py b/flexmeasures/api/dev/sensors.py new file mode 100644 index 000000000..a28e44e1c --- /dev/null +++ b/flexmeasures/api/dev/sensors.py @@ -0,0 +1,73 @@ +from flask_classful import FlaskView, route +from flask_login import login_required +from flask_security import roles_required +from marshmallow import fields +from webargs.flaskparser import use_kwargs +from werkzeug.exceptions import abort + +from flexmeasures.api.common.schemas.times import AwareDateTimeField +from flexmeasures.data.models.time_series import Sensor + + +class SensorAPI(FlaskView): + """ + This view exposes sensor attributes through API endpoints under development. + These endpoints are not yet part of our official API, but support the FlexMeasures UI. + """ + + route_base = "/sensor" + + @login_required + @roles_required("admin") # todo: remove after we check for sensor ownership + @route("//chart/") + @use_kwargs( + { + "event_starts_after": AwareDateTimeField(format="iso", required=False), + "event_ends_before": AwareDateTimeField(format="iso", required=False), + "beliefs_after": AwareDateTimeField(format="iso", required=False), + "beliefs_before": AwareDateTimeField(format="iso", required=False), + "include_data": fields.Boolean(required=False), + "as_html": fields.Boolean(required=False), + "dataset_name": fields.Str(required=False), + }, + location="query", + ) + def get_chart(self, id, **kwargs): + """GET from /sensor//chart""" + sensor = get_sensor_or_abort(id) + return sensor.chart(**kwargs) + + @login_required + @roles_required("admin") # todo: remove after we check for sensor ownership + @route("//chart_data/") + @use_kwargs( + { + "event_starts_after": AwareDateTimeField(format="iso", required=False), + "event_ends_before": AwareDateTimeField(format="iso", required=False), + "beliefs_after": AwareDateTimeField(format="iso", required=False), + "beliefs_before": AwareDateTimeField(format="iso", required=False), + }, + location="query", + ) + def get_chart_data(self, id, **kwargs): + """GET from /sensor//chart_data + + Data for use in charts (in case you have the chart specs already). + """ + sensor = get_sensor_or_abort(id) + return sensor.search_beliefs(as_json=True, **kwargs) + + @login_required + @roles_required("admin") # todo: remove after we check for sensor ownership + def get(self, id: int): + """GET from /sensor/""" + sensor = get_sensor_or_abort(id) + attributes = ["name", "timezone", "timerange"] + return {attr: getattr(sensor, attr) for attr in attributes} + + +def get_sensor_or_abort(id: int) -> Sensor: + sensor = Sensor.query.filter(Sensor.id == id).one_or_none() + if sensor is None: + raise abort(404, f"Sensor {id} not found") + return sensor diff --git a/flexmeasures/ui/__init__.py b/flexmeasures/ui/__init__.py index 2107e5755..6348aa578 100644 --- a/flexmeasures/ui/__init__.py +++ b/flexmeasures/ui/__init__.py @@ -33,12 +33,11 @@ def register_at(app: Flask): from flexmeasures.ui.crud.assets import AssetCrudUI from flexmeasures.ui.crud.users import UserCrudUI - from flexmeasures.ui.views.sensors import SensorUI, SensorAPI + from flexmeasures.ui.views.sensors import SensorUI AssetCrudUI.register(app) UserCrudUI.register(app) SensorUI.register(app) - SensorAPI.register(app) import flexmeasures.ui.views # noqa: F401 this is necessary to load the views diff --git a/flexmeasures/ui/views/sensors.py b/flexmeasures/ui/views/sensors.py index ffaa28f90..5be5e1763 100644 --- a/flexmeasures/ui/views/sensors.py +++ b/flexmeasures/ui/views/sensors.py @@ -1,11 +1,10 @@ -from flask import abort from flask_classful import FlaskView, route from flask_security import login_required, roles_required from marshmallow import fields from webargs.flaskparser import use_kwargs from flexmeasures.api.common.schemas.times import AwareDateTimeField -from flexmeasures.data.models.time_series import Sensor +from flexmeasures.api.dev.sensors import SensorAPI from flexmeasures.ui.utils.view_utils import render_flexmeasures_template @@ -44,67 +43,3 @@ def get(self, id: int): sensor_id=id, msg="", ) - - -class SensorAPI(FlaskView): - """ - This view exposes sensor attributes through API endpoints under development. - These endpoints are not yet part of our official API, but support the FlexMeasures UI. - """ - - route_base = "/sensor" - - @login_required - @roles_required("admin") # todo: remove after we check for sensor ownership - @route("//chart/") - @use_kwargs( - { - "event_starts_after": AwareDateTimeField(format="iso", required=False), - "event_ends_before": AwareDateTimeField(format="iso", required=False), - "beliefs_after": AwareDateTimeField(format="iso", required=False), - "beliefs_before": AwareDateTimeField(format="iso", required=False), - "include_data": fields.Boolean(required=False), - "as_html": fields.Boolean(required=False), - "dataset_name": fields.Str(required=False), - }, - location="query", - ) - def get_chart(self, id, **kwargs): - """GET from /sensor//chart""" - sensor = get_sensor_or_abort(id) - return sensor.chart(**kwargs) - - @login_required - @roles_required("admin") # todo: remove after we check for sensor ownership - @route("//chart_data/") - @use_kwargs( - { - "event_starts_after": AwareDateTimeField(format="iso", required=False), - "event_ends_before": AwareDateTimeField(format="iso", required=False), - "beliefs_after": AwareDateTimeField(format="iso", required=False), - "beliefs_before": AwareDateTimeField(format="iso", required=False), - }, - location="query", - ) - def get_chart_data(self, id, **kwargs): - """GET from /sensor//chart_data - - Data for use in charts (in case you have the chart specs already). - """ - sensor = get_sensor_or_abort(id) - return sensor.search_beliefs(as_json=True, **kwargs) - - @login_required - @roles_required("admin") # todo: remove after we check for sensor ownership - def get(self, id: int): - """GET from /sensor/""" - sensor = get_sensor_or_abort(id) - attributes = ["name", "timezone", "timerange"] - return {attr: getattr(sensor, attr) for attr in attributes} - - -def get_sensor_or_abort(id: int) -> Sensor: - sensor = Sensor.query.filter(Sensor.id == id).one_or_none() - if sensor is None: - raise abort(404, f"Sensor {id} not found") - return sensor