Skip to content

Commit

Permalink
Merge branch 'remove-obsolete-data-classes' of github.com:FlexMeasure…
Browse files Browse the repository at this point in the history
…s/flexmeasures into remove-obsolete-data-classes
  • Loading branch information
nhoening committed Jul 13, 2023
2 parents ede6df9 + 63d4cb7 commit 090529b
Show file tree
Hide file tree
Showing 8 changed files with 18 additions and 1,130 deletions.
2 changes: 1 addition & 1 deletion documentation/api/notation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ It uses the fact that all FlexMeasures sensors have unique IDs.
The ``fm0`` scheme is the original scheme.
It identified different types of sensors (such as grid connections, weather sensors and markets) in different ways.
The ``fm0`` scheme has been deprecated and is no longer supported officially.
The ``fm0`` scheme has been sunset since API version 3.


Timeseries
Expand Down
25 changes: 5 additions & 20 deletions flexmeasures/api/common/schemas/sensors.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
from marshmallow import fields

from flexmeasures.api import FMValidationError
from flexmeasures.api.common.utils.api_utils import (
get_sensor_by_generic_asset_type_and_location,
)
from flexmeasures.utils.entity_address_utils import (
parse_entity_address,
EntityAddressException,
Expand Down Expand Up @@ -33,20 +30,21 @@ def _serialize(self, sensor: Sensor, attr, data, **kwargs) -> int:

class SensorField(fields.Str):
"""Field that de-serializes to a Sensor,
and serializes a Sensor, Asset, Market or WeatherSensor into an entity address (string)."""
and serializes a Sensor into an entity address (string).
"""

# todo: when Actuators also get an entity address, refactor this class to EntityField,
# where an Entity represents anything with an entity address: we currently foresee Sensors and Actuators

def __init__(
self,
entity_type: str,
fm_scheme: str,
fm_scheme: str = "fm1",
*args,
**kwargs,
):
"""
:param entity_type: "sensor", "connection", "market" or "weather_sensor"
:param entity_type: "sensor" (in the future, possibly also another type of resource that is assigned an entity address)
:param fm_scheme: "fm0" or "fm1"
"""
self.entity_type = entity_type
Expand All @@ -58,20 +56,7 @@ def _deserialize(self, value, attr, obj, **kwargs) -> Sensor:
try:
ea = parse_entity_address(value, self.entity_type, self.fm_scheme)
if self.fm_scheme == "fm0":
if self.entity_type == "connection":
sensor = Sensor.query.filter(
Sensor.id == ea["asset_id"]
).one_or_none()
elif self.entity_type == "market":
sensor = Sensor.query.filter(
Sensor.name == ea["market_name"]
).one_or_none()
elif self.entity_type == "weather_sensor":
sensor = get_sensor_by_generic_asset_type_and_location(
ea["weather_sensor_type_name"], ea["latitude"], ea["longitude"]
)
else:
return NotImplemented
raise EntityAddressException("The fm0 scheme is no longer supported.")
else:
sensor = Sensor.query.filter(Sensor.id == ea["sensor_id"]).one_or_none()
if sensor is not None:
Expand Down
35 changes: 9 additions & 26 deletions flexmeasures/api/common/schemas/tests/test_sensors.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,6 @@
"fm1",
"height",
),
(
build_entity_address(
dict(market_name="epex_da"), "market", fm_scheme="fm0"
),
"market",
"fm0",
"epex_da",
),
(
build_entity_address(
dict(owner_id=1, asset_id=4), "connection", fm_scheme="fm0"
),
"connection",
"fm0",
"Test battery with no known prices",
),
],
)
def test_sensor_field_straightforward(
Expand All @@ -47,38 +31,37 @@ def test_sensor_field_straightforward(
sf = SensorField(entity_type, fm_scheme)
deser = sf.deserialize(entity_address, None, None)
assert deser.name == exp_deserialization_name
if fm_scheme == "fm0" and entity_type in ("connection", "market", "weather_sensor"):
# These entity types are deserialized to Sensors, which have no entity address under the fm0 scheme
return
assert sf.serialize(entity_type, {entity_type: deser}) == entity_address


@pytest.mark.parametrize(
"entity_address, entity_type, fm_scheme, error_msg",
[
(
"ea1.2021-01.io.flexmeasures:some.weird:identifier%that^is*not)used",
build_entity_address(
dict(market_name="epex_da"), "market", fm_scheme="fm0"
),
"market",
"fm0",
"Could not parse",
"fm0 scheme is no longer supported",
),
(
"ea1.2021-01.io.flexmeasures:fm1.some.weird:identifier%that^is*not)used",
"market",
"sensor",
"fm1",
"Could not parse",
),
(
build_entity_address(
dict(market_name="non_existing_market"), "market", fm_scheme="fm0"
dict(sensor_id=99999999999999), "sensor", fm_scheme="fm1"
),
"market",
"fm0",
"sensor",
"fm1",
"doesn't exist",
),
(
build_entity_address(dict(sensor_id=-1), "sensor", fm_scheme="fm1"),
"market",
"sensor",
"fm1",
"Could not parse",
),
Expand Down
58 changes: 0 additions & 58 deletions flexmeasures/api/common/utils/api_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,9 @@
import timely_beliefs as tb

from flexmeasures.data import db
from flexmeasures.data.models.generic_assets import GenericAsset, GenericAssetType
from flexmeasures.data.models.time_series import Sensor
from flexmeasures.data.models.weather import WeatherSensor
from flexmeasures.data.utils import save_to_db as modern_save_to_db
from flexmeasures.api.common.responses import (
invalid_replacement,
unrecognized_sensor,
ResponseTuple,
request_processed,
already_received_and_successfully_processed,
Expand Down Expand Up @@ -240,60 +236,6 @@ def unique_ever_seen(iterable: Sequence, selector: Sequence):
return u, s


def get_sensor_by_generic_asset_type_and_location(
generic_asset_type_name: str, latitude: float = 0, longitude: float = 0
) -> Union[Sensor, ResponseTuple]:
"""
Search a sensor by generic asset type and location.
Can create a sensor if needed (depends on API mode)
and then inform the requesting user which one to use.
"""
# Look for the Sensor object
sensor = (
Sensor.query.join(GenericAsset)
.join(GenericAssetType)
.filter(GenericAssetType.name == generic_asset_type_name)
.filter(GenericAsset.generic_asset_type_id == GenericAssetType.id)
.filter(GenericAsset.latitude == latitude)
.filter(GenericAsset.longitude == longitude)
.filter(Sensor.generic_asset_id == GenericAsset.id)
.one_or_none()
)
if sensor is None:
create_sensor_if_unknown = False
if current_app.config.get("FLEXMEASURES_MODE", "") == "play":
create_sensor_if_unknown = True

# either create a new weather sensor and post to that
if create_sensor_if_unknown:
current_app.logger.info("CREATING NEW WEATHER SENSOR...")
weather_sensor = WeatherSensor(
name="Weather sensor for %s at latitude %s and longitude %s"
% (generic_asset_type_name, latitude, longitude),
weather_sensor_type_name=generic_asset_type_name,
latitude=latitude,
longitude=longitude,
)
db.session.add(weather_sensor)
db.session.flush() # flush so that we can reference the new object in the current db session
sensor = weather_sensor.corresponding_sensor

# or query and return the nearest sensor and let the requesting user post to that one
else:
nearest_weather_sensor = WeatherSensor.query.order_by(
WeatherSensor.great_circle_distance(
latitude=latitude, longitude=longitude
).asc()
).first()
if nearest_weather_sensor is not None:
return unrecognized_sensor(
*nearest_weather_sensor.location,
)
else:
return unrecognized_sensor()
return sensor


def enqueue_forecasting_jobs(
forecasting_jobs: list[Job] | None = None,
):
Expand Down
Loading

0 comments on commit 090529b

Please sign in to comment.