Skip to content

Commit

Permalink
Issue 499 allow showing sensor data from other assets in the same acc…
Browse files Browse the repository at this point in the history
…ount (#500)

Let the asset page also show sensor data from other assets that belong to the same account.


* Make it possible to show sensor data from other assets in the same account

Signed-off-by: F.N. Claessen <felix@seita.nl>

* Expand docstring

Signed-off-by: F.N. Claessen <felix@seita.nl>

* changelog entry

Signed-off-by: F.N. Claessen <felix@seita.nl>

* Refactor util function to get sensors for a given account (Account, id or name), and avoid duplicate queries of a known account

Signed-off-by: F.N. Claessen <felix@seita.nl>

* Sensors on a given asset are a subset of sensors on assets with the same owner

Signed-off-by: F.N. Claessen <felix@seita.nl>

* Limit query to requested sensor ids (instead of returning all sensors from the same account, which may be a lot)

Signed-off-by: F.N. Claessen <felix@seita.nl>

* flake8

Signed-off-by: F.N. Claessen <felix@seita.nl>

* Remove redundant if statement

Signed-off-by: F.N. Claessen <felix@seita.nl>

* Refactor: rename function

Signed-off-by: F.N. Claessen <felix@seita.nl>

* Repurpose function by making it mandatory to pass at least one account, or None for public sensors (which makes get_public_sensors obsolete)

Signed-off-by: F.N. Claessen <felix@seita.nl>

* Partially revert repurposing function: passing accounts is optional again, and rename filter argument

Signed-off-by: F.N. Claessen <felix@seita.nl>

* Allow to filter sensors by name

Signed-off-by: F.N. Claessen <felix@seita.nl>

* Explicit parameter for including sensors of public assets

Signed-off-by: F.N. Claessen <felix@seita.nl>

* Must pass a list of accounts

Signed-off-by: F.N. Claessen <felix@seita.nl>

* The more common use case is looking up sensors for a single account

Signed-off-by: F.N. Claessen <felix@seita.nl>

* Rename variable

Signed-off-by: F.N. Claessen <felix@seita.nl>

* fix variable order

Signed-off-by: F.N. Claessen <felix@seita.nl>

Signed-off-by: F.N. Claessen <felix@seita.nl>
  • Loading branch information
Flix6x committed Oct 11, 2022
1 parent 75db997 commit f6ad242
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 33 deletions.
1 change: 1 addition & 0 deletions documentation/changelog.rst
Expand Up @@ -9,6 +9,7 @@ New features
-------------

* Hit the replay button to replay what happened, available on the sensor and asset pages [see `PR #463 <http://www.github.com/FlexMeasures/flexmeasures/pull/463>`_]
* The asset page also allows to show sensor data from other assets that belong to the same account [see `PR #500 <http://www.github.com/FlexMeasures/flexmeasures/pull/500>`_]
* Improved import of time series data from CSV file: 1) drop duplicate records with warning, and 2) allow configuring which column contains explicit recording times for each data point (use case: import forecasts) [see `PR #501 <http://www.github.com/FlexMeasures/flexmeasures/pull/501>`_]

Bugfixes
Expand Down
2 changes: 1 addition & 1 deletion flexmeasures/api/v3_0/sensors.py
Expand Up @@ -110,7 +110,7 @@ def index(self, account: Account):
:status 403: INVALID_SENDER
:status 422: UNPROCESSABLE_ENTITY
"""
sensors = get_sensors(account_name=account.name)
sensors = get_sensors(account=account)
return sensors_schema.dump(sensors), 200

@route("/data", methods=["POST"])
Expand Down
15 changes: 12 additions & 3 deletions flexmeasures/data/models/generic_assets.py
Expand Up @@ -431,18 +431,27 @@ def search_beliefs(
def sensors_to_show(self) -> List["Sensor"]: # noqa F821
"""Sensors to show, as defined by the sensors_to_show attribute.
Sensors to show are defined as a list of sensor ids, which
is set by the "sensors_to_show" field of the asset's "attributes" column.
Valid sensors either belong to the asset itself, to other assets in the same account,
or to public assets.
Defaults to two of the asset's sensors.
"""
if not self.has_attribute("sensors_to_show"):
return self.sensors[:2]

from flexmeasures.data.services.sensors import get_public_sensors
from flexmeasures.data.services.sensors import get_sensors

sensor_ids = self.get_attribute("sensors_to_show")
sensor_map = {
sensor.id: sensor
for sensor in self.sensors + get_public_sensors(sensor_ids)
if sensor.id in sensor_ids
for sensor in get_sensors(
account=self.owner,
include_public_assets=True,
sensor_id_allowlist=sensor_ids,
)
}

# Return sensors in the order given by the sensors_to_show attribute
Expand Down
58 changes: 29 additions & 29 deletions flexmeasures/data/services/sensors.py
@@ -1,43 +1,43 @@
from __future__ import annotations

from werkzeug.exceptions import NotFound
import sqlalchemy as sa

from flexmeasures import Sensor, Account
from flexmeasures.data.models.generic_assets import GenericAsset


def get_sensors(
account_name: str | None = None,
account: Account | list[Account],
include_public_assets: bool = False,
sensor_id_allowlist: list[int] | None = None,
sensor_name_allowlist: list[str] | None = None,
) -> list[Sensor]:
"""Return a list of Sensor objects.
"""Return a list of Sensor objects that belong to the given account, and/or public sensors.
:param account_name: optionally, filter by account name.
:param account: select only sensors from this account (or list of accounts)
:param include_public_assets: if True, include sensors that belong to a public asset
:param sensor_id_allowlist: optionally, allow only sensors whose id is in this list
:param sensor_name_allowlist: optionally, allow only sensors whose name is in this list
"""
sensor_query = Sensor.query

if account_name is not None:
account = Account.query.filter(Account.name == account_name).one_or_none()
if not account:
raise NotFound(f"There is no account named {account_name}!")
sensor_query = (
sensor_query.join(GenericAsset)
.filter(Sensor.generic_asset_id == GenericAsset.id)
.filter(GenericAsset.owner == account)
)

return sensor_query.all()


def get_public_sensors(sensor_ids: list[int] | None = None) -> list[Sensor]:
"""Return a list of Sensor objects that belong to a public asset.
:param sensor_ids: optionally, filter by sensor id.
"""
sensor_query = (
Sensor.query.join(GenericAsset)
.filter(Sensor.generic_asset_id == GenericAsset.id)
.filter(GenericAsset.account_id.is_(None))
if isinstance(account, list):
account_ids = [account.id for account in account]
else:
account_ids = [account.id]
sensor_query = sensor_query.join(GenericAsset).filter(
Sensor.generic_asset_id == GenericAsset.id
)
if sensor_ids:
sensor_query = sensor_query.filter(Sensor.id.in_(sensor_ids))
if include_public_assets:
sensor_query = sensor_query.filter(
sa.or_(
GenericAsset.account_id.in_(account_ids),
GenericAsset.account_id.is_(None),
)
)
else:
sensor_query = sensor_query.filter(GenericAsset.account_id.in_(account_ids))
if sensor_id_allowlist:
sensor_query = sensor_query.filter(Sensor.id.in_(sensor_id_allowlist))
if sensor_name_allowlist:
sensor_query = sensor_query.filter(Sensor.name.in_(sensor_name_allowlist))
return sensor_query.all()

0 comments on commit f6ad242

Please sign in to comment.