From a293a81277a644b72fec514c1a6fe35a23fdf509 Mon Sep 17 00:00:00 2001 From: Florian Bezannier Date: Mon, 3 May 2021 16:59:54 +0200 Subject: [PATCH] add card to webui --- web/figures.py | 47 ++--- web/static/images/battery-charge-line.svg | 54 ++++++ web/static/images/consumption.svg | 52 +++++ web/static/images/electricity bill.svg | 224 ++++++++++++++++++++++ web/static/images/pollution.svg | 77 ++++++++ web/views.py | 58 ++++-- 6 files changed, 462 insertions(+), 50 deletions(-) create mode 100644 web/static/images/battery-charge-line.svg create mode 100644 web/static/images/consumption.svg create mode 100644 web/static/images/electricity bill.svg create mode 100644 web/static/images/pollution.svg diff --git a/web/figures.py b/web/figures.py index 088ce0c..2dcc436 100644 --- a/web/figures.py +++ b/web/figures.py @@ -62,6 +62,12 @@ info = "" battery_info = ERROR_DIV battery_table = None +SUMMARY_CARDS = {"Average consumption": {"text": None, "src": "static/images/consumption.svg"}, + "Average emission": {"text": None, "src": "static/images/pollution.svg"}, + "Average charge speed": {"text": None, "src": "static/images/battery-charge-line.svg"}, + "Electricity consumption": {"text": None, "src": "static/images/electricity bill.svg"} + } + # pylint: disable=too-many-locals def get_figures(trips: Trips, charging: List[dict]): @@ -115,7 +121,8 @@ def get_figures(trips: Trips, charging: List[dict]): ) # consumption_fig consumption_df = DataFrame.from_records(trips.get_long_trips()) - consumption_fig = px.line(consumption_df, x="date", y="consumption", title='Consumption of the car') + consumption_fig = px.histogram(consumption_df, x="date", y="consumption", title='Consumption of the car', + histfunc="avg") consumption_fig.update_layout(yaxis_title="Consumption kWh/100Km") consumption_fig_by_speed = px.histogram(consumption_df, x="speed", y="consumption_km", histfunc="avg", @@ -143,38 +150,11 @@ def get_figures(trips: Trips, charging: List[dict]): price_kw = 0 total_elec = 0 - battery_info = html.Div(children=[ - html.Tr( - [ - html.Td('Average emission:', rowSpan=2), - html.Td("{:.1f} g/km".format(co2_per_km))]), - html.Tr( - [ - "{:.1f} g/kWh".format(co2_per_kw), - ] - ), - html.Tr([ - html.Td("Average charge speed:", style=PADDING_TOP), - html.Td("{:.3f} kW".format(charge_speed)) - ]), - html.Tr(html.Td(" ", colSpan=2)), - html.Tr([ - html.Td('Average Price:', rowSpan=2, style=PADDING_TOP), - html.Td("{:.2f} {}/100km".format(price_kw * kw_per_km, ElecPrice.currency)), - ]), - html.Tr([ - "{:.2f} {}/kWh".format(price_kw, ElecPrice.currency), - ]), - html.Tr(html.Td(" ", colSpan=2)), - html.Tr([ - html.Td('Electricity consumption:', rowSpan=2, style=PADDING_TOP), - html.Td("{:.0f} kWh".format(total_elec)), - ]), - html.Tr([ - "{:.0f} {}".format(total_elec * price_kw, ElecPrice.currency), - ]), - ]) - + SUMMARY_CARDS["Average charge speed"]["text"] = f"{charge_speed:.2f} kW" + SUMMARY_CARDS["Average emission"]["text"] = [html.P(f"{co2_per_km:.1f} g/km"), html.P(f"{co2_per_kw:.1f} g/kWh")] + SUMMARY_CARDS["Electricity consumption"]["text"] = [f"{total_elec:.0f} kWh", html.Br(), \ + f"{total_elec * price_kw:.0f} {ElecPrice.currency}"] + SUMMARY_CARDS["Average consumption"]["text"] = f"{consumption_df['consumption_km'].mean():.1f} kWh/100km" battery_table = dash_table.DataTable( id='battery-table', sort_action='native', @@ -220,6 +200,7 @@ def get_figures(trips: Trips, charging: List[dict]): consumption_graph_by_temp = html.Div(Graph(style={'display': 'none'}), id="consumption_graph_by_temp") return True + def __calculate_co2_per_kw(charging_data): try: co2_data = charging_data[charging_data["co2"] > 0] diff --git a/web/static/images/battery-charge-line.svg b/web/static/images/battery-charge-line.svg new file mode 100644 index 0000000..a763347 --- /dev/null +++ b/web/static/images/battery-charge-line.svg @@ -0,0 +1,54 @@ + +image/svg+xml diff --git a/web/static/images/consumption.svg b/web/static/images/consumption.svg new file mode 100644 index 0000000..e9b18db --- /dev/null +++ b/web/static/images/consumption.svg @@ -0,0 +1,52 @@ + +image/svg+xml diff --git a/web/static/images/electricity bill.svg b/web/static/images/electricity bill.svg new file mode 100644 index 0000000..93ed62c --- /dev/null +++ b/web/static/images/electricity bill.svg @@ -0,0 +1,224 @@ + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + electricity cost bill copy + + Created with Sketch. + + + + + + + + + + + + + + + + + + + diff --git a/web/static/images/pollution.svg b/web/static/images/pollution.svg new file mode 100644 index 0000000..fbe0761 --- /dev/null +++ b/web/static/images/pollution.svg @@ -0,0 +1,77 @@ + + + + + + image/svg+xml + + + + + + + 12, cloud, smoke, polution, CO2, ecology, enviroment, + + + + + + + + diff --git a/web/views.py b/web/views.py index d3f6f20..f18cac0 100644 --- a/web/views.py +++ b/web/views.py @@ -4,6 +4,7 @@ from typing import List import dash_bootstrap_components as dbc from dash.dependencies import Output, Input, MATCH, State +from dash.development.base_component import Component from dash.exceptions import PreventUpdate import dash_core_components as dcc import dash_html_components as html @@ -61,9 +62,8 @@ def create_callback(): # noqa: MC0001 Output('consumption_fig', 'figure'), Output('consumption_fig_by_speed', 'figure'), Output('consumption_graph_by_temp', 'children'), - Output('consumption', 'children'), + Output('summary-cards', 'children'), Output('tab_trips_fig', 'children'), - Output('tab_battery_fig', 'children'), Output('tab_charge', 'children'), Output('date-slider', 'max'), Output('date-slider', 'step'), @@ -78,11 +78,9 @@ def create_callback(): # noqa: MC0001 filtered_trips.append(trip) filtered_chargings = Charging.get_chargings(mini, maxi) figures.get_figures(filtered_trips, filtered_chargings) - consumption = "Average consumption: {:.1f} kWh/100km".format( - float(figures.consumption_df["consumption_km"].mean())) return figures.trips_map, figures.consumption_fig, figures.consumption_fig_by_speed, \ - figures.consumption_graph_by_temp, consumption, figures.table_fig, figures.battery_info, \ - figures.battery_table, max_millis, step, marks + figures.consumption_graph_by_temp, create_card(figures.SUMMARY_CARDS), \ + figures.table_fig, figures.battery_table, max_millis, step, marks @dash_app.callback(Output(EMPTY_DIV, "children"), [Input("battery-table", "data_timestamp")], @@ -268,7 +266,7 @@ def update_trips(): return # update for slider try: - logger.debug("min_date:%s - max_date:%s",min_date, max_date) + logger.debug("min_date:%s - max_date:%s", min_date, max_date) min_millis = figures.unix_time_millis(min_date) max_millis = figures.unix_time_millis(max_date) step = (max_millis - min_millis) / 100 @@ -280,6 +278,7 @@ def update_trips(): logger.debug("position table is probably empty :", exc_info=True) return + def __get_control_tabs(): tabs = [] for car in myp.vehicles_list: @@ -299,14 +298,35 @@ def __get_control_tabs(): return tabs +def create_card(card: dict): + res = [] + for tile, value in card.items(): + text = value["text"] + # if isinstance(text, str): + # text = html.H3(text) + res.append(html.Div( + dbc.Card([ + html.H4(tile, className="card-title text-center"), + dbc.Row([ + dbc.Col(dbc.CardBody(text, style={"white-space": "nowrap", "font-size": "160%"}), + className="text-center"), + dbc.Col(dbc.CardImg(src=value.get("src", Component.UNDEFINED), style={"max-height": "7rem"})) + ], + className="align-items-center flex-nowrap") + ], className="h-100 p-2"), + className="col-sm-12 col-md-6 col-lg-3 py-2" + )) + return res + + def serve_layout(): global cached_layout if cached_layout is None: logger.debug("Create new layout") try: figures.get_figures(trips, chargings) - summary_tab = [html.H2(id="consumption", - children=figures.info), + summary_tab = [dbc.Container(dbc.Row(id="summary-cards", + children=create_card(figures.SUMMARY_CARDS)), fluid=True), dcc.Graph(figure=figures.consumption_fig, id="consumption_fig"), dcc.Graph(figure=figures.consumption_fig_by_speed, id="consumption_fig_by_speed"), figures.consumption_graph_by_temp] @@ -323,7 +343,7 @@ def serve_layout(): except (IndexError, TypeError, NameError): summary_tab = figures.ERROR_DIV maps = figures.ERROR_DIV - logger.warning("Failed to generate figure, there is probably not enough data yet") + logger.warning("Failed to generate figure, there is probably not enough data yet", exc_info_debug=True) range_slider = html.Div() data_div = html.Div([ range_slider, @@ -347,20 +367,24 @@ def serve_layout(): size="xl", ) ]), - dbc.Tab(label="Battery", tab_id="battery", id="tab_battery", - children=[html.Div(id="tab_battery_fig", children=[figures.battery_info]), + dbc.Tab(label="Charge", tab_id="charge", id="tab_charge", + children=[figures.battery_table, dbc.Modal( [ - dbc.ModalHeader("Charging speed"), - dbc.ModalBody(html.Div(id="tab_battery_popup_graph")), + dbc.ModalHeader( + "Charging speed"), + dbc.ModalBody(html.Div( + id="tab_battery_popup_graph")), dbc.ModalFooter( - dbc.Button("Close", id="tab_battery_popup-close", className="ml-auto") + dbc.Button("Close", + id="tab_battery_popup-close", + className="ml-auto") ), ], id="tab_battery_popup", size="xl", - )]), - dbc.Tab(label="Charge", tab_id="charge", id="tab_charge", children=[figures.battery_table]), + ) + ]), dbc.Tab(label="Map", tab_id="map", children=[maps]), dbc.Tab(label="Control", tab_id="control", children=dbc.Tabs(id="control-tabs", children=__get_control_tabs()))],