Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 499 allow showing sensor data from other assets in the same account #500

Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
11 changes: 9 additions & 2 deletions flexmeasures/data/models/generic_assets.py
Expand Up @@ -431,17 +431,24 @@ 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, get_public_sensors

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

Expand Down
21 changes: 15 additions & 6 deletions flexmeasures/data/services/sensors.py
Expand Up @@ -7,23 +7,32 @@


def get_sensors(
Flix6x marked this conversation as resolved.
Show resolved Hide resolved
account_name: str | None = None,
account: Account | int | str | None = None,
sensor_ids: list[int] | None = None,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a good name imho. How about "sensor_id_whitelist" ? "sensor_id_filter"?'

I tend to think we don't need it.
We now have double logic as we already do if sensor.id in sensor_ids in sensors_to_show()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the double logic by removing the now redundant if statement in sensors_to_show().

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think the name is not clear. It's used as a whitelist.

) -> list[Sensor]:
"""Return a list of Sensor objects.

:param account_name: optionally, filter by account name.
:param account: optionally, filter by account by passing an Account, int (account id) or string (account name).
:param sensor_ids: optionally, filter by sensor id.
"""
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}!")
if account is not None:
if isinstance(account, int):
account = Account.query.filter_by(id=account).one_or_none()
if not account:
raise NotFound(f"There is no account with id {account}!")
elif isinstance(account, str):
account = Account.query.filter_by(name=account).one_or_none()
if not account:
raise NotFound(f"There is no account named {account}!")
sensor_query = (
sensor_query.join(GenericAsset)
.filter(Sensor.generic_asset_id == GenericAsset.id)
.filter(GenericAsset.owner == account)
)
if sensor_ids:
sensor_query = sensor_query.filter(Sensor.id.in_(sensor_ids))

return sensor_query.all()

Expand Down