add tabs to dashboard, fix get_trips

This commit is contained in:
Florian Bezannier
2020-12-22 09:27:39 +01:00
parent b45a1bec26
commit 534a4300e5
6 changed files with 136 additions and 89 deletions
+11 -8
View File
@@ -10,20 +10,20 @@ from json import JSONEncoder
from hashlib import md5
from time import sleep
import pytz
from oauth2_client.credentials_manager import CredentialManager, ServiceInformation
import paho.mqtt.client as mqtt
from requests import Response
from typing import List
import psa_connectedcar as psac
from Trips import Trips
from Trip import Trip
from psa_connectedcar import ApiClient
from psa_connectedcar.rest import ApiException
from MyLogger import logger
from threading import Semaphore, Timer
from functools import wraps
from web.figures import convert_datetime
import sqlite3
oauhth_url = {"clientsB2CPeugeot":"https://idpcvs.peugeot.com/am/oauth2/access_token",
"clientsB2CCitroen":"https://idpcvs.citroen.com/am/oauth2/access_token",
@@ -406,7 +406,6 @@ class MyPSACC:
self._record_enabled = value
def record_position(self,vin, res:psac.models.status.Status):
import sqlite3
conn = sqlite3.connect('info.db')
conn.execute(
"CREATE TABLE IF NOT EXISTS position (Timestamp DATETIME PRIMARY KEY, VIN TEXT, longitude REAL, latitude REAL, mileage REAL, level INTEGER);")
@@ -442,7 +441,7 @@ class MyPSACC:
return geo_dumps(feature_collection, sort_keys=True)
@staticmethod
def get_trips() -> List[Trips]:
def get_trips() -> List[Trip]:
sqlite3.register_converter("DATETIME", convert_datetime)
conn = sqlite3.connect('info.db', detect_types=sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES)
conn.row_factory = sqlite3.Row
@@ -450,11 +449,13 @@ class MyPSACC:
start = res[0]
end = res[1]
trips = []
tr = Trips()
tr = Trip()
battery_power = 46
for next_el in res[2:]:
distance = next_el["mileage"] - end["mileage"] # km
if distance == 0:
duration = (next_el["Timestamp"] - end["Timestamp"]).total_seconds()/3600
speed = distance/duration
if distance == 0 or speed < 1: # check the speed to handle missing point
tr.distance = end["mileage"] - start["mileage"] # km
if tr.distance > 0:
tr.start_at = start["Timestamp"]
@@ -469,7 +470,7 @@ class MyPSACC:
f"{start['Timestamp']} {tr.distance:.1f}km {tr.duration:.2f}h {tr.speed_average:.2f} km/h {tr.consumption:.2f} kw {tr.consumption_km:.2f}kw/100km")
trips.append(tr)
start = next_el
tr = Trips()
tr = Trip()
else:
tr.add_points(end["longitude"], end["latitude"])
end = next_el
@@ -488,3 +489,5 @@ class MyPeugeotEncoder(JSONEncoder):
return mpd
def convert_datetime(st):
return datetime.strptime(st.decode("utf-8"), "%Y-%m-%d %H:%M:%S+00:00").replace(tzinfo=pytz.UTC)
+53
View File
@@ -0,0 +1,53 @@
from typing import List
from geojson import Feature, Point, FeatureCollection, MultiLineString
from geojson import dumps as geo_dumps
class Points():
def __init__(self, latitude, longitude):
self.latitude = latitude
self.longitude = longitude
def list(self):
return self.latitude, self.longitude
class Trips(list):
def __init__(self, *args):
list.__init__(self, *args)
def to_geo_json(self):
feature_collection = FeatureCollection(self)
return feature_collection
class Trip:
def __init__(self):
self.start_at = None
self.end_at = None
self.positions: List[Points] = []
self.speed_average = None
self.consumption = None
self.consumption_km = None
self.distance = None
self.duration = None
def add_points(self, longitude, latitude):
self.positions.append(Points(longitude, latitude))
def get_consumption(self):
return {
'date': self.start_at,
'consumption': self.consumption_km,
}
def to_geojson(self):
multi_line_string = MultiLineString(tuple(map(list, self.positions)))
return Feature(geometry=multi_line_string, properties={"start_at": self.start_at, "end_at": self.end_at,
"average speed": self.speed_average,
"average consumption": self.consumption_km})
def get_info(self):
res = {"start_at": self.start_at.strftime("%x %X"), "end_at": self.end_at.strftime("%x %X"), "duration": self.duration*60,
"speed_average": self.speed_average, "consumption_km": self.consumption_km, "distance": self.distance}
return res
-25
View File
@@ -1,25 +0,0 @@
from typing import List
class Points():
def __init__(self, latitude, longitude):
self.latitude = latitude
self.longitude = longitude
class Trips:
def __init__(self):
self.start_at = None
self.end_at = None
self.positions: List[Points] = []
self.speed_average = None
self.consumption = None
self.consumption_km = None
def add_points(self, longitude, latitude):
self.positions.append(Points(longitude, latitude))
def get_consumption(self):
return {
'date': self.start_at,
'consumption': self.consumption_km,
}
+14 -14
View File
@@ -1,16 +1,16 @@
paho-mqtt>=1.5.0
dash>=1.18.0
plotly>=4
pandas
oauth2_client
python-dateutil
Flask>=1.1.1
certifi>=2018.4.16
urllib3>=1.25.10
six>=1.15.0
requests>=2.24.0
paho-mqtt>=1.5.0
dash>=1.18.1
numpy>=1.19.4
pytz>=2019.3
plotly>=4.5.0
typing>=3.6.6
pandas>=1.1.3
argparse>=1.4.0
androguard>=3.3.5
certifi
urllib3
six
requests
numpy
pytz
typing
argparse
flask
dash_bootstrap_components
+35 -30
View File
@@ -6,15 +6,21 @@ import dash_bootstrap_components as dbc
from dash.dependencies import Output, Input
import dash_core_components as dcc
import dash_html_components as html
import locale
from MyLogger import logger
from flask import jsonify, request, Response as FlaskResponse
from web import figures
from MyPSACC import MyPSACC
try:
locale = locale.getlocale()[0].split("_")[0]
locale_url = [f"https://cdn.plot.ly/plotly-locale-{locale}-latest.js"]
dash_app = dash.Dash(external_stylesheets=[dbc.themes.BOOTSTRAP], external_scripts=locale_url, title="My car info")
except:
logger.warn("Can't get language")
dash_app = dash.Dash(external_stylesheets=[dbc.themes.BOOTSTRAP])
dash_app = dash.Dash(external_stylesheets=[dbc.themes.BOOTSTRAP])
app = dash_app.server
myp = None
chc = None
@@ -116,35 +122,34 @@ def save_config(my_peugeot: MyPSACC):
trips = MyPSACC.get_trips()
figures.get_figures(trips)
dash_app.layout = dbc.Container([
dash_app.layout = dbc.Container(fluid=True, children=[
html.H1('My car info'),
html.Hr(),
html.Div([
dcc.RangeSlider(
id='date-slider',
min=figures.unix_time_millis(figures.consumption_df["date"].min()),
max=figures.unix_time_millis(figures.consumption_df["date"].max()),
step=None,
marks=figures.get_marks_from_start_end(figures.consumption_df["date"].min(),
figures.consumption_df["date"].max()),
value=[figures.unix_time_millis(figures.consumption_df["date"].min()),
figures.unix_time_millis(figures.consumption_df["date"].max())],
),
html.Div(id='updatemode-output-container', style={'margin-top': 20})
],
style={'margin-left': 20}
dcc.RangeSlider(
id='date-slider',
min=figures.unix_time_millis(figures.consumption_df["date"].min()),
max=figures.unix_time_millis(figures.consumption_df["date"].max()),
step=None,
marks=figures.get_marks_from_start_end(figures.consumption_df["date"].min(),
figures.consumption_df["date"].max()),
value=[figures.unix_time_millis(figures.consumption_df["date"].min()),
figures.unix_time_millis(figures.consumption_df["date"].max())],
),
html.Div([
html.Div([
dcc.Graph(figure=figures.trips_map, id="trips_map")
]),
html.Div([
dcc.Graph(figure=figures.consumption_fig, id="consumption_fig")
]),
html.H2(id="consumption",
children="Average consumption: {:.1f} kW/100km".format(float(figures.consumption_df.mean()))),
html.Div([
dcc.Graph(figure=figures.consumption_fig_by_speed, id="consumption_fig_by_speed")
]),
], id="data-div"),
dbc.Tabs([
dbc.Tab(label="Summary", tab_id="summary", children=[
html.H2(id="consumption",
children="Average consumption: {:.1f} kW/100km".format(
float(figures.consumption_df.mean()))),
dcc.Graph(figure=figures.consumption_fig, id="consumption_fig"),
dcc.Graph(figure=figures.consumption_fig_by_speed, id="consumption_fig_by_speed")
]),
dbc.Tab(label="Trips", tab_id="trips", children=[figures.table_fig]),
dbc.Tab(label="Map", tab_id="map", children=[
dcc.Graph(figure=figures.trips_map, id="trips_map", style={"height": '90vh'})]),
],
id="tabs",
active_tab="summary",
),
html.Div(id="tab-content", className="p-4"),
]),
])
+23 -12
View File
@@ -1,12 +1,15 @@
from datetime import datetime
from copy import deepcopy
from typing import List
import dash_table
import numpy as np
import pytz
from dash_table.Format import Format, Scheme, Symbol
from dateutil.relativedelta import relativedelta
from pandas import DataFrame
import plotly.express as px
import plotly.graph_objects as go
from Trips import Trips
from Trip import Trip
from pandas import options as pandas_options
def unix_time_millis(dt):
@@ -36,18 +39,16 @@ def get_marks_from_start_end(start, end):
return marks
def convert_datetime(st):
return datetime.strptime(st.decode("utf-8"), "%Y-%m-%d %H:%M:%S+00:00").replace(tzinfo=pytz.UTC)
consumption_fig = None
consumption_df = None
trips_map = None
consumption_fig_by_speed = None
table_fig = None
pandas_options.display.float_format = '${:.2f}'.format
def get_figures(trips: List[Trips]):
global consumption_fig, consumption_df, trips_map, consumption_fig_by_speed
def get_figures(trips: List[Trip]):
global consumption_fig, consumption_df, trips_map, consumption_fig_by_speed, table_fig
lats = []
lons = []
names = []
@@ -60,6 +61,19 @@ def get_figures(trips: List[Trips]):
lons = np.append(lons, None)
names = np.append(names, None)
# table
nb_format = Format(precision=2, scheme=Scheme.fixed, symbol=Symbol.yes)
table_fig = dash_table.DataTable(
id='trips-table',
sort_action='native',
columns=[{'id': 'start_at', 'name': 'start at', 'type': 'datetime'},
{'id': 'duration', 'name': 'duration', 'type': 'numeric', 'format': deepcopy(nb_format).symbol_suffix(" min").precision(0)},
{'id': 'speed_average', 'name': 'average speed', 'type': 'numeric', 'format': deepcopy(nb_format).symbol_suffix(" km/h")},
{'id': 'consumption_km', 'name': 'average consumption', 'type': 'numeric', 'format': deepcopy(nb_format).symbol_suffix(" kw/100km")},
{'id': 'distance', 'name': 'distance', 'type': 'numeric', 'format': nb_format.symbol_suffix(" km")}],
data=[tr.get_info() for tr in trips],
)
# map
trips_map = px.line_mapbox(lat=lats, lon=lons, hover_name=names,
mapbox_style="stamen-terrain", zoom=12)
consumption_df = DataFrame.from_records([tr.get_consumption() for tr in trips])
@@ -75,6 +89,3 @@ def get_figures(trips: List[Trips]):
go.Scatter(mode="markers", x=consum_df_by_speed["speed"], y=consum_df_by_speed["value"],
name="Trips"))
consumption_fig_by_speed.update_layout(xaxis_title="average Speed km/h", yaxis_title="Consumption kW/100Km")