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

Latest state as sensor property #235

Merged
merged 14 commits into from Nov 11, 2021
Merged
Show file tree
Hide file tree
Changes from 11 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
2 changes: 1 addition & 1 deletion flexmeasures/api/v1_3/implementations.py
Expand Up @@ -151,7 +151,7 @@ def get_device_message_response(generic_asset_name_groups, duration):
# event_starts_after=schedule_start,
# event_ends_before=schedule_start + planning_horizon,
# source=scheduler_source,
# most_recent_only=True,
# most_recent_beliefs_only=True,
# )

# Subquery to get the most recent schedule only
Expand Down
17 changes: 16 additions & 1 deletion flexmeasures/data/models/assets.py
@@ -1,4 +1,5 @@
from typing import Dict, List, Tuple, Union
from datetime import datetime, timedelta
from typing import Dict, List, Optional, Tuple, Union

import isodate
import timely_beliefs as tb
Expand Down Expand Up @@ -144,6 +145,20 @@ def __init__(self, **kwargs):
)
market = db.relationship("Market", backref=db.backref("assets", lazy=True))

def latest_state(self, event_ends_before: Optional[datetime] = None) -> "Power":
"""Search the most recent event for this sensor, optionally before some datetime."""
# todo: replace with Sensor.latest_state
power_query = (
Power.query.filter(Power.asset == self)
.filter(Power.horizon <= timedelta(hours=0))
.order_by(Power.datetime.desc())
)
if event_ends_before is not None:
power_query = power_query.filter(
Power.datetime + self.event_resolution <= event_ends_before
)
return power_query.first()

@property
def power_unit(self) -> float:
"""Return the 'unit' property of the generic asset, just with a more insightful name."""
Expand Down
58 changes: 52 additions & 6 deletions flexmeasures/data/models/time_series.py
Expand Up @@ -63,16 +63,30 @@ def __init__(
def entity_address(self) -> str:
return build_entity_address(dict(sensor_id=self.id), "sensor")

def latest_state(
self,
) -> tb.BeliefsDataFrame:
"""Search the most recent event for this sensor, and return the most recent ex-post belief."""
return self.search_beliefs(
horizons_at_most=timedelta(0),
most_recent_beliefs_only=True,
most_recent_events_only=True,
)

def search_beliefs(
self,
event_starts_after: Optional[datetime_type] = None,
event_ends_before: Optional[datetime_type] = None,
beliefs_after: Optional[datetime_type] = None,
beliefs_before: Optional[datetime_type] = None,
horizons_at_least: Optional[timedelta] = None,
horizons_at_most: Optional[timedelta] = None,
source: Optional[
Union[DataSource, List[DataSource], int, List[int], str, List[str]]
] = None,
most_recent_only: bool = False,
most_recent_beliefs_only: bool = False,
most_recent_events_only: bool = False,
most_recent_only: bool = False, # deprecated
as_json: bool = False,
) -> Union[tb.BeliefsDataFrame, str]:
"""Search all beliefs about events for this sensor.
Expand All @@ -81,19 +95,33 @@ def search_beliefs(
:param event_ends_before: only return beliefs about events that end before this datetime (inclusive)
:param beliefs_after: only return beliefs formed after this datetime (inclusive)
:param beliefs_before: only return beliefs formed before this datetime (inclusive)
:param horizons_at_least: only return beliefs with a belief horizon equal or greater than this timedelta (for example, use timedelta(0) to get ante knowledge time beliefs)
:param horizons_at_most: only return beliefs with a belief horizon equal or less than this timedelta (for example, use timedelta(0) to get post knowledge time beliefs)
:param source: search only beliefs by this source (pass the DataSource, or its name or id) or list of sources
:param most_recent_only: only return the most recent beliefs for each event from each source (minimum belief horizon)
:param most_recent_beliefs_only: only return the most recent beliefs for each event from each source (minimum belief horizon)
:param most_recent_events_only: only return (post knowledge time) beliefs for the most recent event (maximum event start)
:param as_json: return beliefs in JSON format (e.g. for use in charts) rather than as BeliefsDataFrame
:returns: BeliefsDataFrame or JSON string (if as_json is True)
"""
# todo: deprecate the 'most_recent_only' argument in favor of 'most_recent_beliefs_only' (announced v0.8.0)
most_recent_beliefs_only = tb_utils.replace_deprecated_argument(
"most_recent_only",
most_recent_only,
"most_recent_beliefs_only",
most_recent_beliefs_only,
required_argument=False,
)
bdf = TimedBelief.search(
sensor=self,
event_starts_after=event_starts_after,
event_ends_before=event_ends_before,
beliefs_after=beliefs_after,
beliefs_before=beliefs_before,
horizons_at_least=horizons_at_least,
horizons_at_most=horizons_at_most,
source=source,
most_recent_only=most_recent_only,
most_recent_beliefs_only=most_recent_beliefs_only,
most_recent_events_only=most_recent_events_only,
Flix6x marked this conversation as resolved.
Show resolved Hide resolved
)
if as_json:
df = bdf.reset_index()
Expand Down Expand Up @@ -219,10 +247,14 @@ def search(
event_ends_before: Optional[datetime_type] = None,
beliefs_after: Optional[datetime_type] = None,
beliefs_before: Optional[datetime_type] = None,
horizons_at_least: Optional[timedelta] = None,
horizons_at_most: Optional[timedelta] = None,
source: Optional[
Union[DataSource, List[DataSource], int, List[int], str, List[str]]
] = None,
most_recent_only: bool = False,
most_recent_beliefs_only: bool = False,
most_recent_events_only: bool = False,
most_recent_only: bool = False, # deprecated
) -> tb.BeliefsDataFrame:
"""Search all beliefs about events for a given sensor.

Expand All @@ -231,9 +263,20 @@ def search(
:param event_ends_before: only return beliefs about events that end before this datetime (inclusive)
:param beliefs_after: only return beliefs formed after this datetime (inclusive)
:param beliefs_before: only return beliefs formed before this datetime (inclusive)
:param horizons_at_least: only return beliefs with a belief horizon equal or greater than this timedelta (for example, use timedelta(0) to get ante knowledge time beliefs)
:param horizons_at_most: only return beliefs with a belief horizon equal or less than this timedelta (for example, use timedelta(0) to get post knowledge time beliefs)
:param source: search only beliefs by this source (pass the DataSource, or its name or id) or list of sources
:param most_recent_only: only return the most recent beliefs for each event from each source (minimum belief horizon)
:param most_recent_beliefs_only: only return the most recent beliefs for each event from each source (minimum belief horizon)
:param most_recent_events_only: only return (post knowledge time) beliefs for the most recent event (maximum event start)
"""
# todo: deprecate the 'most_recent_only' argument in favor of 'most_recent_beliefs_only' (announced v0.8.0)
most_recent_beliefs_only = tb_utils.replace_deprecated_argument(
"most_recent_only",
most_recent_only,
"most_recent_beliefs_only",
most_recent_beliefs_only,
required_argument=False,
)
parsed_sources = parse_source_arg(source)
return cls.search_session(
session=db.session,
Expand All @@ -242,8 +285,11 @@ def search(
event_ends_before=event_ends_before,
beliefs_after=beliefs_after,
beliefs_before=beliefs_before,
horizons_at_least=horizons_at_least,
horizons_at_most=horizons_at_most,
source=parsed_sources,
most_recent_only=most_recent_only,
most_recent_beliefs_only=most_recent_beliefs_only,
most_recent_events_only=most_recent_events_only,
)

@classmethod
Expand Down
2 changes: 1 addition & 1 deletion flexmeasures/data/services/time_series.py
Expand Up @@ -266,7 +266,7 @@ def convert_query_window_for_demo(
else:
end = query_window[-1]

if start >= end:
if start > end:
start, end = (end, start)
return start, end

Expand Down
23 changes: 5 additions & 18 deletions flexmeasures/ui/utils/plotting_utils.py
Expand Up @@ -23,8 +23,9 @@
import numpy as np
import timely_beliefs as tb

from flexmeasures.data.models.assets import Asset, Power
from flexmeasures.data.models.assets import Asset
from flexmeasures.data.models.data_sources import DataSource
from flexmeasures.data.services.time_series import convert_query_window_for_demo
from flexmeasures.utils.flexmeasures_inflection import capitalize
from flexmeasures.utils.time_utils import (
server_now,
Expand Down Expand Up @@ -563,27 +564,13 @@ def get_latest_power_as_plot(asset: Asset, small: bool = False) -> Tuple[str, st
"""Create a plot of an asset's latest power measurement as an embeddable html string (incl. javascript).
First returned string is the measurement time, second string is the html string."""

if current_app.config.get("FLEXMEASURES_MODE", "") == "demo":
demo_year = current_app.config.get("FLEXMEASURES_DEMO_YEAR", None)
if demo_year is None:
before = server_now()
else:
before = server_now().replace(year=demo_year)
elif current_app.config.get("FLEXMEASURES_MODE", "") == "play":
if current_app.config.get("FLEXMEASURES_MODE", "") == "play":
before = None # type:ignore
else:
before = server_now()
_, before = convert_query_window_for_demo((before, before))

power_query = (
Power.query.filter(Power.asset == asset)
.filter(Power.horizon <= timedelta(hours=0))
.order_by(Power.datetime.desc())
)
if before is not None:
power_query = power_query.filter(
Power.datetime + asset.event_resolution <= before
)
latest_power = power_query.first()
latest_power = asset.latest_state(event_ends_before=before)
if latest_power is not None:
latest_power_value = latest_power.value
if current_app.config.get("FLEXMEASURES_MODE", "") == "demo":
Expand Down
2 changes: 1 addition & 1 deletion requirements/app.in
Expand Up @@ -32,7 +32,7 @@ netCDF4
siphon
tables
timetomodel>=0.7.1
timely-beliefs>=1.6.0
timely-beliefs>=1.7.0
python-dotenv
# a backport, not needed in Python3.8
importlib_metadata
Expand Down
2 changes: 1 addition & 1 deletion requirements/app.txt
Expand Up @@ -352,7 +352,7 @@ tables==3.6.1
# via -r requirements/app.in
threadpoolctl==3.0.0
# via scikit-learn
timely-beliefs==1.6.0
timely-beliefs==1.7.0
# via -r requirements/app.in
timetomodel==0.7.1
# via -r requirements/app.in
Expand Down