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

Responsive sensor chart #313

Merged
merged 6 commits into from Jan 12, 2022
Merged
Show file tree
Hide file tree
Changes from all 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 @@ -11,6 +11,7 @@ v0.8.0 | November XX, 2021
New features
-----------
* Charts with sensor data can be requested in one of the supported [`vega-lite themes <https://github.com/vega/vega-themes#included-themes>`_] (incl. a dark theme) [see `PR #221 <http://www.github.com/FlexMeasures/flexmeasures/pull/221>`_]
* Mobile friendly (responsive) charts of sensor data, and such charts can be requested with a custom width and height [see `PR #313 <http://www.github.com/FlexMeasures/flexmeasures/pull/313>`_]
* Schedulers take into account round-trip efficiency if set [see `PR #291 <http://www.github.com/FlexMeasures/flexmeasures/pull/291>`_]

Bugfixes
Expand Down
2 changes: 2 additions & 0 deletions flexmeasures/api/dev/sensors.py
Expand Up @@ -29,6 +29,8 @@ class SensorAPI(FlaskView):
"beliefs_before": AwareDateTimeField(format="iso", required=False),
"include_data": fields.Boolean(required=False),
"dataset_name": fields.Str(required=False),
"height": fields.Str(required=False),
"width": fields.Str(required=False),
},
location="query",
)
Expand Down
25 changes: 19 additions & 6 deletions flexmeasures/data/models/charts/belief_charts.py
@@ -1,21 +1,25 @@
from flexmeasures.data.models.charts.defaults import FIELD_DEFINITIONS
from flexmeasures.utils.flexmeasures_inflection import capitalize


def bar_chart(title: str, quantity: str = "unknown quantity", unit: str = "a.u."):
if not unit:
unit = "a.u."
def bar_chart(
sensor: "Sensor", # noqa F821
**override_chart_specs: dict,
):
unit = sensor.unit if sensor.unit else "a.u."
event_value_field_definition = dict(
title=f"{quantity} ({unit})",
title=f"{capitalize(sensor.sensor_type)} ({unit})",
format=".3s",
stack=None,
**FIELD_DEFINITIONS["event_value"],
)
return {
chart_specs = {
"description": "A simple bar chart.",
"title": title,
"title": capitalize(sensor.name),
"mark": "bar",
"encoding": {
"x": FIELD_DEFINITIONS["event_start"],
"x2": FIELD_DEFINITIONS["event_end"],
"y": event_value_field_definition,
"color": FIELD_DEFINITIONS["source"],
"opacity": {"value": 0.7},
Expand All @@ -25,4 +29,13 @@ def bar_chart(title: str, quantity: str = "unknown quantity", unit: str = "a.u."
FIELD_DEFINITIONS["source"],
],
},
"transform": [
{
"calculate": f"datum.event_start + {sensor.event_resolution.total_seconds() * 1000}",
"as": "event_end",
},
],
}
for k, v in override_chart_specs.items():
chart_specs[k] = v
return chart_specs
21 changes: 17 additions & 4 deletions flexmeasures/data/models/charts/defaults.py
Expand Up @@ -16,6 +16,11 @@
type="temporal",
title=None,
),
"event_end": dict(
field="event_end",
type="temporal",
title=None,
),
"event_value": dict(
field="event_value",
type="quantitative",
Expand Down Expand Up @@ -48,14 +53,22 @@ def decorated_chart_specs(*args, **kwargs):
chart_specs.pop("$schema")
if dataset_name:
chart_specs["data"] = {"name": dataset_name}
chart_specs["height"] = HEIGHT
chart_specs["width"] = WIDTH
chart_specs["transform"] = [

# Fall back to default height and width, if needed
if "height" not in chart_specs:
chart_specs["height"] = HEIGHT
if "width" not in chart_specs:
chart_specs["width"] = WIDTH

# Add transform function to calculate full date
if "transform" not in chart_specs:
chart_specs["transform"] = []
chart_specs["transform"].append(
{
"as": "full_date",
"calculate": f"timeFormat(datum.event_start, '{TIME_FORMAT}')",
}
]
)
return chart_specs

return decorated_chart_specs
5 changes: 1 addition & 4 deletions flexmeasures/data/models/time_series.py
Expand Up @@ -28,7 +28,6 @@
from flexmeasures.data.models.generic_assets import GenericAsset
from flexmeasures.data.models.validation_utils import check_required_attributes
from flexmeasures.utils.time_utils import server_now
from flexmeasures.utils.flexmeasures_inflection import capitalize


class Sensor(db.Model, tb.SensorDBMixin, AuthModelMixin):
Expand Down Expand Up @@ -270,9 +269,7 @@ def chart(
) # todo remove this placeholder when sensor types are modelled
chart_specs = chart_type_to_chart_specs(
chart_type,
title=capitalize(self.name),
quantity=capitalize(self.sensor_type),
unit=self.unit,
sensor=self,
dataset_name=dataset_name,
**kwargs,
)
Expand Down
9 changes: 7 additions & 2 deletions flexmeasures/ui/templates/views/sensors.html
Expand Up @@ -9,7 +9,12 @@
<div class="charts text-center">
<div class="row"><div class="alert alert-info" id="tzwarn" style="display:none;"></div></div>
<div class="row"><div id="datepicker"></div></div>
<div class="row"><div id="sensorchart"></div></div><hr>
<div class="row">
<div class="col-sm-12">
<div id="sensorchart" style="width: 100%;"></div>
</div>
</div>
<hr>
</div>

<script src="https://d3js.org/d3.v6.min.js"></script>
Expand All @@ -31,7 +36,7 @@

async function embedAndLoad(chartSpecsPath, elementId, datasetName) {

await vegaEmbed('#'+elementId, chartSpecsPath + '?dataset_name=' + datasetName, {{ chart_options | safe }})
await vegaEmbed('#'+elementId, chartSpecsPath + '?dataset_name=' + datasetName + '&width=container', {{ chart_options | safe }})
.then(function (result) {
// result.view is the Vega View, chartSpecsPath is the original Vega-Lite specification
vegaView = result.view;
Expand Down