From 3f9abbf5a21afa7c61135655d9db0002b39dab2d Mon Sep 17 00:00:00 2001 From: Felix Claessen <30658763+Flix6x@users.noreply.github.com> Date: Fri, 2 Apr 2021 20:16:57 +0200 Subject: [PATCH] Add CLI function to create new sensor (#83) * Add CLI function to create new sensor * Adjustments to CLI print statements * Register Sensor as a structure table for db-ops * Changelog entry * flake8 on merge Co-authored-by: F.N. Claessen --- documentation/changelog.rst | 4 ++ flexmeasures/data/models/assets.py | 4 +- flexmeasures/data/models/time_series.py | 11 +++++- flexmeasures/data/models/weather.py | 4 +- .../data/scripts/cli_tasks/data_add.py | 37 +++++++++++++++++-- flexmeasures/data/scripts/data_gen.py | 2 +- 6 files changed, 52 insertions(+), 10 deletions(-) diff --git a/documentation/changelog.rst b/documentation/changelog.rst index 49fb85af2..d558d9bb4 100644 --- a/documentation/changelog.rst +++ b/documentation/changelog.rst @@ -6,6 +6,10 @@ FlexMeasures Changelog v0.2.5 | April XX, 2021 =========================== +New features +----------- +* Add sensors with CLI command [see `PR #83 `_] + Infrastructure / Support ---------------------- * Updated dependencies, including Flask-Security-Too [see `PR #82 `_] diff --git a/flexmeasures/data/models/assets.py b/flexmeasures/data/models/assets.py index cf6e16b73..68a0a18f3 100644 --- a/flexmeasures/data/models/assets.py +++ b/flexmeasures/data/models/assets.py @@ -7,7 +7,7 @@ from flexmeasures.data.config import db from flexmeasures.data import ma -from flexmeasures.data.models.time_series import Sensor, SensorSchema, TimedValue +from flexmeasures.data.models.time_series import Sensor, SensorSchemaMixin, TimedValue from flexmeasures.data.models.markets import Market from flexmeasures.data.models.user import User from flexmeasures.utils.entity_address_utils import build_entity_address @@ -179,7 +179,7 @@ def __repr__(self): ) -class AssetSchema(SensorSchema, ma.SQLAlchemySchema): +class AssetSchema(SensorSchemaMixin, ma.SQLAlchemySchema): """ Asset schema, with validations. """ diff --git a/flexmeasures/data/models/time_series.py b/flexmeasures/data/models/time_series.py index 98f91b85f..c1f940ebf 100644 --- a/flexmeasures/data/models/time_series.py +++ b/flexmeasures/data/models/time_series.py @@ -22,7 +22,7 @@ class Sensor(db.Model, tb.SensorDBMixin): """A sensor measures events. """ -class SensorSchema(Schema): +class SensorSchemaMixin(Schema): """ Base sensor schema. @@ -42,6 +42,15 @@ class Meta: event_resolution = fields.TimeDelta(required=True, precision="minutes") +class SensorSchema(SensorSchemaMixin, ma.SQLAlchemySchema): + """ + Sensor schema, with validations. + """ + + class Meta: + model = Sensor + + class TimedValue(object): """ A mixin of all tables that store time series data, either forecasts or measurements. diff --git a/flexmeasures/data/models/weather.py b/flexmeasures/data/models/weather.py index 00dcc05dd..a2d36c16c 100644 --- a/flexmeasures/data/models/weather.py +++ b/flexmeasures/data/models/weather.py @@ -11,7 +11,7 @@ from flexmeasures.data.config import db from flexmeasures.data import ma -from flexmeasures.data.models.time_series import Sensor, SensorSchema, TimedValue +from flexmeasures.data.models.time_series import Sensor, SensorSchemaMixin, TimedValue from flexmeasures.utils.geo_utils import parse_lat_lng from flexmeasures.utils.flexmeasures_inflection import humanize @@ -171,7 +171,7 @@ def to_dict(self) -> Dict[str, str]: return dict(name=self.name, sensor_type=self.weather_sensor_type_name) -class WeatherSensorSchema(SensorSchema, ma.SQLAlchemySchema): +class WeatherSensorSchema(SensorSchemaMixin, ma.SQLAlchemySchema): """ WeatherSensor schema, with validations. """ diff --git a/flexmeasures/data/scripts/cli_tasks/data_add.py b/flexmeasures/data/scripts/cli_tasks/data_add.py index d5152a1c3..ff8b7e4ed 100644 --- a/flexmeasures/data/scripts/cli_tasks/data_add.py +++ b/flexmeasures/data/scripts/cli_tasks/data_add.py @@ -13,6 +13,7 @@ from flexmeasures.data.services.forecasting import create_forecasting_jobs from flexmeasures.data.services.users import create_user +from flexmeasures.data.models.time_series import Sensor, SensorSchema from flexmeasures.data.models.assets import Asset, AssetSchema from flexmeasures.data.models.markets import Market from flexmeasures.data.models.weather import WeatherSensor, WeatherSensorSchema @@ -62,6 +63,34 @@ def new_user(username: str, email: str, roles: List[str], timezone: str): print(f"Successfully created user {created_user}") +@fm_add_data.command("sensor") +@with_appcontext +@click.option("--name", required=True) +@click.option("--unit", required=True, help="e.g. °C, m/s, kW/m²") +@click.option( + "--event-resolution", + required=True, + type=int, + help="Expected resolution of the data in minutes", +) +@click.option( + "--timezone", + required=True, + help="timezone as string, e.g. 'UTC' or 'Europe/Amsterdam'", +) +def add_sensor(**args): + """Add a sensor.""" + check_timezone(args["timezone"]) + check_errors(SensorSchema().validate(args)) + args["event_resolution"] = timedelta(minutes=args["event_resolution"]) + sensor = Sensor(**args) + app.db.session.add(sensor) + app.db.session.commit() + print(f"Successfully created sensor with ID {sensor.id}") + # TODO: uncomment when #66 has landed + # print(f"You can access it at its entity address {sensor.entity_address}") + + @fm_add_data.command("asset") @with_appcontext @click.option("--name", required=True) @@ -118,8 +147,8 @@ def new_asset(**args): asset = Asset(**args) app.db.session.add(asset) app.db.session.commit() - print(f"Successfully created asset with ID:{asset.id}.") - print(f" You can access it at its entity address {asset.entity_address}") + print(f"Successfully created asset with ID {asset.id}") + print(f"You can access it at its entity address {asset.entity_address}") @fm_add_data.command("weather-sensor") @@ -158,9 +187,9 @@ def add_weather_sensor(**args): sensor = WeatherSensor(**args) app.db.session.add(sensor) app.db.session.commit() - print(f"Successfully created sensor with ID:{sensor.id}.") + print(f"Successfully created sensor with ID {sensor.id}") # TODO: uncomment when #66 has landed - # print(f" You can access it at its entity address {sensor.entity_address}") + # print(f"You can access it at its entity address {sensor.entity_address}") @fm_add_data.command("structure") diff --git a/flexmeasures/data/scripts/data_gen.py b/flexmeasures/data/scripts/data_gen.py index 700838bd2..9848bdc42 100644 --- a/flexmeasures/data/scripts/data_gen.py +++ b/flexmeasures/data/scripts/data_gen.py @@ -18,8 +18,8 @@ from humanize import naturaldelta import inflect -from flexmeasures.data.models.markets import MarketType, Market, Price from flexmeasures.data.models.time_series import Sensor +from flexmeasures.data.models.markets import MarketType, Market, Price from flexmeasures.data.models.assets import AssetType, Asset, Power from flexmeasures.data.models.data_sources import DataSource from flexmeasures.data.models.weather import WeatherSensorType, WeatherSensor, Weather