From e2e52bd55eec65190aec12bb2262ee0e48ad5d14 Mon Sep 17 00:00:00 2001 From: cortadocodes Date: Mon, 12 Jun 2023 12:53:56 +0100 Subject: [PATCH 01/17] FEA: Add ability to see and select measurement sessions --- dashboard/callbacks.py | 67 +++++++++++++++++++++-- dashboard/components/time_range_select.py | 2 +- dashboard/layouts.py | 5 +- pyproject.toml | 4 +- 4 files changed, 70 insertions(+), 8 deletions(-) diff --git a/dashboard/callbacks.py b/dashboard/callbacks.py index fc00ea8..e52fb90 100644 --- a/dashboard/callbacks.py +++ b/dashboard/callbacks.py @@ -4,6 +4,7 @@ import plotly.express as px from dash import Input, Output, State +from dash.exceptions import PreventUpdate from aerosense_tools.plots import plot_connection_statistic, plot_cp_curve, plot_sensors from aerosense_tools.preprocess import RawSignal, SensorMeasurementSession @@ -384,19 +385,77 @@ def update_graph_title(selected_y_axis): Output("end-hour", "disabled"), Output("end-minute", "disabled"), Output("end-second", "disabled"), + Output("measurement-session-select", "disabled"), ], [ Input("time-range-select", "value"), ], ) - def enable_custom_time_range_select(time_range): - """Enable the custom time range selection if "Custom" is chosen in the time range selector. + def enable_measurement_session_time_range_select(time_range): + """Enable the measurement session time range selection if "By measurement session" is chosen in the time range + selector. :param str time_range: :return bool: """ - disabled = time_range != "Custom" - return (disabled, None, disabled, disabled, disabled, disabled, None, disabled, disabled, disabled) + disabled = time_range != "By measurement session" + return (disabled, None, disabled, disabled, disabled, disabled, None, disabled, disabled, disabled, disabled) + + @app.callback( + Output("measurement-session-select", "options"), + Input("measurement-session-select", "disabled"), + Input("installation-select", "value"), + Input("node-select", "value"), + Input("y-axis-select", "value"), + Input("start-date", "date"), + Input("start-hour", "value"), + Input("start-minute", "value"), + Input("start-second", "value"), + Input("end-date", "date"), + Input("end-hour", "value"), + Input("end-minute", "value"), + Input("end-second", "value"), + ) + def update_measurement_session_selector( + measurement_session_selection_disabled, + installation_reference, + node_id, + y_axis, + start_date, + start_hour, + start_minute, + start_second, + end_date, + end_hour, + end_minute, + end_second, + ): + if measurement_session_selection_disabled: + raise PreventUpdate + + start_datetime, finish_datetime = _combine_dates_and_times( + start_date, + start_hour, + start_minute, + start_second, + end_date, + end_hour, + end_minute, + end_second, + ) + + measurement_sessions = BigQuery().get_measurement_sessions( + installation_reference=installation_reference, + node_id=node_id, + sensor_type_reference=y_axis, + start=start_datetime, + finish=finish_datetime, + ) + + return [ + f"{session[1][0]} to {session[1][1]}" + for session in measurement_sessions[["start_time", "end_time"]].iterrows() + ] @app.callback( Output("app", "children"), diff --git a/dashboard/components/time_range_select.py b/dashboard/components/time_range_select.py index 6662373..25fd013 100644 --- a/dashboard/components/time_range_select.py +++ b/dashboard/components/time_range_select.py @@ -5,7 +5,7 @@ def TimeRangeSelect(): return dcc.Dropdown( - options=list(TIME_RANGE_OPTIONS.keys()) + ["All time", "Custom"], + options=list(TIME_RANGE_OPTIONS.keys()) + ["All time", "By measurement session"], id="time-range-select", value="Last day", persistence=True, diff --git a/dashboard/layouts.py b/dashboard/layouts.py index a0323a5..1afdd1e 100644 --- a/dashboard/layouts.py +++ b/dashboard/layouts.py @@ -40,7 +40,7 @@ def create_sensors_tab_layout(app, tab_name, sensor_names, graph_id, data_limit_ html.Label(html.B("Time range")), TimeRangeSelect(), html.Br(), - html.Label(html.B("Custom time range")), + html.Label(html.B("Measurement sessions")), html.Div( [ html.Div( @@ -129,6 +129,9 @@ def create_sensors_tab_layout(app, tab_name, sensor_names, graph_id, data_limit_ style={"margin": "10px 0"}, ), html.Br(), + html.Label("Measurement session"), + dcc.Dropdown(id="measurement-session-select", persistence=True), + html.Br(), html.Button("Plot", id="refresh-button", n_clicks=0), html.Button("Check for new installations", id="installation-check-button", n_clicks=0), ], diff --git a/pyproject.toml b/pyproject.toml index d548c4d..92ff051 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ [tool.poetry] name = "aerosense-dashboard" -version = "0.5.0" +version = "0.6.0" description = "High-level visualisation for aerosense" authors = ["Tom Clark", "Marcus Lugg", "Yuriy Marykovsky"] license = "BSD-3" @@ -38,7 +38,7 @@ gunicorn = "^20.1.0" dash = "^2.4.1" Flask-Caching = "^2.0.0" dash-daq = "^0.5.0" -aerosense-tools = {git = "https://github.com/aerosense-ai/aerosense-tools.git", rev = "0.9.2"} +aerosense-tools = {git = "https://github.com/aerosense-ai/aerosense-tools.git", rev = "0.10.0"} [tool.poetry.dev-dependencies] coverage = "^6.2" From b129872db7c11dea2763d2cf9f0fea68deeeb1bf Mon Sep 17 00:00:00 2001 From: cortadocodes Date: Mon, 12 Jun 2023 15:21:02 +0100 Subject: [PATCH 02/17] ENH: Update sensor plot to use measurement session datetimes --- dashboard/callbacks.py | 45 ++++------------------- dashboard/components/time_range_select.py | 2 +- dashboard/utils.py | 43 ++++++++++++++++++++++ 3 files changed, 52 insertions(+), 38 deletions(-) create mode 100644 dashboard/utils.py diff --git a/dashboard/callbacks.py b/dashboard/callbacks.py index e52fb90..44ac162 100644 --- a/dashboard/callbacks.py +++ b/dashboard/callbacks.py @@ -9,7 +9,7 @@ from aerosense_tools.plots import plot_connection_statistic, plot_cp_curve, plot_sensors from aerosense_tools.preprocess import RawSignal, SensorMeasurementSession from aerosense_tools.queries import ROW_LIMIT, BigQuery -from aerosense_tools.utils import generate_time_range +from dashboard.utils import generate_time_range logger = logging.getLogger(__name__) @@ -127,14 +127,7 @@ def plot_information_sensors_graph( State("node-select", "value"), State("y-axis-select", "value"), State("time-range-select", "value"), - State("start-date", "date"), - State("start-hour", "value"), - State("start-minute", "value"), - State("start-second", "value"), - State("end-date", "date"), - State("end-hour", "value"), - State("end-minute", "value"), - State("end-second", "value"), + State("measurement-session-select", "value"), Input("refresh-button", "n_clicks"), ) @cache.memoize(timeout=cache_timeout, args_to_ignore=["refresh"]) @@ -143,14 +136,7 @@ def plot_sensors_graph( node_id, sensor_name, time_range, - custom_start_date, - custom_start_hour, - custom_start_minute, - custom_start_second, - custom_end_date, - custom_end_hour, - custom_end_minute, - custom_end_second, + measurement_session, refresh, ): """Plot a graph of the sensor data for the given installation, y-axis column, and time range when these values are @@ -160,32 +146,17 @@ def plot_sensors_graph( :param str node_id: :param str sensor_name: :param str time_range: - :param str|None custom_start_date: - :param int|None custom_start_hour: - :param int|None custom_start_minute: - :param int|None custom_start_second: - :param str|None custom_end_date: - :param int|None custom_end_hour: - :param int|None custom_end_minute: - :param int|None custom_end_second: + :param str measurement_session: :param int refresh: :return (plotly.graph_objs.Figure, str): """ if not node_id: node_id = None - custom_start, custom_end = _combine_dates_and_times( - custom_start_date, - custom_start_hour, - custom_start_minute, - custom_start_second, - custom_end_date, - custom_end_hour, - custom_end_minute, - custom_end_second, - ) + start, finish = generate_time_range(time_range, measurement_session) - start, finish = generate_time_range(time_range, custom_start, custom_end) + if start is None: + return (px.scatter(), "No measurement session selected.") df, data_limit_applied = BigQuery().get_sensor_data( installation_reference, @@ -196,7 +167,7 @@ def plot_sensors_graph( ) if df.empty: - return (px.scatter(), "No data to plot") + return (px.scatter(), "No data to plot.") # Extract only data columns and set index to 'datetime', so that DataFrame is accepted by RawSignal class data_columns = df.columns[df.columns.str.startswith("f")].tolist() diff --git a/dashboard/components/time_range_select.py b/dashboard/components/time_range_select.py index 25fd013..2420e1b 100644 --- a/dashboard/components/time_range_select.py +++ b/dashboard/components/time_range_select.py @@ -1,6 +1,6 @@ from dash import dcc -from aerosense_tools.utils import TIME_RANGE_OPTIONS +from dashboard.utils import TIME_RANGE_OPTIONS def TimeRangeSelect(): diff --git a/dashboard/utils.py b/dashboard/utils.py new file mode 100644 index 0000000..d7f172a --- /dev/null +++ b/dashboard/utils.py @@ -0,0 +1,43 @@ +import datetime + + +TIME_RANGE_OPTIONS = { + "Last minute": datetime.timedelta(minutes=1), + "Last hour": datetime.timedelta(hours=1), + "Last day": datetime.timedelta(days=1), + "Last week": datetime.timedelta(weeks=1), + "Last month": datetime.timedelta(days=31), + "Last year": datetime.timedelta(days=365), +} + + +def generate_time_range(time_range, measurement_session=None): + """Generate a convenient time range to plot. The options are: + - Last minute + - Last hour + - Last day + - Last week + - Last month + - Last year + - All time + - Custom + + :param str time_range: + :param str|None measurement_session: + :return (datetime.datetime, datetime.datetime, bool): the start and finish datetimes + """ + if time_range == "All time": + return datetime.datetime.min, datetime.datetime.utcnow() + + if time_range == "By measurement session": + try: + start, finish = measurement_session.split(" to ") + start = datetime.datetime.fromisoformat(start) + finish = datetime.datetime.fromisoformat(finish) + return start, finish + except ValueError: + return None, None + + finish = datetime.datetime.utcnow() + start = finish - TIME_RANGE_OPTIONS[time_range] + return start, finish From e36b11e7055a6ac15471c2f99e3db27d24128bce Mon Sep 17 00:00:00 2001 From: cortadocodes Date: Mon, 12 Jun 2023 15:50:46 +0100 Subject: [PATCH 03/17] ENH: Remove unnecessary whitespace from dashboard --- dashboard/layouts.py | 1 - 1 file changed, 1 deletion(-) diff --git a/dashboard/layouts.py b/dashboard/layouts.py index 1afdd1e..1be0b67 100644 --- a/dashboard/layouts.py +++ b/dashboard/layouts.py @@ -128,7 +128,6 @@ def create_sensors_tab_layout(app, tab_name, sensor_names, graph_id, data_limit_ ], style={"margin": "10px 0"}, ), - html.Br(), html.Label("Measurement session"), dcc.Dropdown(id="measurement-session-select", persistence=True), html.Br(), From 9c2dc775a410b18f6dbb6740a34c2dc1e716cc81 Mon Sep 17 00:00:00 2001 From: cortadocodes Date: Mon, 12 Jun 2023 15:51:58 +0100 Subject: [PATCH 04/17] ENH: Update information sensor plot to use measurement sessions --- dashboard/callbacks.py | 46 +++++++++--------------------------------- 1 file changed, 10 insertions(+), 36 deletions(-) diff --git a/dashboard/callbacks.py b/dashboard/callbacks.py index 44ac162..2d735c1 100644 --- a/dashboard/callbacks.py +++ b/dashboard/callbacks.py @@ -33,14 +33,7 @@ def register_callbacks(app, cache, cache_timeout, tabs, sensor_types): State("node-select", "value"), State("y-axis-select", "value"), State("time-range-select", "value"), - State("start-date", "date"), - State("start-hour", "value"), - State("start-minute", "value"), - State("start-second", "value"), - State("end-date", "date"), - State("end-hour", "value"), - State("end-minute", "value"), - State("end-second", "value"), + State("measurement-session-select", "value"), Input("refresh-button", "n_clicks"), ) @cache.memoize(timeout=cache_timeout, args_to_ignore=["refresh"]) @@ -49,14 +42,7 @@ def plot_information_sensors_graph( node_id, y_axis_column, time_range, - custom_start_date, - custom_start_hour, - custom_start_minute, - custom_start_second, - custom_end_date, - custom_end_hour, - custom_end_minute, - custom_end_second, + measurement_session, refresh, ): """Plot a graph of the information sensors for the given installation, y-axis column, and time range when these @@ -66,32 +52,14 @@ def plot_information_sensors_graph( :param str node_id: :param str y_axis_column: :param str time_range: - :param str|None custom_start_date: - :param int|None custom_start_hour: - :param int|None custom_start_minute: - :param int|None custom_start_second: - :param str|None custom_end_date: - :param int|None custom_end_hour: - :param int|None custom_end_minute: - :param int|None custom_end_second: + :param str measurement_session: :param int refresh: :return (plotly.graph_objs.Figure, str): """ if not node_id: node_id = None - custom_start, custom_end = _combine_dates_and_times( - custom_start_date, - custom_start_hour, - custom_start_minute, - custom_start_second, - custom_end_date, - custom_end_hour, - custom_end_minute, - custom_end_second, - ) - - start, finish = generate_time_range(time_range, custom_start, custom_end) + start, finish = generate_time_range(time_range, measurement_session) if y_axis_column == "battery_info": df, data_limit_applied = BigQuery().get_sensor_data( @@ -102,6 +70,9 @@ def plot_information_sensors_graph( finish=finish, ) + if df.empty: + return (px.scatter(), "No data to plot.") + figure = plot_sensors(df, line_descriptions=sensor_types[y_axis_column]["variable"]) else: @@ -112,6 +83,9 @@ def plot_information_sensors_graph( finish=finish, ) + if df.empty: + return (px.scatter(), "No data to plot.") + data_limit_applied = "" figure = plot_connection_statistic(df, y_axis_column) From c6a965531fe484ce0eed14307e589906c94d5484 Mon Sep 17 00:00:00 2001 From: cortadocodes Date: Mon, 12 Jun 2023 16:24:10 +0100 Subject: [PATCH 05/17] ENH: Refresh measurement sessions on button click --- dashboard/callbacks.py | 45 ++++++++++++++++++++++++++++++------------ dashboard/layouts.py | 3 +++ 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/dashboard/callbacks.py b/dashboard/callbacks.py index 2d735c1..e338c7f 100644 --- a/dashboard/callbacks.py +++ b/dashboard/callbacks.py @@ -61,6 +61,9 @@ def plot_information_sensors_graph( start, finish = generate_time_range(time_range, measurement_session) + if start is None: + return (px.scatter(), "No measurement session selected.") + if y_axis_column == "battery_info": df, data_limit_applied = BigQuery().get_sensor_data( installation_reference, @@ -331,6 +334,7 @@ def update_graph_title(selected_y_axis): Output("end-minute", "disabled"), Output("end-second", "disabled"), Output("measurement-session-select", "disabled"), + Output("measurement-session-check-button", "disabled"), ], [ Input("time-range-select", "value"), @@ -344,22 +348,36 @@ def enable_measurement_session_time_range_select(time_range): :return bool: """ disabled = time_range != "By measurement session" - return (disabled, None, disabled, disabled, disabled, disabled, None, disabled, disabled, disabled, disabled) + return ( + disabled, + None, + disabled, + disabled, + disabled, + disabled, + None, + disabled, + disabled, + disabled, + disabled, + disabled, + ) @app.callback( Output("measurement-session-select", "options"), - Input("measurement-session-select", "disabled"), - Input("installation-select", "value"), - Input("node-select", "value"), - Input("y-axis-select", "value"), - Input("start-date", "date"), - Input("start-hour", "value"), - Input("start-minute", "value"), - Input("start-second", "value"), - Input("end-date", "date"), - Input("end-hour", "value"), - Input("end-minute", "value"), - Input("end-second", "value"), + State("measurement-session-select", "disabled"), + State("installation-select", "value"), + State("node-select", "value"), + State("y-axis-select", "value"), + State("start-date", "date"), + State("start-hour", "value"), + State("start-minute", "value"), + State("start-second", "value"), + State("end-date", "date"), + State("end-hour", "value"), + State("end-minute", "value"), + State("end-second", "value"), + Input("measurement-session-check-button", "n_clicks"), ) def update_measurement_session_selector( measurement_session_selection_disabled, @@ -374,6 +392,7 @@ def update_measurement_session_selector( end_hour, end_minute, end_second, + refresh, ): if measurement_session_selection_disabled: raise PreventUpdate diff --git a/dashboard/layouts.py b/dashboard/layouts.py index 1be0b67..d51530e 100644 --- a/dashboard/layouts.py +++ b/dashboard/layouts.py @@ -128,6 +128,9 @@ def create_sensors_tab_layout(app, tab_name, sensor_names, graph_id, data_limit_ ], style={"margin": "10px 0"}, ), + html.Button("Get measurement sessions", id="measurement-session-check-button", n_clicks=0), + html.Br(), + html.Br(), html.Label("Measurement session"), dcc.Dropdown(id="measurement-session-select", persistence=True), html.Br(), From cd2e9f22a33e6d506889382b284dacd9dd6175d7 Mon Sep 17 00:00:00 2001 From: cortadocodes Date: Mon, 12 Jun 2023 16:24:26 +0100 Subject: [PATCH 06/17] FIX: Avoid splitting errors when datetime is `None` --- dashboard/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dashboard/utils.py b/dashboard/utils.py index d7f172a..f502288 100644 --- a/dashboard/utils.py +++ b/dashboard/utils.py @@ -35,7 +35,7 @@ def generate_time_range(time_range, measurement_session=None): start = datetime.datetime.fromisoformat(start) finish = datetime.datetime.fromisoformat(finish) return start, finish - except ValueError: + except (ValueError, AttributeError): return None, None finish = datetime.datetime.utcnow() From 42355b5a8014a92e896786fcd569d08afea074a4 Mon Sep 17 00:00:00 2001 From: cortadocodes Date: Mon, 12 Jun 2023 16:26:46 +0100 Subject: [PATCH 07/17] FIX: Show first measurement session on refresh --- dashboard/callbacks.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/dashboard/callbacks.py b/dashboard/callbacks.py index e338c7f..7cbcea6 100644 --- a/dashboard/callbacks.py +++ b/dashboard/callbacks.py @@ -365,6 +365,7 @@ def enable_measurement_session_time_range_select(time_range): @app.callback( Output("measurement-session-select", "options"), + Output("measurement-session-select", "value"), State("measurement-session-select", "disabled"), State("installation-select", "value"), State("node-select", "value"), @@ -416,11 +417,16 @@ def update_measurement_session_selector( finish=finish_datetime, ) - return [ + measurement_sessions = [ f"{session[1][0]} to {session[1][1]}" for session in measurement_sessions[["start_time", "end_time"]].iterrows() ] + if not measurement_sessions: + return [], None + + return measurement_sessions, measurement_sessions[0] + @app.callback( Output("app", "children"), Input("nav-tabs", "value"), From 5b4e6e684c67e3d73c855d38283f192ec4d2f57a Mon Sep 17 00:00:00 2001 From: cortadocodes Date: Mon, 12 Jun 2023 16:34:05 +0100 Subject: [PATCH 08/17] ENH: Add loading indicator to measurement sessions selector --- dashboard/layouts.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dashboard/layouts.py b/dashboard/layouts.py index d51530e..47626e0 100644 --- a/dashboard/layouts.py +++ b/dashboard/layouts.py @@ -132,7 +132,11 @@ def create_sensors_tab_layout(app, tab_name, sensor_names, graph_id, data_limit_ html.Br(), html.Br(), html.Label("Measurement session"), - dcc.Dropdown(id="measurement-session-select", persistence=True), + dcc.Loading( + [ + dcc.Dropdown(id="measurement-session-select", persistence=True), + ] + ), html.Br(), html.Button("Plot", id="refresh-button", n_clicks=0), html.Button("Check for new installations", id="installation-check-button", n_clicks=0), From 18213c97e9fb4d571c1e98dc8f3efb1f447ff97c Mon Sep 17 00:00:00 2001 From: cortadocodes Date: Mon, 12 Jun 2023 16:38:13 +0100 Subject: [PATCH 09/17] ENH: Add loading indicator to other dynamic selectors --- dashboard/layouts.py | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/dashboard/layouts.py b/dashboard/layouts.py index 47626e0..7ad3029 100644 --- a/dashboard/layouts.py +++ b/dashboard/layouts.py @@ -30,9 +30,17 @@ def create_sensors_tab_layout(app, tab_name, sensor_names, graph_id, data_limit_ [ html.Label(html.B("Installation")), html.Label("Installation reference"), - InstallationSelect(), + dcc.Loading( + [ + InstallationSelect(), + ], + ), html.Label("Node ID"), - NodeSelect(), + dcc.Loading( + [ + NodeSelect(), + ], + ), html.Br(), html.Label(html.B("Sensor")), SensorSelect(sensor_names), @@ -183,11 +191,23 @@ def create_cp_plot_tab_layout(app): [ html.Label(html.B("Installation")), html.Label("Installation reference"), - InstallationSelect(), + dcc.Loading( + [ + InstallationSelect(), + ], + ), html.Label("Node ID"), - NodeSelect(), + dcc.Loading( + [ + NodeSelect(), + ], + ), html.Label("Sensor coordinates reference"), - SensorCoordinatesSelect(), + dcc.Loading( + [ + SensorCoordinatesSelect(), + ] + ), html.Div( [ html.Div( From d6b65dc05ba9216df5f1d41886ecc476cd2a182b Mon Sep 17 00:00:00 2001 From: cortadocodes Date: Mon, 12 Jun 2023 16:52:32 +0100 Subject: [PATCH 10/17] REF: Rename measurement session time range option --- dashboard/callbacks.py | 4 ++-- dashboard/components/time_range_select.py | 2 +- dashboard/utils.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dashboard/callbacks.py b/dashboard/callbacks.py index 7cbcea6..7713126 100644 --- a/dashboard/callbacks.py +++ b/dashboard/callbacks.py @@ -341,13 +341,13 @@ def update_graph_title(selected_y_axis): ], ) def enable_measurement_session_time_range_select(time_range): - """Enable the measurement session time range selection if "By measurement session" is chosen in the time range + """Enable the measurement session time range selection if "Measurement session" is chosen in the time range selector. :param str time_range: :return bool: """ - disabled = time_range != "By measurement session" + disabled = time_range != "Measurement session" return ( disabled, None, diff --git a/dashboard/components/time_range_select.py b/dashboard/components/time_range_select.py index 2420e1b..4d8ef12 100644 --- a/dashboard/components/time_range_select.py +++ b/dashboard/components/time_range_select.py @@ -5,7 +5,7 @@ def TimeRangeSelect(): return dcc.Dropdown( - options=list(TIME_RANGE_OPTIONS.keys()) + ["All time", "By measurement session"], + options=list(TIME_RANGE_OPTIONS.keys()) + ["All time", "Measurement session"], id="time-range-select", value="Last day", persistence=True, diff --git a/dashboard/utils.py b/dashboard/utils.py index f502288..9b34d87 100644 --- a/dashboard/utils.py +++ b/dashboard/utils.py @@ -29,7 +29,7 @@ def generate_time_range(time_range, measurement_session=None): if time_range == "All time": return datetime.datetime.min, datetime.datetime.utcnow() - if time_range == "By measurement session": + if time_range == "Measurement session": try: start, finish = measurement_session.split(" to ") start = datetime.datetime.fromisoformat(start) From 7ce7e1012a7effd280ce57b0eeb5a572e82e3dbb Mon Sep 17 00:00:00 2001 From: cortadocodes Date: Mon, 12 Jun 2023 17:17:57 +0100 Subject: [PATCH 11/17] ENH: Update info sensors plot to work with all info sensors --- dashboard/callbacks.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dashboard/callbacks.py b/dashboard/callbacks.py index 7713126..e1d4af7 100644 --- a/dashboard/callbacks.py +++ b/dashboard/callbacks.py @@ -409,6 +409,10 @@ def update_measurement_session_selector( end_second, ) + # The connection statistics always come together, so they have the same measurement sessions. + if y_axis in {"filtered_rssi", "filtered_rssi", "tx_power", "allocated_heap_memory"}: + y_axis = "connection_statistics" + measurement_sessions = BigQuery().get_measurement_sessions( installation_reference=installation_reference, node_id=node_id, From 6f6b024274605cd330c3d26cfa8ca7d487b771dd Mon Sep 17 00:00:00 2001 From: cortadocodes Date: Mon, 12 Jun 2023 17:25:26 +0100 Subject: [PATCH 12/17] FIX: Only update graph title once plot button has been clicked --- dashboard/callbacks.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dashboard/callbacks.py b/dashboard/callbacks.py index e1d4af7..af68ceb 100644 --- a/dashboard/callbacks.py +++ b/dashboard/callbacks.py @@ -308,9 +308,10 @@ def update_sensor_coordinates_selector(refresh): @app.callback( Output("graph-title", "children"), - Input("y-axis-select", "value"), + State("y-axis-select", "value"), + Input("refresh-button", "n_clicks"), ) - def update_graph_title(selected_y_axis): + def update_graph_title(selected_y_axis, refresh): """Update the graph title with the name of the currently selected y-axis. :param str selected_y_axis: From a886537d353d2d60927a74ab65be6026082a68aa Mon Sep 17 00:00:00 2001 From: cortadocodes Date: Tue, 13 Jun 2023 12:32:20 +0100 Subject: [PATCH 13/17] DEP: Update lock file --- poetry.lock | 142 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 87 insertions(+), 55 deletions(-) diff --git a/poetry.lock b/poetry.lock index ea86e66..931768c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2,7 +2,7 @@ [[package]] name = "aerosense-tools" -version = "0.9.2" +version = "0.10.0" description = "Functions for working with aerosense data, useful in building dashboards, analysis notebooks and digital twin services" category = "main" optional = false @@ -21,8 +21,8 @@ docs = [] [package.source] type = "git" url = "https://github.com/aerosense-ai/aerosense-tools.git" -reference = "0.9.2" -resolved_reference = "21ff319324777c8380b5a04e4184dc3e2a85b7bb" +reference = "0.10.0" +resolved_reference = "d354668e330a7e38cb0c9cec7978a8dfa4f0015e" [[package]] name = "alabaster" @@ -536,19 +536,19 @@ files = [ [[package]] name = "filelock" -version = "3.12.0" +version = "3.12.2" description = "A platform independent file lock." category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "filelock-3.12.0-py3-none-any.whl", hash = "sha256:ad98852315c2ab702aeb628412cbf7e95b7ce8c3bf9565670b4eaecf1db370a9"}, - {file = "filelock-3.12.0.tar.gz", hash = "sha256:fc03ae43288c013d2ea83c8597001b1129db351aad9c57fe2409327916b8e718"}, + {file = "filelock-3.12.2-py3-none-any.whl", hash = "sha256:cbb791cdea2a72f23da6ac5b5269ab0a0d161e9ef0100e653b69049a7706d1ec"}, + {file = "filelock-3.12.2.tar.gz", hash = "sha256:002740518d8aa59a26b0c76e10fb8c6e15eae825d34b6fdf670333fd7b938d81"}, ] [package.extras] -docs = ["furo (>=2023.3.27)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] +docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] [[package]] name = "flask" @@ -592,14 +592,46 @@ Flask = "<3" [[package]] name = "fonttools" -version = "4.39.4" +version = "4.40.0" description = "Tools to manipulate font files" category = "main" optional = false python-versions = ">=3.8" files = [ - {file = "fonttools-4.39.4-py3-none-any.whl", hash = "sha256:106caf6167c4597556b31a8d9175a3fdc0356fdcd70ab19973c3b0d4c893c461"}, - {file = "fonttools-4.39.4.zip", hash = "sha256:dba8d7cdb8e2bac1b3da28c5ed5960de09e59a2fe7e63bb73f5a59e57b0430d2"}, + {file = "fonttools-4.40.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b802dcbf9bcff74672f292b2466f6589ab8736ce4dcf36f48eb994c2847c4b30"}, + {file = "fonttools-4.40.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7f6e3fa3da923063c286320e728ba2270e49c73386e3a711aa680f4b0747d692"}, + {file = "fonttools-4.40.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fdf60f8a5c6bcce7d024a33f7e4bc7921f5b74e8ea13bccd204f2c8b86f3470"}, + {file = "fonttools-4.40.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91784e21a1a085fac07c6a407564f4a77feb471b5954c9ee55a4f9165151f6c1"}, + {file = "fonttools-4.40.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:05171f3c546f64d78569f10adc0de72561882352cac39ec7439af12304d8d8c0"}, + {file = "fonttools-4.40.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7449e5e306f3a930a8944c85d0cbc8429cba13503372a1a40f23124d6fb09b58"}, + {file = "fonttools-4.40.0-cp310-cp310-win32.whl", hash = "sha256:bae8c13abbc2511e9a855d2142c0ab01178dd66b1a665798f357da0d06253e0d"}, + {file = "fonttools-4.40.0-cp310-cp310-win_amd64.whl", hash = "sha256:425b74a608427499b0e45e433c34ddc350820b6f25b7c8761963a08145157a66"}, + {file = "fonttools-4.40.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:00ab569b2a3e591e00425023ade87e8fef90380c1dde61be7691cb524ca5f743"}, + {file = "fonttools-4.40.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:18ea64ac43e94c9e0c23d7a9475f1026be0e25b10dda8f236fc956188761df97"}, + {file = "fonttools-4.40.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:022c4a16b412293e7f1ce21b8bab7a6f9d12c4ffdf171fdc67122baddb973069"}, + {file = "fonttools-4.40.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:530c5d35109f3e0cea2535742d6a3bc99c0786cf0cbd7bb2dc9212387f0d908c"}, + {file = "fonttools-4.40.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5e00334c66f4e83535384cb5339526d01d02d77f142c23b2f97bd6a4f585497a"}, + {file = "fonttools-4.40.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb52c10fda31159c22c7ed85074e05f8b97da8773ea461706c273e31bcbea836"}, + {file = "fonttools-4.40.0-cp311-cp311-win32.whl", hash = "sha256:6a8d71b9a5c884c72741868e845c0e563c5d83dcaf10bb0ceeec3b4b2eb14c67"}, + {file = "fonttools-4.40.0-cp311-cp311-win_amd64.whl", hash = "sha256:15abb3d055c1b2dff9ce376b6c3db10777cb74b37b52b78f61657634fd348a0d"}, + {file = "fonttools-4.40.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:14037c31138fbd21847ad5e5441dfdde003e0a8f3feb5812a1a21fd1c255ffbd"}, + {file = "fonttools-4.40.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:94c915f6716589f78bc00fbc14c5b8de65cfd11ee335d32504f1ef234524cb24"}, + {file = "fonttools-4.40.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37467cee0f32cada2ec08bc16c9c31f9b53ea54b2f5604bf25a1246b5f50593a"}, + {file = "fonttools-4.40.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56d4d85f5374b45b08d2f928517d1e313ea71b4847240398decd0ab3ebbca885"}, + {file = "fonttools-4.40.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8c4305b171b61040b1ee75d18f9baafe58bd3b798d1670078efe2c92436bfb63"}, + {file = "fonttools-4.40.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a954b90d1473c85a22ecf305761d9fd89da93bbd31dae86e7dea436ad2cb5dc9"}, + {file = "fonttools-4.40.0-cp38-cp38-win32.whl", hash = "sha256:1bc4c5b147be8dbc5df9cc8ac5e93ee914ad030fe2a201cc8f02f499db71011d"}, + {file = "fonttools-4.40.0-cp38-cp38-win_amd64.whl", hash = "sha256:8a917828dbfdb1cbe50cf40eeae6fbf9c41aef9e535649ed8f4982b2ef65c091"}, + {file = "fonttools-4.40.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:882983279bf39afe4e945109772c2ffad2be2c90983d6559af8b75c19845a80a"}, + {file = "fonttools-4.40.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c55f1b4109dbc3aeb496677b3e636d55ef46dc078c2a5e3f3db4e90f1c6d2907"}, + {file = "fonttools-4.40.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec468c022d09f1817c691cf884feb1030ef6f1e93e3ea6831b0d8144c06480d1"}, + {file = "fonttools-4.40.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d5adf4ba114f028fc3f5317a221fd8b0f4ef7a2e5524a2b1e0fd891b093791a"}, + {file = "fonttools-4.40.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:aa83b3f151bc63970f39b2b42a06097c5a22fd7ed9f7ba008e618de4503d3895"}, + {file = "fonttools-4.40.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:97d95b8301b62bdece1af943b88bcb3680fd385f88346a4a899ee145913b414a"}, + {file = "fonttools-4.40.0-cp39-cp39-win32.whl", hash = "sha256:1a003608400dd1cca3e089e8c94973c6b51a4fb1ef00ff6d7641617b9242e637"}, + {file = "fonttools-4.40.0-cp39-cp39-win_amd64.whl", hash = "sha256:7961575221e3da0841c75da53833272c520000d76f7f71274dbf43370f8a1065"}, + {file = "fonttools-4.40.0-py3-none-any.whl", hash = "sha256:200729d12461e2038700d31f0d49ad5a7b55855dec7525074979a06b46f88505"}, + {file = "fonttools-4.40.0.tar.gz", hash = "sha256:337b6e83d7ee73c40ea62407f2ce03b07c3459e213b6f332b94a69923b9e1cb9"}, ] [package.extras] @@ -910,22 +942,22 @@ requests = ["requests (>=2.18.0,<3.0.0dev)"] [[package]] name = "googleapis-common-protos" -version = "1.59.0" +version = "1.59.1" description = "Common protobufs used in Google APIs" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "googleapis-common-protos-1.59.0.tar.gz", hash = "sha256:4168fcb568a826a52f23510412da405abd93f4d23ba544bb68d943b14ba3cb44"}, - {file = "googleapis_common_protos-1.59.0-py2.py3-none-any.whl", hash = "sha256:b287dc48449d1d41af0c69f4ea26242b5ae4c3d7249a38b0984c86a4caffff1f"}, + {file = "googleapis-common-protos-1.59.1.tar.gz", hash = "sha256:b35d530fe825fb4227857bc47ad84c33c809ac96f312e13182bdeaa2abe1178a"}, + {file = "googleapis_common_protos-1.59.1-py2.py3-none-any.whl", hash = "sha256:0cbedb6fb68f1c07e18eb4c48256320777707e7d0c55063ae56c15db3224a61e"}, ] [package.dependencies] -grpcio = {version = ">=1.44.0,<2.0.0dev", optional = true, markers = "extra == \"grpc\""} -protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" +grpcio = {version = ">=1.44.0,<2.0.0.dev0", optional = true, markers = "extra == \"grpc\""} +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" [package.extras] -grpc = ["grpcio (>=1.44.0,<2.0.0dev)"] +grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] [[package]] name = "grpc-google-iam-v1" @@ -1611,30 +1643,30 @@ tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "pa [[package]] name = "platformdirs" -version = "3.5.1" +version = "3.5.3" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "platformdirs-3.5.1-py3-none-any.whl", hash = "sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5"}, - {file = "platformdirs-3.5.1.tar.gz", hash = "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f"}, + {file = "platformdirs-3.5.3-py3-none-any.whl", hash = "sha256:0ade98a4895e87dc51d47151f7d2ec290365a585151d97b4d8d6312ed6132fed"}, + {file = "platformdirs-3.5.3.tar.gz", hash = "sha256:e48fabd87db8f3a7df7150a4a5ea22c546ee8bc39bc2473244730d4b56d2cc4e"}, ] [package.extras] -docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.2.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] +docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)"] [[package]] name = "plotly" -version = "5.14.1" +version = "5.15.0" description = "An open-source, interactive data visualization library for Python" category = "main" optional = false python-versions = ">=3.6" files = [ - {file = "plotly-5.14.1-py2.py3-none-any.whl", hash = "sha256:a63f3ad9e4cc2e02902a738e5e3e7f3d1307f2732ac71a6c28f1238ed3052826"}, - {file = "plotly-5.14.1.tar.gz", hash = "sha256:bcac86d7fcba3eff7260c1eddc36ca34dae2aded10a0709808446565e0e53b93"}, + {file = "plotly-5.15.0-py2.py3-none-any.whl", hash = "sha256:3508876bbd6aefb8a692c21a7128ca87ce42498dd041efa5c933ee44b55aab24"}, + {file = "plotly-5.15.0.tar.gz", hash = "sha256:822eabe53997d5ebf23c77e1d1fcbf3bb6aa745eb05d532afd4b6f9a2e2ab02f"}, ] [package.dependencies] @@ -1731,37 +1763,37 @@ files = [ [[package]] name = "pyarrow" -version = "12.0.0" +version = "12.0.1" description = "Python library for Apache Arrow" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "pyarrow-12.0.0-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:3b97649c8a9a09e1d8dc76513054f1331bd9ece78ee39365e6bf6bc7503c1e94"}, - {file = "pyarrow-12.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bc4ea634dacb03936f50fcf59574a8e727f90c17c24527e488d8ceb52ae284de"}, - {file = "pyarrow-12.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d568acfca3faa565d663e53ee34173be8e23a95f78f2abfdad198010ec8f745"}, - {file = "pyarrow-12.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b50bb9a82dca38a002d7cbd802a16b1af0f8c50ed2ec94a319f5f2afc047ee9"}, - {file = "pyarrow-12.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:3d1733b1ea086b3c101427d0e57e2be3eb964686e83c2363862a887bb5c41fa8"}, - {file = "pyarrow-12.0.0-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:a7cd32fe77f967fe08228bc100433273020e58dd6caced12627bcc0a7675a513"}, - {file = "pyarrow-12.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:92fb031e6777847f5c9b01eaa5aa0c9033e853ee80117dce895f116d8b0c3ca3"}, - {file = "pyarrow-12.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:280289ebfd4ac3570f6b776515baa01e4dcbf17122c401e4b7170a27c4be63fd"}, - {file = "pyarrow-12.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:272f147d4f8387bec95f17bb58dcfc7bc7278bb93e01cb7b08a0e93a8921e18e"}, - {file = "pyarrow-12.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:0846ace49998825eda4722f8d7f83fa05601c832549c9087ea49d6d5397d8cec"}, - {file = "pyarrow-12.0.0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:993287136369aca60005ee7d64130f9466489c4f7425f5c284315b0a5401ccd9"}, - {file = "pyarrow-12.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a7b6a765ee4f88efd7d8348d9a1f804487d60799d0428b6ddf3344eaef37282"}, - {file = "pyarrow-12.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1c4fce253d5bdc8d62f11cfa3da5b0b34b562c04ce84abb8bd7447e63c2b327"}, - {file = "pyarrow-12.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e6be4d85707fc8e7a221c8ab86a40449ce62559ce25c94321df7c8500245888f"}, - {file = "pyarrow-12.0.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:ea830d9f66bfb82d30b5794642f83dd0e4a718846462d22328981e9eb149cba8"}, - {file = "pyarrow-12.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7b5b9f60d9ef756db59bec8d90e4576b7df57861e6a3d6a8bf99538f68ca15b3"}, - {file = "pyarrow-12.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b99e559d27db36ad3a33868a475f03e3129430fc065accc839ef4daa12c6dab6"}, - {file = "pyarrow-12.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b0810864a593b89877120972d1f7af1d1c9389876dbed92b962ed81492d3ffc"}, - {file = "pyarrow-12.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:23a77d97f4d101ddfe81b9c2ee03a177f0e590a7e68af15eafa06e8f3cf05976"}, - {file = "pyarrow-12.0.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:2cc63e746221cddb9001f7281dee95fd658085dd5b717b076950e1ccc607059c"}, - {file = "pyarrow-12.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d8c26912607e26c2991826bbaf3cf2b9c8c3e17566598c193b492f058b40d3a4"}, - {file = "pyarrow-12.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d8b90efc290e99a81d06015f3a46601c259ecc81ffb6d8ce288c91bd1b868c9"}, - {file = "pyarrow-12.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2466be046b81863be24db370dffd30a2e7894b4f9823fb60ef0a733c31ac6256"}, - {file = "pyarrow-12.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:0e36425b1c1cbf5447718b3f1751bf86c58f2b3ad299f996cd9b1aa040967656"}, - {file = "pyarrow-12.0.0.tar.gz", hash = "sha256:19c812d303610ab5d664b7b1de4051ae23565f9f94d04cbea9e50569746ae1ee"}, + {file = "pyarrow-12.0.1-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:6d288029a94a9bb5407ceebdd7110ba398a00412c5b0155ee9813a40d246c5df"}, + {file = "pyarrow-12.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:345e1828efdbd9aa4d4de7d5676778aba384a2c3add896d995b23d368e60e5af"}, + {file = "pyarrow-12.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d6009fdf8986332b2169314da482baed47ac053311c8934ac6651e614deacd6"}, + {file = "pyarrow-12.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d3c4cbbf81e6dd23fe921bc91dc4619ea3b79bc58ef10bce0f49bdafb103daf"}, + {file = "pyarrow-12.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:cdacf515ec276709ac8042c7d9bd5be83b4f5f39c6c037a17a60d7ebfd92c890"}, + {file = "pyarrow-12.0.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:749be7fd2ff260683f9cc739cb862fb11be376de965a2a8ccbf2693b098db6c7"}, + {file = "pyarrow-12.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6895b5fb74289d055c43db3af0de6e16b07586c45763cb5e558d38b86a91e3a7"}, + {file = "pyarrow-12.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1887bdae17ec3b4c046fcf19951e71b6a619f39fa674f9881216173566c8f718"}, + {file = "pyarrow-12.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2c9cb8eeabbadf5fcfc3d1ddea616c7ce893db2ce4dcef0ac13b099ad7ca082"}, + {file = "pyarrow-12.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:ce4aebdf412bd0eeb800d8e47db854f9f9f7e2f5a0220440acf219ddfddd4f63"}, + {file = "pyarrow-12.0.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:e0d8730c7f6e893f6db5d5b86eda42c0a130842d101992b581e2138e4d5663d3"}, + {file = "pyarrow-12.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43364daec02f69fec89d2315f7fbfbeec956e0d991cbbef471681bd77875c40f"}, + {file = "pyarrow-12.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:051f9f5ccf585f12d7de836e50965b3c235542cc896959320d9776ab93f3b33d"}, + {file = "pyarrow-12.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:be2757e9275875d2a9c6e6052ac7957fbbfc7bc7370e4a036a9b893e96fedaba"}, + {file = "pyarrow-12.0.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:cf812306d66f40f69e684300f7af5111c11f6e0d89d6b733e05a3de44961529d"}, + {file = "pyarrow-12.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:459a1c0ed2d68671188b2118c63bac91eaef6fc150c77ddd8a583e3c795737bf"}, + {file = "pyarrow-12.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85e705e33eaf666bbe508a16fd5ba27ca061e177916b7a317ba5a51bee43384c"}, + {file = "pyarrow-12.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9120c3eb2b1f6f516a3b7a9714ed860882d9ef98c4b17edcdc91d95b7528db60"}, + {file = "pyarrow-12.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:c780f4dc40460015d80fcd6a6140de80b615349ed68ef9adb653fe351778c9b3"}, + {file = "pyarrow-12.0.1-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:a3c63124fc26bf5f95f508f5d04e1ece8cc23a8b0af2a1e6ab2b1ec3fdc91b24"}, + {file = "pyarrow-12.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b13329f79fa4472324f8d32dc1b1216616d09bd1e77cfb13104dec5463632c36"}, + {file = "pyarrow-12.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb656150d3d12ec1396f6dde542db1675a95c0cc8366d507347b0beed96e87ca"}, + {file = "pyarrow-12.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6251e38470da97a5b2e00de5c6a049149f7b2bd62f12fa5dbb9ac674119ba71a"}, + {file = "pyarrow-12.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:3de26da901216149ce086920547dfff5cd22818c9eab67ebc41e863a5883bac7"}, + {file = "pyarrow-12.0.1.tar.gz", hash = "sha256:cce317fc96e5b71107bf1f9f184d5e54e2bd14bbf3f9a3d62819961f0af86fec"}, ] [package.dependencies] @@ -2470,14 +2502,14 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "coverage-enable-subprocess [[package]] name = "werkzeug" -version = "2.3.4" +version = "2.3.6" description = "The comprehensive WSGI web application library." category = "main" optional = false python-versions = ">=3.8" files = [ - {file = "Werkzeug-2.3.4-py3-none-any.whl", hash = "sha256:48e5e61472fee0ddee27ebad085614ebedb7af41e88f687aaf881afb723a162f"}, - {file = "Werkzeug-2.3.4.tar.gz", hash = "sha256:1d5a58e0377d1fe39d061a5de4469e414e78ccb1e1e59c0f5ad6fa1c36c52b76"}, + {file = "Werkzeug-2.3.6-py3-none-any.whl", hash = "sha256:935539fa1413afbb9195b24880778422ed620c0fc09670945185cce4d91a8890"}, + {file = "Werkzeug-2.3.6.tar.gz", hash = "sha256:98c774df2f91b05550078891dee5f0eb0cb797a522c757a2452b9cee5b202330"}, ] [package.dependencies] @@ -2508,4 +2540,4 @@ docs = ["Sphinx", "sphinx-charts", "sphinx-math-dollar", "sphinx-rtd-theme", "sp [metadata] lock-version = "2.0" python-versions = ">=3.8,<3.11" -content-hash = "37c3efc794b2a24e7e390258db9625d49e33d770b20ccc50f5a2d13752aab1f2" +content-hash = "1af9df2c850862953123cf79d66ebac5e10ac05350c340e86d091274e746e999" From db9a15298f2baddb4a4d4e0100fd54e525a1b058 Mon Sep 17 00:00:00 2001 From: cortadocodes Date: Tue, 13 Jun 2023 12:57:47 +0100 Subject: [PATCH 14/17] ENH: Move installation checker button to installations section --- dashboard/layouts.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/dashboard/layouts.py b/dashboard/layouts.py index 7ad3029..14dd401 100644 --- a/dashboard/layouts.py +++ b/dashboard/layouts.py @@ -42,6 +42,9 @@ def create_sensors_tab_layout(app, tab_name, sensor_names, graph_id, data_limit_ ], ), html.Br(), + html.Button("Get new installations", id="installation-check-button", n_clicks=0), + html.Br(), + html.Br(), html.Label(html.B("Sensor")), SensorSelect(sensor_names), html.Br(), @@ -147,7 +150,6 @@ def create_sensors_tab_layout(app, tab_name, sensor_names, graph_id, data_limit_ ), html.Br(), html.Button("Plot", id="refresh-button", n_clicks=0), - html.Button("Check for new installations", id="installation-check-button", n_clicks=0), ], id="buttons-section", className="sidebar-content", @@ -202,6 +204,10 @@ def create_cp_plot_tab_layout(app): NodeSelect(), ], ), + html.Br(), + html.Button("Get new installations", id="installation-check-button", n_clicks=0), + html.Br(), + html.Br(), html.Label("Sensor coordinates reference"), dcc.Loading( [ @@ -337,7 +343,6 @@ def create_cp_plot_tab_layout(app): ), html.Br(), html.Button("Plot", id="refresh-button", n_clicks=0), - html.Button("Check for new installations", id="installation-check-button", n_clicks=0), html.Button( "Check for new sensor coordinates", id="sensor-coordinates-check-button", From 4d22f60844019434790bf79e097e8d0d14cbd8ff Mon Sep 17 00:00:00 2001 From: cortadocodes Date: Tue, 13 Jun 2023 15:49:27 +0100 Subject: [PATCH 15/17] FEA: Add button for extracting new sessions from database --- dashboard/callbacks.py | 13 +++++++++++++ dashboard/layouts.py | 9 +++++++++ poetry.lock | 8 ++++---- pyproject.toml | 2 +- 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/dashboard/callbacks.py b/dashboard/callbacks.py index af68ceb..485ba71 100644 --- a/dashboard/callbacks.py +++ b/dashboard/callbacks.py @@ -432,6 +432,19 @@ def update_measurement_session_selector( return measurement_sessions, measurement_sessions[0] + @app.callback( + # Use a dummy output. + Output("run-session-extraction", "children"), + Input("run-session-extraction", "n_clicks"), + prevent_initial_call=True, + ) + def run_session_extraction_in_database(refresh): + """Run session extraction in the database. + + :return None: + """ + BigQuery().extract_and_add_new_measurement_sessions() + @app.callback( Output("app", "children"), Input("nav-tabs", "value"), diff --git a/dashboard/layouts.py b/dashboard/layouts.py index 14dd401..43159d6 100644 --- a/dashboard/layouts.py +++ b/dashboard/layouts.py @@ -150,6 +150,15 @@ def create_sensors_tab_layout(app, tab_name, sensor_names, graph_id, data_limit_ ), html.Br(), html.Button("Plot", id="refresh-button", n_clicks=0), + dcc.Loading( + [ + html.Button( + "Run session extraction in database", + id="run-session-extraction", + n_clicks=0, + ), + ] + ), ], id="buttons-section", className="sidebar-content", diff --git a/poetry.lock b/poetry.lock index 931768c..93f3bf4 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2,7 +2,7 @@ [[package]] name = "aerosense-tools" -version = "0.10.0" +version = "0.10.1" description = "Functions for working with aerosense data, useful in building dashboards, analysis notebooks and digital twin services" category = "main" optional = false @@ -21,8 +21,8 @@ docs = [] [package.source] type = "git" url = "https://github.com/aerosense-ai/aerosense-tools.git" -reference = "0.10.0" -resolved_reference = "d354668e330a7e38cb0c9cec7978a8dfa4f0015e" +reference = "0.10.1" +resolved_reference = "12f38a0cf17683b013d5fe1c3856992f4b7c7f99" [[package]] name = "alabaster" @@ -2540,4 +2540,4 @@ docs = ["Sphinx", "sphinx-charts", "sphinx-math-dollar", "sphinx-rtd-theme", "sp [metadata] lock-version = "2.0" python-versions = ">=3.8,<3.11" -content-hash = "1af9df2c850862953123cf79d66ebac5e10ac05350c340e86d091274e746e999" +content-hash = "b023c5d874fec905862624a2d8a7ce9c06a0aead2c2dc55a4870ebc095130a3b" diff --git a/pyproject.toml b/pyproject.toml index 92ff051..22e5bb4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,7 +38,7 @@ gunicorn = "^20.1.0" dash = "^2.4.1" Flask-Caching = "^2.0.0" dash-daq = "^0.5.0" -aerosense-tools = {git = "https://github.com/aerosense-ai/aerosense-tools.git", rev = "0.10.0"} +aerosense-tools = {git = "https://github.com/aerosense-ai/aerosense-tools.git", rev = "0.10.1"} [tool.poetry.dev-dependencies] coverage = "^6.2" From f2127a82ac1840e3ac0e91b723ebe47919680c22 Mon Sep 17 00:00:00 2001 From: cortadocodes Date: Wed, 14 Jun 2023 12:45:54 +0100 Subject: [PATCH 16/17] ENH: Move measurement session extraction to cloud function --- dashboard/callbacks.py | 14 +++++++++++--- dashboard/layouts.py | 13 +++++-------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/dashboard/callbacks.py b/dashboard/callbacks.py index 485ba71..d96432a 100644 --- a/dashboard/callbacks.py +++ b/dashboard/callbacks.py @@ -1,8 +1,10 @@ import datetime import datetime as dt import logging +import threading import plotly.express as px +import requests from dash import Input, Output, State from dash.exceptions import PreventUpdate @@ -15,6 +17,11 @@ logger = logging.getLogger(__name__) +SESSIONS_EXTRACTION_CLOUD_FUNCTION_URL = ( + "https://europe-west6-aerosense-twined.cloudfunctions.net/data-gateway-sessions" +) + + def register_callbacks(app, cache, cache_timeout, tabs, sensor_types): """Register the dashboards callbacks with the app. @@ -434,16 +441,17 @@ def update_measurement_session_selector( @app.callback( # Use a dummy output. - Output("run-session-extraction", "children"), + Output("run-session-extraction-output-placeholder", "children"), Input("run-session-extraction", "n_clicks"), prevent_initial_call=True, ) def run_session_extraction_in_database(refresh): - """Run session extraction in the database. + """Trigger measurement session extraction in the database. :return None: """ - BigQuery().extract_and_add_new_measurement_sessions() + threading.Thread(target=requests.post, args=(SESSIONS_EXTRACTION_CLOUD_FUNCTION_URL,), daemon=True).start() + logger.info("Triggered measurement session extraction cloud function.") @app.callback( Output("app", "children"), diff --git a/dashboard/layouts.py b/dashboard/layouts.py index 43159d6..200e173 100644 --- a/dashboard/layouts.py +++ b/dashboard/layouts.py @@ -150,15 +150,12 @@ def create_sensors_tab_layout(app, tab_name, sensor_names, graph_id, data_limit_ ), html.Br(), html.Button("Plot", id="refresh-button", n_clicks=0), - dcc.Loading( - [ - html.Button( - "Run session extraction in database", - id="run-session-extraction", - n_clicks=0, - ), - ] + html.Button( + "Run session extraction in database", + id="run-session-extraction", + n_clicks=0, ), + html.Br(id="run-session-extraction-output-placeholder"), ], id="buttons-section", className="sidebar-content", From e9d6348bb210228c225732076d05bb6f5d253b01 Mon Sep 17 00:00:00 2001 From: cortadocodes Date: Wed, 14 Jun 2023 12:56:07 +0100 Subject: [PATCH 17/17] DOC: Update docstring --- dashboard/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dashboard/utils.py b/dashboard/utils.py index 9b34d87..aa95727 100644 --- a/dashboard/utils.py +++ b/dashboard/utils.py @@ -20,7 +20,7 @@ def generate_time_range(time_range, measurement_session=None): - Last month - Last year - All time - - Custom + - Measurement session :param str time_range: :param str|None measurement_session: