Skip to content

Commit

Permalink
Price and inflexible device sensors data to connectivity table on ass…
Browse files Browse the repository at this point in the history
…et status page

Signed-off-by: Nikolai <nrozanov@iponweb.net>
  • Loading branch information
Nikolai committed Jun 20, 2024
1 parent 4d5ece1 commit 1c6c988
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 30 deletions.
58 changes: 54 additions & 4 deletions flexmeasures/api/common/schemas/tests/test_sensor_data_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
PostSensorDataSchema,
GetSensorDataSchema,
)
from flexmeasures.data.models.generic_assets import (
GenericAssetInflexibleSensorRelationship,
)
from flexmeasures.data.models.time_series import Sensor
from flexmeasures.data.services.forecasting import create_forecasting_jobs
from flexmeasures.data.services.scheduling import create_scheduling_job
from flexmeasures.data.services.sensors import (
Expand Down Expand Up @@ -258,20 +262,66 @@ def test_get_status(
assert sensor_status["stale"] == expected_stale


def test_build_asset_status_data(mock_get_status, add_weather_sensors):
def test_build_asset_status_data(
db, mock_get_status, add_weather_sensors, add_battery_assets
):
asset = add_weather_sensors["asset"]
battery_asset = add_battery_assets["Test battery"]
wind_sensor, temperature_sensor = (
add_weather_sensors["wind"],
add_weather_sensors["temperature"],
)

production_price_sensor = Sensor(
name="production price",
generic_asset=battery_asset,
event_resolution=timedelta(minutes=5),
unit="EUR/MWh",
)
db.session.add(production_price_sensor)
db.session.flush()

asset.consumption_price_sensor_id = wind_sensor.id
asset.production_price_sensor_id = production_price_sensor.id
db.session.add(asset)

asset_inflexible_temperature_sensor_relationship = (
GenericAssetInflexibleSensorRelationship(
generic_asset_id=asset.id, inflexible_sensor_id=temperature_sensor.id
)
)
db.session.add(asset_inflexible_temperature_sensor_relationship)

wind_speed_res, temperature_res = {"staleness": True}, {"staleness": False}
mock_get_status.side_effect = (wind_speed_res, temperature_res)
production_price_res = {"staleness": True}
mock_get_status.side_effect = (
wind_speed_res,
temperature_res,
production_price_res,
)

status_data = build_sensor_status_data(asset=asset)
assert status_data == [
{**wind_speed_res, "name": "wind speed", "id": None, "asset_name": asset.name},
{
**wind_speed_res,
"name": "wind speed",
"id": wind_sensor.id,
"asset_name": asset.name,
"relation": "ownership;consumption price",
},
{
**temperature_res,
"name": "temperature",
"id": None,
"id": temperature_sensor.id,
"asset_name": asset.name,
"relation": "ownership;inflexible device",
},
{
**production_price_res,
"name": "production price",
"id": production_price_sensor.id,
"asset_name": battery_asset.name,
"relation": "production price",
},
]

Expand Down
30 changes: 30 additions & 0 deletions flexmeasures/data/models/generic_assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,13 @@ def set_attribute(self, attribute: str, value):

def get_consumption_price_sensor(self):
"""Searches for consumption_price_sensor upwards on the asset tree"""

from flexmeasures.data.models.time_series import Sensor

if self.consumption_price_sensor_id and not self.consumption_price_sensor:
self.consumption_price_sensor = Sensor.query.get(
self.consumption_price_sensor_id
)
if self.consumption_price_sensor:
return self.consumption_price_sensor
if self.parent_asset:
Expand All @@ -270,6 +277,13 @@ def get_consumption_price_sensor(self):

def get_production_price_sensor(self):
"""Searches for production_price_sensor upwards on the asset tree"""

from flexmeasures.data.models.time_series import Sensor

if self.production_price_sensor_id and not self.production_price_sensor:
self.production_price_sensor = Sensor.query.get(
self.production_price_sensor_id
)
if self.production_price_sensor:
return self.production_price_sensor
if self.parent_asset:
Expand All @@ -281,6 +295,22 @@ def get_inflexible_device_sensors(self):
Searches for inflexible_device_sensors upwards on the asset tree
This search will stop once any sensors are found (will not aggregate towards the top of the tree)
"""

from flexmeasures.data.models.time_series import Sensor

if not self.inflexible_device_sensors:
self.inflexible_device_sensors = (
db.session.query(Sensor)
.join(
GenericAssetInflexibleSensorRelationship,
GenericAssetInflexibleSensorRelationship.inflexible_sensor_id
== Sensor.id,
)
.filter(
GenericAssetInflexibleSensorRelationship.generic_asset_id == self.id
)
.all()
)
if self.inflexible_device_sensors:
return self.inflexible_device_sensors
if self.parent_asset:
Expand Down
47 changes: 44 additions & 3 deletions flexmeasures/data/services/sensors.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,26 @@ def get_status(
return status


def _get_sensor_asset_relation(
asset: Asset,
sensor: Sensor,
inflexible_device_sensors: list[Sensor],
) -> str:
"""Get the relation of a sensor to an asset."""
relations = list()
if sensor.generic_asset_id == asset.id:
relations.append("ownership")
if asset.consumption_price_sensor_id == sensor.id:
relations.append("consumption price")
if asset.production_price_sensor_id == sensor.id:
relations.append("production price")
inflexible_device_sensors_ids = {sensor.id for sensor in inflexible_device_sensors}
if sensor.id in inflexible_device_sensors_ids:
relations.append("inflexible device")

return ";".join(relations)


def build_sensor_status_data(
asset: Asset,
now: datetime = None,
Expand All @@ -184,20 +204,41 @@ def build_sensor_status_data(
- stale: whether the sensor is stale
- staleness_since: time since sensor data is considered stale
- reason: reason for staleness
- relation: relation of the sensor to the asset
"""
if not now:
now = server_now()

sensors = []
for asset in (asset, *asset.child_assets):
for sensor in asset.sensors:
sensor_ids = set()
production_price_sensor = asset.get_production_price_sensor()
consumption_price_sensor = asset.get_consumption_price_sensor()
inflexible_device_sensors = asset.get_inflexible_device_sensors()
for asset, is_child_asset in (
(asset, False),
*[(child_asset, True) for child_asset in asset.child_assets],
):
sensors_list = list(asset.sensors)
if not is_child_asset:
sensors_list += [
*asset.inflexible_device_sensors,
production_price_sensor,
consumption_price_sensor,
]
for sensor in sensors_list:
if sensor is None or sensor.id in sensor_ids:
continue
sensor_status = get_status(
sensor=sensor,
now=now,
)
sensor_status["name"] = sensor.name
sensor_status["id"] = sensor.id
sensor_status["asset_name"] = asset.name
sensor_status["asset_name"] = sensor.generic_asset.name
sensor_status["relation"] = _get_sensor_asset_relation(
asset, sensor, inflexible_device_sensors
)
sensor_ids.add(sensor.id)
sensors.append(sensor_status)
return sensors

Expand Down
26 changes: 3 additions & 23 deletions flexmeasures/ui/crud/assets/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,7 @@

from flexmeasures.auth.policy import user_has_admin_access
from flexmeasures.data import db
from flexmeasures.data.models.generic_assets import (
GenericAsset,
GenericAssetType,
GenericAssetInflexibleSensorRelationship,
)
from flexmeasures.data.models.generic_assets import GenericAsset, GenericAssetType
from flexmeasures.data.models.user import Account
from flexmeasures.data.models.time_series import Sensor
from flexmeasures.ui.crud.assets.utils import (
Expand Down Expand Up @@ -154,25 +150,9 @@ def with_inflexible_sensors(
allowed_inflexible_sensor_data = get_allowed_inflexible_sensor_data(account_id)
linked_sensor_data = {}
if asset:
linked_sensors = (
db.session.query(
GenericAssetInflexibleSensorRelationship.inflexible_sensor_id,
Sensor.name,
)
.join(
Sensor,
GenericAssetInflexibleSensorRelationship.inflexible_sensor_id
== Sensor.id,
)
.filter(
GenericAssetInflexibleSensorRelationship.generic_asset_id
== asset.id
)
.all()
)
linked_sensors = asset.get_inflexible_device_sensors()
linked_sensor_data = {
sensor_id: f"{asset.name}:{sensor_name}"
for sensor_id, sensor_name in linked_sensors
sensor.id: f"{asset.name}:{sensor.name}" for sensor in linked_sensors
}

all_sensor_data = {**allowed_inflexible_sensor_data, **linked_sensor_data}
Expand Down
3 changes: 3 additions & 0 deletions flexmeasures/ui/templates/views/status.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ <h3>Data connectivity for sensors of {{ asset.name }}</h3>
<tr>
<th>Name</th>
<th>Asset name</th>
<th>Relation to asset</th>
<th class="no-sort" title="This is the knowledge time of the most recent event recorded">Time of last value</th>
<th class="text-right no-sort">Status</th>
<th class="d-none">URL</th>
Expand All @@ -39,6 +40,8 @@ <h3>Data connectivity for sensors of {{ asset.name }}</h3>
<td>
{{ sensor.asset_name }}
</td>
<td>
{{ sensor.relation }}
<td>
<span title="{{ sensor['staleness_since'] }}">{{ sensor["staleness_since"] | naturalized_datetime }}</span>
</td>
Expand Down

0 comments on commit 1c6c988

Please sign in to comment.