From 72753b5a097d973b4559d0beb1aeebf409211818 Mon Sep 17 00:00:00 2001 From: Florian Bezannier Date: Sat, 23 Oct 2021 11:05:02 +0200 Subject: [PATCH 01/13] fix deprecated and myp not found --- libs/config.py | 1 + web/figures.py | 8 ++++---- web/tools/Button.py | 2 +- web/tools/Switch.py | 2 +- web/tools/figurefilter.py | 7 ++++--- web/tools/import_dash_core.py | 5 +++++ web/tools/import_dash_html.py | 5 +++++ web/utils.py | 2 +- web/view/config_views.py | 5 +++-- web/view/control.py | 2 +- web/view/views.py | 4 ++-- 11 files changed, 28 insertions(+), 15 deletions(-) create mode 100644 web/tools/import_dash_core.py create mode 100644 web/tools/import_dash_html.py diff --git a/libs/config.py b/libs/config.py index f39fd0f..efda32f 100644 --- a/libs/config.py +++ b/libs/config.py @@ -94,6 +94,7 @@ class Config(metaclass=Singleton): logger.info(str(self.myp.get_vehicles())) self.is_good = True else: + self.is_good = False logger.error("Please reconnect by going to config web page") if self.args.refresh: self.myp.info_refresh_rate = self.args.refresh * 60 diff --git a/web/figures.py b/web/figures.py index dd74270..c024fae 100644 --- a/web/figures.py +++ b/web/figures.py @@ -3,11 +3,11 @@ from statistics import mean import dash_bootstrap_components as dbc import dash_table -from dash_core_components import Graph from dash_table.Format import Format, Scheme, Symbol import plotly.express as px import plotly.graph_objects as go -import dash_html_components as html +from web.tools.import_dash_html import html +from web.tools.import_dash_core import dcc from libs.car import Car from libs.elec_price import ElecPrice @@ -199,7 +199,7 @@ def get_battery_curve_fig(row: dict, car: Car): battery_curves.append({"level": row["end_level"], "speed": speed}) fig = px.line(battery_curves, x="level", y="speed") fig.update_layout(xaxis_title="Battery %", yaxis_title="Charging speed in kW") - return html.Div(Graph(figure=fig)) + return html.Div(dcc.Graph(figure=fig)) def get_altitude_fig(trip: Trip): @@ -212,4 +212,4 @@ def get_altitude_fig(trip: Trip): fig = px.line(res, x=0, y=1) fig.update_layout(xaxis_title="Distance km", yaxis_title="Altitude m") conn.close() - return html.Div(Graph(figure=fig)) + return html.Div(dcc.Graph(figure=fig)) diff --git a/web/tools/Button.py b/web/tools/Button.py index 582e73c..5292e8d 100644 --- a/web/tools/Button.py +++ b/web/tools/Button.py @@ -1,7 +1,7 @@ import dash_bootstrap_components as dbc from dash._utils import create_callback_id from dash.dependencies import Output, Input -import dash_html_components as html +from web.tools.import_dash_html import html from web.app import dash_app diff --git a/web/tools/Switch.py b/web/tools/Switch.py index 0321688..33e7cab 100644 --- a/web/tools/Switch.py +++ b/web/tools/Switch.py @@ -1,6 +1,6 @@ import dash_bootstrap_components as dbc import dash_daq as daq -import dash_html_components as html +from web.tools.import_dash_html import html from dash.dependencies import Input from web.app import dash_app diff --git a/web/tools/figurefilter.py b/web/tools/figurefilter.py index 722d9f6..3afc56d 100644 --- a/web/tools/figurefilter.py +++ b/web/tools/figurefilter.py @@ -3,7 +3,8 @@ from logging import DEBUG from dash._utils import create_callback_id from dash.dependencies import Output, Input -from dash_core_components import Store +from web.tools.import_dash_core import dcc + from mylogger import logger @@ -135,5 +136,5 @@ class FigureFilter: return False def get_store(self): - return [Store(id='clientside-figure-store', data=self.__get_figures()), - Store(id='clientside-data-store', data=self.src)] + return [dcc.Store(id='clientside-figure-store', data=self.__get_figures()), + dcc.Store(id='clientside-data-store', data=self.src)] diff --git a/web/tools/import_dash_core.py b/web/tools/import_dash_core.py new file mode 100644 index 0000000..153011d --- /dev/null +++ b/web/tools/import_dash_core.py @@ -0,0 +1,5 @@ +# pylint: disable=unused-import +try: + from dash import dcc +except ImportError: + import dash_core_components as dcc diff --git a/web/tools/import_dash_html.py b/web/tools/import_dash_html.py new file mode 100644 index 0000000..3f7d5f4 --- /dev/null +++ b/web/tools/import_dash_html.py @@ -0,0 +1,5 @@ +# pylint: disable=unused-import +try: + from dash import html +except ImportError: + import dash_html_components as html diff --git a/web/utils.py b/web/utils.py index c584bd1..c0e88d5 100644 --- a/web/utils.py +++ b/web/utils.py @@ -1,7 +1,7 @@ from datetime import datetime, timedelta import dash_bootstrap_components as dbc -import dash_html_components as html +from web.tools.import_dash_html import html from dash.development.base_component import Component from pandas import DataFrame from pytz import UTC diff --git a/web/view/config_views.py b/web/view/config_views.py index c42ab6c..7baabc6 100644 --- a/web/view/config_views.py +++ b/web/view/config_views.py @@ -9,8 +9,8 @@ from otp.otp import new_otp_session from web.app import dash_app import dash_bootstrap_components as dbc from dash.dependencies import Output, Input, State -import dash_core_components as dcc -import dash_html_components as html +from web.tools.import_dash_core import dcc +from web.tools.import_dash_html import html config = Config() login_config_layout = dbc.Row(dbc.Col(md=12, lg=2, children=[ @@ -147,6 +147,7 @@ def connectPSA(n_clicks, app_name, email, password, countrycode): # pylint: dis try: res = firstLaunchConfig(app_name, email, password, countrycode) config.load_app() + config.start_remote_control() return dbc.Alert([res, html.A(" Go to otp config", href=request.url_root + "config_otp")], color="success") except Exception as e: res = str(e) diff --git a/web/view/control.py b/web/view/control.py index 3e59568..6386375 100644 --- a/web/view/control.py +++ b/web/view/control.py @@ -1,5 +1,5 @@ import dash_bootstrap_components as dbc -import dash_html_components as html +from web.tools.import_dash_html import html from mylogger import logger from web.tools.Button import Button diff --git a/web/view/views.py b/web/view/views.py index 5c56cc5..ae06b25 100644 --- a/web/view/views.py +++ b/web/view/views.py @@ -5,8 +5,6 @@ from urllib.parse import parse_qs, urlparse import dash_bootstrap_components as dbc from dash.dependencies import Output, Input, State from dash.exceptions import PreventUpdate -import dash_core_components as dcc -import dash_html_components as html from flask import jsonify, request, Response as FlaskResponse import web.utils @@ -25,6 +23,8 @@ from web.utils import diff_dashtable, dash_date_to_datetime # pylint: disable=invalid-name from web.tools.figurefilter import FigureFilter +from web.tools.import_dash_html import html +from web.tools.import_dash_core import dcc from web.utils import create_card from libs.config import Config from web.view.control import get_control_tabs From 220ae91386eedb2d7cc7f62663fcba6897d21ed0 Mon Sep 17 00:00:00 2001 From: Florian Bezannier Date: Sat, 23 Oct 2021 12:36:10 +0200 Subject: [PATCH 02/13] update to dash v2,dbc v1 and fix issue --- libs/requirements.py | 0 web/tools/import_dash_core.py | 5 ----- web/tools/import_dash_html.py | 5 ----- 3 files changed, 10 deletions(-) create mode 100644 libs/requirements.py delete mode 100644 web/tools/import_dash_core.py delete mode 100644 web/tools/import_dash_html.py diff --git a/libs/requirements.py b/libs/requirements.py new file mode 100644 index 0000000..e69de29 diff --git a/web/tools/import_dash_core.py b/web/tools/import_dash_core.py deleted file mode 100644 index 153011d..0000000 --- a/web/tools/import_dash_core.py +++ /dev/null @@ -1,5 +0,0 @@ -# pylint: disable=unused-import -try: - from dash import dcc -except ImportError: - import dash_core_components as dcc diff --git a/web/tools/import_dash_html.py b/web/tools/import_dash_html.py deleted file mode 100644 index 3f7d5f4..0000000 --- a/web/tools/import_dash_html.py +++ /dev/null @@ -1,5 +0,0 @@ -# pylint: disable=unused-import -try: - from dash import html -except ImportError: - import dash_html_components as html From 89e13627dea86448d38404a7d13f5d3c1dbdf884 Mon Sep 17 00:00:00 2001 From: Florian Bezannier Date: Sat, 23 Oct 2021 12:37:51 +0200 Subject: [PATCH 03/13] update to dash v2,dbc v1 and fix issue --- libs/config.py | 5 +++-- libs/requirements.py | 17 +++++++++++++++++ requirements.txt | 4 ++-- server.py | 5 +++++ web/figures.py | 16 ++++++++-------- web/tools/Button.py | 3 ++- web/tools/Switch.py | 2 +- web/tools/figurefilter.py | 2 +- web/utils.py | 2 +- web/view/config_views.py | 24 +++++++++++------------- web/view/control.py | 2 +- web/view/views.py | 6 ++---- 12 files changed, 54 insertions(+), 34 deletions(-) diff --git a/libs/config.py b/libs/config.py index efda32f..30a8491 100644 --- a/libs/config.py +++ b/libs/config.py @@ -57,7 +57,7 @@ class Config(metaclass=Singleton): def start_remote_control(self): if self.args.remote_disable: logger.info("mqtt disabled") - elif not self.args.web_conf or path.exists(OTP_CONFIG_NAME): + elif not self.args.web_conf or path.isfile(OTP_CONFIG_NAME): if self.myp.mqtt_client is not None: self.myp.mqtt_client.disconnect() self.myp.start_mqtt() @@ -70,9 +70,10 @@ class Config(metaclass=Singleton): my_logger(handler_level=int(self.args.debug)) if self.args.config: self.config_name = self.args.config - if path.exists(self.config_name): + if path.isfile(self.config_name): self.myp = MyPSACC.load_config(name=self.config_name) elif self.args.web_conf: + self.is_good = False return False else: raise FileNotFoundError(self.config_name) diff --git a/libs/requirements.py b/libs/requirements.py index e69de29..9e43fb2 100644 --- a/libs/requirements.py +++ b/libs/requirements.py @@ -0,0 +1,17 @@ +import pkg_resources +from pathlib import Path + + +class TestRequirements: + """Test availability of required packages.""" + + def __init__(self, requirement_path): + self.requirement_path = Path(requirement_path) + + def test_requirements(self): + """Test that each required package is available.""" + # Ref: https://stackoverflow.com/a/45474387/ + requirements = pkg_resources.parse_requirements(self.requirement_path.open()) + for requirement in requirements: + requirement = str(requirement) + pkg_resources.require(requirement) diff --git a/requirements.txt b/requirements.txt index e34af87..5b1b714 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ paho-mqtt>=1.5.0 -dash>=1.18.0 +dash>=2 dash_daq plotly>=4 cryptography>=2.6 @@ -11,7 +11,7 @@ pytz typing argparse flask -dash_bootstrap_components +dash_bootstrap_components>=1 geojson reverse_geocode androguard diff --git a/server.py b/server.py index 63acaef..b2f4585 100755 --- a/server.py +++ b/server.py @@ -2,9 +2,14 @@ # pylint: disable=wrong-import-position import sys from threading import Thread + +from libs.requirements import TestRequirements + if sys.version_info < (3, 6): raise RuntimeError("This application requires Python 3.6+") +TestRequirements("requirements.txt").test_requirements() + import web.app from libs.config import Config from mylogger import logger diff --git a/web/figures.py b/web/figures.py index c024fae..923837f 100644 --- a/web/figures.py +++ b/web/figures.py @@ -2,12 +2,12 @@ from copy import deepcopy from statistics import mean import dash_bootstrap_components as dbc -import dash_table -from dash_table.Format import Format, Scheme, Symbol +from dash import html +from dash.dash_table import Format, DataTable import plotly.express as px import plotly.graph_objects as go -from web.tools.import_dash_html import html -from web.tools.import_dash_core import dcc +from dash.dash_table.Format import Scheme, Symbol +from dash.dcc import Graph from libs.car import Car from libs.elec_price import ElecPrice @@ -69,7 +69,7 @@ def get_figures(car: Car): style_cell_conditional.append({'if': {'column_id': 'consumption_fuel_km', }, 'display': 'None', }) if car.is_thermal(): style_cell_conditional.append({'if': {'column_id': 'consumption_km', }, 'display': 'None', }) - table_fig = dash_table.DataTable( + table_fig = DataTable( id='trips-table', sort_action='custom', sort_by=[{'column_id': 'id', 'direction': 'desc'}], @@ -116,7 +116,7 @@ def get_figures(car: Car): consumption_fig_by_speed.update_layout(xaxis_title="average Speed km/h", yaxis_title="Consumption kWh/100Km") # battery_table - battery_table = dash_table.DataTable( + battery_table = DataTable( id='battery-table', sort_action='custom', sort_by=[{'column_id': 'start_at_str', 'direction': 'desc'}], @@ -199,7 +199,7 @@ def get_battery_curve_fig(row: dict, car: Car): battery_curves.append({"level": row["end_level"], "speed": speed}) fig = px.line(battery_curves, x="level", y="speed") fig.update_layout(xaxis_title="Battery %", yaxis_title="Charging speed in kW") - return html.Div(dcc.Graph(figure=fig)) + return html.Div(Graph(figure=fig)) def get_altitude_fig(trip: Trip): @@ -212,4 +212,4 @@ def get_altitude_fig(trip: Trip): fig = px.line(res, x=0, y=1) fig.update_layout(xaxis_title="Distance km", yaxis_title="Altitude m") conn.close() - return html.Div(dcc.Graph(figure=fig)) + return html.Div(Graph(figure=fig)) diff --git a/web/tools/Button.py b/web/tools/Button.py index 5292e8d..c8d9bcd 100644 --- a/web/tools/Button.py +++ b/web/tools/Button.py @@ -1,7 +1,8 @@ +import html + import dash_bootstrap_components as dbc from dash._utils import create_callback_id from dash.dependencies import Output, Input -from web.tools.import_dash_html import html from web.app import dash_app diff --git a/web/tools/Switch.py b/web/tools/Switch.py index 33e7cab..ff28ce9 100644 --- a/web/tools/Switch.py +++ b/web/tools/Switch.py @@ -1,6 +1,6 @@ import dash_bootstrap_components as dbc import dash_daq as daq -from web.tools.import_dash_html import html +from dash import html from dash.dependencies import Input from web.app import dash_app diff --git a/web/tools/figurefilter.py b/web/tools/figurefilter.py index 3afc56d..7031dd3 100644 --- a/web/tools/figurefilter.py +++ b/web/tools/figurefilter.py @@ -1,9 +1,9 @@ import json from logging import DEBUG +from dash import dcc from dash._utils import create_callback_id from dash.dependencies import Output, Input -from web.tools.import_dash_core import dcc from mylogger import logger diff --git a/web/utils.py b/web/utils.py index c0e88d5..db3c09f 100644 --- a/web/utils.py +++ b/web/utils.py @@ -1,7 +1,7 @@ from datetime import datetime, timedelta import dash_bootstrap_components as dbc -from web.tools.import_dash_html import html +from dash import html from dash.development.base_component import Component from pandas import DataFrame from pytz import UTC diff --git a/web/view/config_views.py b/web/view/config_views.py index 7baabc6..b81dc91 100644 --- a/web/view/config_views.py +++ b/web/view/config_views.py @@ -1,4 +1,4 @@ -from dash import callback_context +from dash import callback_context, html, dcc from dash.exceptions import PreventUpdate from flask import request @@ -9,14 +9,12 @@ from otp.otp import new_otp_session from web.app import dash_app import dash_bootstrap_components as dbc from dash.dependencies import Output, Input, State -from web.tools.import_dash_core import dcc -from web.tools.import_dash_html import html config = Config() -login_config_layout = dbc.Row(dbc.Col(md=12, lg=2, children=[ +login_config_layout = dbc.Row(dbc.Col(md=12, lg=2, style={"ml": 2}, children=[ html.H2('Config'), dbc.Form([ - dbc.FormGroup([ + dbc.Row([ dbc.Label("Car Brand", html_for="psa-app"), dcc.Dropdown( id="psa-app", @@ -28,7 +26,7 @@ login_config_layout = dbc.Row(dbc.Col(md=12, lg=2, children=[ {"label": "Vauxhall", "value": "com.psa.mym.myvauxhall"} ], )]), - dbc.FormGroup( + dbc.Row( [ dbc.Label("Email", html_for="psa-email"), dbc.Input(type="email", id="psa-email", placeholder="Enter email"), @@ -38,7 +36,7 @@ login_config_layout = dbc.Row(dbc.Col(md=12, lg=2, children=[ ), ] ), - dbc.FormGroup( + dbc.Row( [ dbc.Label("Password", html_for="psa-password"), dbc.Input( @@ -52,7 +50,7 @@ login_config_layout = dbc.Row(dbc.Col(md=12, lg=2, children=[ ), ] ), - dbc.FormGroup( + dbc.Row( [ dbc.Label("Country code", html_for="countrycode"), dbc.Input( @@ -66,7 +64,7 @@ login_config_layout = dbc.Row(dbc.Col(md=12, lg=2, children=[ ) ] ), - dbc.FormGroup([ + dbc.Row([ dbc.Button("Submit", color="primary", id="submit-form"), dbc.FormText( "After submit be patient it can take some time...", @@ -80,21 +78,21 @@ login_config_layout = dbc.Row(dbc.Col(md=12, lg=2, children=[ ), ])])) -config_otp_layout = dbc.Row(dbc.Col(className="col-md-12 col-lg-2 ml-2", children=[ +config_otp_layout = dbc.Row(dbc.Col(className="col-md-12 col-lg-2", style={"ml": 2}, children=[ html.H2('Config OTP'), dbc.Form([ - dbc.FormGroup([ + dbc.Row([ dbc.Label("Click to receive a code by SMS", html_for="ask-sms"), dbc.Button("Send SMS", color="info", id="ask-sms"), html.Div(id="sms-demand-result", className="mt-2") ]), - dbc.FormGroup( + dbc.Row( [ dbc.Label("Write the code you just received by SMS", html_for="psa-email"), dbc.Input(type="text", id="psa-code", placeholder="Enter code"), ] ), - dbc.FormGroup( + dbc.Row( [ dbc.Label("Enter your code PIN", html_for="psa-pin"), dbc.Input( diff --git a/web/view/control.py b/web/view/control.py index 6386375..b90d69d 100644 --- a/web/view/control.py +++ b/web/view/control.py @@ -1,5 +1,5 @@ import dash_bootstrap_components as dbc -from web.tools.import_dash_html import html +from dash import html from mylogger import logger from web.tools.Button import Button diff --git a/web/view/views.py b/web/view/views.py index ae06b25..6580765 100644 --- a/web/view/views.py +++ b/web/view/views.py @@ -3,6 +3,7 @@ from typing import List from urllib.parse import parse_qs, urlparse import dash_bootstrap_components as dbc +from dash import dcc, html from dash.dependencies import Output, Input, State from dash.exceptions import PreventUpdate from flask import jsonify, request, Response as FlaskResponse @@ -23,8 +24,6 @@ from web.utils import diff_dashtable, dash_date_to_datetime # pylint: disable=invalid-name from web.tools.figurefilter import FigureFilter -from web.tools.import_dash_html import html -from web.tools.import_dash_core import dcc from web.utils import create_card from libs.config import Config from web.view.control import get_control_tabs @@ -42,8 +41,7 @@ def add_header(el): return dbc.Row([dbc.Col(dcc.Link(html.H1('My car info'), href="/", style={"text-decoration": "none"})), dbc.Col(dcc.Link(html.Img(src="assets/images/settings.svg", width="30veh"), href="/config", - className="float-right"))], - className="justify-content-between"), el + className="float-end"))]), el @dash_app.callback(Output('page-content', 'children'), From afd3a4e7816500d8571515493e56a72d9af9bc4b Mon Sep 17 00:00:00 2001 From: Florian Bezannier Date: Sat, 23 Oct 2021 12:44:19 +0200 Subject: [PATCH 04/13] fix test_requirements --- libs/requirements.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/libs/requirements.py b/libs/requirements.py index 9e43fb2..9b3f8f6 100644 --- a/libs/requirements.py +++ b/libs/requirements.py @@ -1,7 +1,9 @@ +import sys + import pkg_resources from pathlib import Path - +from mylogger import logger class TestRequirements: """Test availability of required packages.""" @@ -12,6 +14,13 @@ class TestRequirements: """Test that each required package is available.""" # Ref: https://stackoverflow.com/a/45474387/ requirements = pkg_resources.parse_requirements(self.requirement_path.open()) + missing_requirement = False for requirement in requirements: requirement = str(requirement) - pkg_resources.require(requirement) + try: + pkg_resources.require(requirement) + except pkg_resources.VersionConflict: + logger.error("You need to install or update some dependencies: pip install -U %s", requirement) + missing_requirement=True + if missing_requirement: + sys.exit(10) \ No newline at end of file From ff4aef3a5d76213134b3c6ca2a7e2bd5e0a3b5e1 Mon Sep 17 00:00:00 2001 From: Florian Bezannier Date: Sat, 23 Oct 2021 12:45:33 +0200 Subject: [PATCH 05/13] add docker-compose.yml --- docker-compose.yml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 docker-compose.yml diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..f185c2b --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,9 @@ +version: "2" + +services: + psacc: + image: flobz/psa_car_controller:latest + ports: + - "5000:5000" + volumes: + - ./config:/config From c1130a45cf6b94d6b000e68f616a4cbca163ddd5 Mon Sep 17 00:00:00 2001 From: Florian Bezannier Date: Sat, 23 Oct 2021 14:27:48 +0200 Subject: [PATCH 06/13] fix path --- server.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server.py b/server.py index b2f4585..d7b9602 100755 --- a/server.py +++ b/server.py @@ -1,14 +1,16 @@ #!/usr/bin/env python3 # pylint: disable=wrong-import-position +import os import sys from threading import Thread from libs.requirements import TestRequirements +DIR = os.path.dirname(os.path.realpath(__file__)) if sys.version_info < (3, 6): raise RuntimeError("This application requires Python 3.6+") -TestRequirements("requirements.txt").test_requirements() +TestRequirements(DIR + "/requirements.txt").test_requirements() import web.app from libs.config import Config From b39dd4a6a0bab1aa3759282bab5039e169c51a54 Mon Sep 17 00:00:00 2001 From: Florian Bezannier Date: Sat, 23 Oct 2021 14:44:40 +0200 Subject: [PATCH 07/13] fix format --- libs/requirements.py | 2 +- web/figures.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/requirements.py b/libs/requirements.py index 9b3f8f6..7ae542f 100644 --- a/libs/requirements.py +++ b/libs/requirements.py @@ -20,7 +20,7 @@ class TestRequirements: try: pkg_resources.require(requirement) except pkg_resources.VersionConflict: - logger.error("You need to install or update some dependencies: pip install -U %s", requirement) + logger.error("You need to install or update some dependencies: pip3 install -U %s", requirement) missing_requirement=True if missing_requirement: sys.exit(10) \ No newline at end of file diff --git a/web/figures.py b/web/figures.py index 923837f..ccf2f41 100644 --- a/web/figures.py +++ b/web/figures.py @@ -3,10 +3,10 @@ from statistics import mean import dash_bootstrap_components as dbc from dash import html -from dash.dash_table import Format, DataTable +from dash.dash_table import DataTable import plotly.express as px import plotly.graph_objects as go -from dash.dash_table.Format import Scheme, Symbol +from dash.dash_table.Format import Scheme, Symbol, Format from dash.dcc import Graph from libs.car import Car From 9d64c71cd6838686ff316890d0a837cada97c95a Mon Sep 17 00:00:00 2001 From: Florian Bezannier Date: Sat, 23 Oct 2021 15:22:42 +0200 Subject: [PATCH 08/13] fix style --- web/tools/Button.py | 3 +- web/view/config_views.py | 118 +++++++++++++++++---------------------- 2 files changed, 51 insertions(+), 70 deletions(-) diff --git a/web/tools/Button.py b/web/tools/Button.py index c8d9bcd..0dc0147 100644 --- a/web/tools/Button.py +++ b/web/tools/Button.py @@ -1,6 +1,5 @@ -import html - import dash_bootstrap_components as dbc +from dash import html from dash._utils import create_callback_id from dash.dependencies import Output, Input diff --git a/web/view/config_views.py b/web/view/config_views.py index b81dc91..fa0742f 100644 --- a/web/view/config_views.py +++ b/web/view/config_views.py @@ -11,33 +11,30 @@ import dash_bootstrap_components as dbc from dash.dependencies import Output, Input, State config = Config() -login_config_layout = dbc.Row(dbc.Col(md=12, lg=2, style={"ml": 2}, children=[ - html.H2('Config'), - dbc.Form([ - dbc.Row([ - dbc.Label("Car Brand", html_for="psa-app"), - dcc.Dropdown( - id="psa-app", - options=[ - {"label": "Peugeot", "value": "com.psa.mym.mypeugeot"}, - {"label": "Opel", "value": "com.psa.mym.myopel"}, - {"label": "Citroën", "value": "com.psa.mym.mycitroen"}, - {"label": "DS", "value": "com.psa.mym.myds"}, - {"label": "Vauxhall", "value": "com.psa.mym.myvauxhall"} - ], - )]), - dbc.Row( - [ +login_config_layout = dbc.Row(dbc.Col(md=12, lg=2, className="m-3", children=[ + dbc.Row(html.H2('Config')), + dbc.Row(className="ms-2", children=[ + dbc.Form([ + html.Div([ + dbc.Label("Car Brand", html_for="psa-app"), + dcc.Dropdown( + id="psa-app", + options=[ + {"label": "Peugeot", "value": "com.psa.mym.mypeugeot"}, + {"label": "Opel", "value": "com.psa.mym.myopel"}, + {"label": "Citroën", "value": "com.psa.mym.mycitroen"}, + {"label": "DS", "value": "com.psa.mym.myds"}, + {"label": "Vauxhall", "value": "com.psa.mym.myvauxhall"} + ], + )]), + html.Div([ dbc.Label("Email", html_for="psa-email"), dbc.Input(type="email", id="psa-email", placeholder="Enter email"), dbc.FormText( "PSA account email", color="secondary", - ), - ] - ), - dbc.Row( - [ + )]), + html.Div([ dbc.Label("Password", html_for="psa-password"), dbc.Input( type="password", @@ -47,11 +44,8 @@ login_config_layout = dbc.Row(dbc.Col(md=12, lg=2, style={"ml": 2}, children=[ dbc.FormText( "PSA account password", color="secondary", - ), - ] - ), - dbc.Row( - [ + )]), + html.Div([ dbc.Label("Country code", html_for="countrycode"), dbc.Input( type="text", @@ -61,60 +55,48 @@ login_config_layout = dbc.Row(dbc.Col(md=12, lg=2, style={"ml": 2}, children=[ dbc.FormText( "Example: FR for FRANCE or GB for Great Britain...", color="secondary", - ) - ] - ), - dbc.Row([ - dbc.Button("Submit", color="primary", id="submit-form"), - dbc.FormText( - "After submit be patient it can take some time...", - color="secondary", - ), + )]), + dbc.Row(dbc.Button("Submit", color="primary", id="submit-form")), + dbc.Row( + dbc.FormText( + "After submit be patient it can take some time...", + color="secondary")), dcc.Loading( id="loading-2", children=[html.Div([html.Div(id="form_result")])], type="circle", - )] - ), + ), + ]) ])])) -config_otp_layout = dbc.Row(dbc.Col(className="col-md-12 col-lg-2", style={"ml": 2}, children=[ - html.H2('Config OTP'), - dbc.Form([ - dbc.Row([ - dbc.Label("Click to receive a code by SMS", html_for="ask-sms"), - dbc.Button("Send SMS", color="info", id="ask-sms"), - html.Div(id="sms-demand-result", className="mt-2") - ]), - dbc.Row( - [ - dbc.Label("Write the code you just received by SMS", html_for="psa-email"), - dbc.Input(type="text", id="psa-code", placeholder="Enter code"), - ] +config_otp_layout = dbc.Row(dbc.Col(className="col-md-12 col-lg-2 m-3", children=[ + dbc.Row(html.H2('Config OTP')), + dbc.Form(className="ms-2", children=[ + dbc.Label("Click to receive a code by SMS", html_for="ask-sms"), + dbc.Button("Send SMS", color="info", id="ask-sms"), + html.Div(id="sms-demand-result", className="mt-2"), + dbc.Label("Write the code you just received by SMS", html_for="psa-email"), + dbc.Input(type="text", id="psa-code", placeholder="Enter code"), + dbc.Label("Enter your PIN code", html_for="psa-pin"), + dbc.Input( + type="password", + id="psa-pin", + placeholder="Enter codepin", ), - dbc.Row( - [ - dbc.Label("Enter your code PIN", html_for="psa-pin"), - dbc.Input( - type="password", - id="psa-pin", - placeholder="Enter codepin", - ), - dbc.FormText( - "It's a digit password", - color="secondary", - ), - dbc.Button("Submit", color="primary", id="finish-otp") - ] + dbc.FormText( + "It's a digit password", + color="secondary", ), - html.Div(id="opt-result") + html.Div([ + dbc.Button("Submit", color="primary", id="finish-otp"), + html.Div(id="opt-result")]), ])])) def log_layout(): with open(LOG_FILE, "r") as f: log_text = f.read() - return html.H3(children=["Log:", dbc.Container( + return html.H3(className="m-2", children=["Log:", dbc.Container( fluid=True, style={"height": "80vh", "overflow": "auto", @@ -123,7 +105,7 @@ def log_layout(): "white-space": "pre-line"}, children=log_text, className="m-3 bg-light h5"), - html.Div(id="empty-div")]) + html.Div(id="empty-div")]) config_layout = dbc.Tabs([ From 1ef90283a1113351f7a9809dffe80321b5b90607 Mon Sep 17 00:00:00 2001 From: Florian Bezannier Date: Sat, 23 Oct 2021 15:27:16 +0200 Subject: [PATCH 09/13] add docker-compose --- docs/Docker.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/Docker.md b/docs/Docker.md index 4006fee..18af3ce 100644 --- a/docs/Docker.md +++ b/docs/Docker.md @@ -1,14 +1,19 @@ # Docker installation -A containerised version of the psa_car_controller Python scripts by Flobz. +A containerised version of the psa_car_controller. - Docker Hub: https://hub.docker.com/r/flobz/psa_car_controller ### Overview -Once the container is running, the configuration of the psa_car_controller app is near-identical to setup instructions detailed in the app's readme. +Once the container is running, the configuration of the psa_car_controller app is near-identical classic Linux/Windows installtion. ### Installation -Create the container, detached, exposing port 5000, and mapping the a new config folder on your host to /config inside the container: +Create the container, detached, exposing port 5000, and mapping config folder on your host to /config inside the container: +#### With docker-compose +``` +docker-compose up -d +``` +#### With Docker: ``` docker run -d -ti --name psa_car_controller1 \ --publish 5000:5000 \ From cb88f5a4d9e58abc95f270cdb8999479913614b4 Mon Sep 17 00:00:00 2001 From: Florian Bezannier Date: Sat, 23 Oct 2021 15:29:19 +0200 Subject: [PATCH 10/13] add 2008 II --- libs/car_model.py | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/car_model.py b/libs/car_model.py index 5a29c1d..7474de0 100644 --- a/libs/car_model.py +++ b/libs/car_model.py @@ -64,6 +64,7 @@ car_models = [ CarModel("308", 0, 56, reg=r"VF3L35GG.*"), CarModel("208", 0, 44, reg=r"VR3UPHN[SE].*"), # VR3UPHNSSM VR3UPHNEKM CarModel("2008", 0, 44, reg=r"VR3USHNS.*"), # VR3USHNSKM + CarModel("2008 II", 0, 45, reg=r"VR3USHNK.*"), # VR3USHNKKL CarModel("SUV 5008 II", 0, 56, reg=r"VF3MRHNS.*"), # vf3mrhnsum CarModel("SUV 5008 II 2018", 0, 56, reg=r"VF3MRHNY.*"), # VF3MRHNYHH CarModel("C5 Aircross Hybrid", 13.2, 43, reg=r"VR7A4DGZ.*"), # VR7A4DGZSM From c6d41a713f9e970209ff8868f871a0937c49ed99 Mon Sep 17 00:00:00 2001 From: Florian Bezannier Date: Sat, 23 Oct 2021 17:54:16 +0200 Subject: [PATCH 11/13] update config layout --- libs/requirements.py | 3 ++- web/view/config_views.py | 9 +++++---- web/view/views.py | 8 ++++---- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/libs/requirements.py b/libs/requirements.py index 7ae542f..2a0d7f4 100644 --- a/libs/requirements.py +++ b/libs/requirements.py @@ -1,4 +1,5 @@ import sys +import traceback import pkg_resources from pathlib import Path @@ -12,7 +13,6 @@ class TestRequirements: def test_requirements(self): """Test that each required package is available.""" - # Ref: https://stackoverflow.com/a/45474387/ requirements = pkg_resources.parse_requirements(self.requirement_path.open()) missing_requirement = False for requirement in requirements: @@ -20,6 +20,7 @@ class TestRequirements: try: pkg_resources.require(requirement) except pkg_resources.VersionConflict: + logger.debug(traceback.format_exc()) logger.error("You need to install or update some dependencies: pip3 install -U %s", requirement) missing_requirement=True if missing_requirement: diff --git a/web/view/config_views.py b/web/view/config_views.py index fa0742f..7d6872c 100644 --- a/web/view/config_views.py +++ b/web/view/config_views.py @@ -108,10 +108,11 @@ def log_layout(): html.Div(id="empty-div")]) -config_layout = dbc.Tabs([ - dbc.Tab([log_layout()], label="Log"), - dbc.Tab([login_config_layout], label="User config"), - dbc.Tab([config_otp_layout], label="OTP config")]) +def config_layout(activeTabs="log"): + return dbc.Tabs(active_tab=activeTabs, children=[ + dbc.Tab([log_layout()], label="Log", tab_id="log"), + dbc.Tab([login_config_layout], label="User config", tab_id="login"), + dbc.Tab([config_otp_layout], label="OTP config", tab_id="otp")]) @dash_app.callback( diff --git a/web/view/views.py b/web/view/views.py index 6580765..1773738 100644 --- a/web/view/views.py +++ b/web/view/views.py @@ -19,7 +19,7 @@ from web import figures from web.app import app, dash_app from web.db import Database -from web.view.config_views import login_config_layout, config_otp_layout, log_layout, config_layout +from web.view.config_views import log_layout, config_layout from web.utils import diff_dashtable, dash_date_to_datetime # pylint: disable=invalid-name @@ -52,15 +52,15 @@ def display_page(pathname, search): query_params = parse_qs(urlparse(search).query) no_header = query_params.get("header", None) == ["false"] if pathname == "/config": - page = config_layout + page = config_layout() elif pathname == "/config_login": - page = login_config_layout + page = config_layout("login") elif pathname == "/log": page = log_layout() elif not CONFIG.is_good: page = dcc.Location(pathname=dash_app.requests_pathname_external_prefix + "config_login", id="config_redirect") elif pathname == "/config_otp": - page = config_otp_layout + page = config_layout("otp") elif pathname == "/control": page = get_control_tabs(CONFIG) else: From 017959581cf1ddeb932541736a81941eb9449eee Mon Sep 17 00:00:00 2001 From: Florian Bezannier Date: Sat, 23 Oct 2021 18:05:39 +0200 Subject: [PATCH 12/13] update requirements --- libs/requirements.py | 12 ++++++------ requirements.txt | 5 +++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/libs/requirements.py b/libs/requirements.py index 2a0d7f4..b7e64cf 100644 --- a/libs/requirements.py +++ b/libs/requirements.py @@ -1,10 +1,11 @@ import sys -import traceback import pkg_resources from pathlib import Path from mylogger import logger + + class TestRequirements: """Test availability of required packages.""" @@ -19,9 +20,8 @@ class TestRequirements: requirement = str(requirement) try: pkg_resources.require(requirement) - except pkg_resources.VersionConflict: - logger.debug(traceback.format_exc()) - logger.error("You need to install or update some dependencies: pip3 install -U %s", requirement) - missing_requirement=True + except (pkg_resources.VersionConflict, pkg_resources.DistributionNotFound) as ex: + logger.error("%s\nYou need to install or update %s: pip3 install -U %s", ex, requirement, requirement) + missing_requirement = True if missing_requirement: - sys.exit(10) \ No newline at end of file + sys.exit(10) diff --git a/requirements.txt b/requirements.txt index 5b1b714..da9c649 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ paho-mqtt>=1.5.0 dash>=2 dash_daq -plotly>=4 +plotly>=5 cryptography>=2.6 Werkzeug>=1.0.0 pandas @@ -21,4 +21,5 @@ pycryptodomex certifi >= 14.05.14 six >= 1.10 python_dateutil >= 2.5.3 -urllib3 >= 1.15.1 \ No newline at end of file +urllib3 >= 1.15.1 + From 31d9a0a5eae499376e14d8aba24d8da2c3888e29 Mon Sep 17 00:00:00 2001 From: Florian Bezannier Date: Sat, 23 Oct 2021 20:51:25 +0200 Subject: [PATCH 13/13] update docker images --- Dockerfile | 6 +++--- requirements.txt | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 71bad7f..a158a02 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ -ARG PYTHON_DEP='python3 python3-wheel python3-typing-extensions python3-pandas python3-plotly python3-six python3-dateutil python3-brotli python3-pycryptodome libatlas3-base python3-cryptography python3-scipy androguard' +ARG PYTHON_DEP='python3 python3-wheel python3-typing-extensions python3-pandas python3-six python3-dateutil python3-brotli python3-pycryptodome libatlas3-base python3-cryptography python3-scipy androguard python3-flask' -FROM debian:buster-slim AS builder +FROM debian:bullseye-slim AS builder ARG PYTHON_DEP RUN BUILD_DEP='python3-pip python3-setuptools python3-dev libblas-dev liblapack-dev gfortran libatlas3-base' ; \ apt-get update && apt-get install -y --no-install-recommends $BUILD_DEP $PYTHON_DEP; @@ -8,7 +8,7 @@ COPY . /psa_car_controller/ RUN pip3 install --system --no-cache-dir -r /psa_car_controller/requirements.txt EXPOSE 5000 -FROM debian:buster-slim +FROM debian:bullseye-slim ARG PYTHON_DEP WORKDIR /config ENV PSACC_BASE_PATH=/ PSACC_PORT=5000 PSACC_OPTIONS="-c -r --web-conf" PSACC_CONFIG_DIR="/config" diff --git a/requirements.txt b/requirements.txt index da9c649..17b3de9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,14 +4,14 @@ dash_daq plotly>=5 cryptography>=2.6 Werkzeug>=1.0.0 +flask>=1.0.4 +dash_bootstrap_components>=1 pandas oauth2_client requests pytz typing argparse -flask -dash_bootstrap_components>=1 geojson reverse_geocode androguard