From 1d6847604cc030c7e180ca1f030c8888cb9cc9e2 Mon Sep 17 00:00:00 2001 From: Florian Bezannier Date: Sun, 7 Jan 2024 20:11:51 +0100 Subject: [PATCH] refacto: move flask api to a new file --- psa_car_controller/web/view/api.py | 150 +++++++++++++++++++++++++++ psa_car_controller/web/view/views.py | 149 +------------------------- 2 files changed, 154 insertions(+), 145 deletions(-) create mode 100644 psa_car_controller/web/view/api.py diff --git a/psa_car_controller/web/view/api.py b/psa_car_controller/web/view/api.py new file mode 100644 index 0000000..ba48cb3 --- /dev/null +++ b/psa_car_controller/web/view/api.py @@ -0,0 +1,150 @@ +from flask import jsonify, request, Response as FlaskResponse +from pydantic import BaseModel + +from psa_car_controller.common.utils import RateLimitException +from psa_car_controller.psacc.application.car_controller import PSACarController +from psa_car_controller.web.app import app + +import json +STYLE_CACHE = None +APP = PSACarController() + +@app.route('/get_vehicles') +def get_vehicules(): + response = app.response_class( + response=json.dumps(APP.myp.get_vehicles(), default=lambda car: car.to_dict()), + status=200, + mimetype='application/json' + ) + return response + + +@app.route('/get_vehicleinfo/') +def get_vehicle_info(vin): + from_cache = int(request.args.get('from_cache', 0)) == 1 + response = app.response_class( + response=json.dumps(APP.myp.get_vehicle_info(vin, from_cache).to_dict(), default=str), + status=200, + mimetype='application/json' + ) + return response + +@app.route("/style.json") +def get_style(): + global STYLE_CACHE + if not STYLE_CACHE: + with open(app.root_path + "/assets/style.json", "r", encoding="utf-8") as f: + res = json.loads(f.read()) + STYLE_CACHE = res + url_root = request.url_root + STYLE_CACHE["sprite"] = url_root + "assets/sprites/osm-liberty" + return jsonify(STYLE_CACHE) + + +@app.route('/charge_now//') +def charge_now(vin, charge): + return jsonify(APP.myp.remote_client.charge_now(vin, charge != 0)) + + +@app.route('/charge_hour') +def change_charge_hour(): + return jsonify(APP.myp.remote_client.change_charge_hour(request.args['vin'], + request.args['hour'], + request.args['minute'])) + + +@app.route('/wakeup/') +def wakeup(vin): + try: + return jsonify(APP.myp.remote_client.wakeup(vin)) + except RateLimitException: + return jsonify({"error": "Wakeup rate limit exceeded"}) + + +@app.route('/preconditioning//') +def preconditioning(vin, activate): + return jsonify(APP.myp.remote_client.preconditioning(vin, activate)) + + +@app.route('/position/') +def get_position(vin): + res = APP.myp.get_vehicle_info(vin) + try: + coordinates = res.last_position.geometry.coordinates + except AttributeError: + return jsonify({'error': 'last_position not available from api'}) + longitude, latitude = coordinates[:2] + if len(coordinates) == 3: # altitude is not always available + altitude = coordinates[2] + else: + altitude = None + return jsonify( + {"longitude": longitude, "latitude": latitude, "altitude": altitude, + "url": f"https://maps.google.com/maps?q={latitude},{longitude}"}) + + +# Set a battery threshold and schedule an hour to stop the charge +@app.route('/charge_control') +def get_charge_control(): + logger.info(request) + vin = request.args['vin'] + charge_control = APP.chc.get(vin) + if charge_control is None: + return jsonify("error: VIN not in list") + if 'hour' in request.args and 'minute' in request.args: + charge_control.set_stop_hour([int(request.args["hour"]), int(request.args["minute"])]) + if 'percentage' in request.args: + charge_control.percentage_threshold = int(request.args['percentage']) + APP.chc.save_config() + return jsonify(charge_control.get_dict()) + + +@app.route('/positions') +def get_recorded_position(): + return FlaskResponse(Database.get_recorded_position(), mimetype='application/json') + + +@app.route('/abrp') +def abrp(): + vin = request.args.get('vin', None) + enable = request.args.get('enable', None) + token = request.args.get('token', None) + if vin is not None and enable is not None: + if enable == '1': + APP.myp.abrp.abrp_enable_vin.add(vin) + else: + APP.myp.abrp.abrp_enable_vin.discard(vin) + if token is not None: + APP.myp.abrp.token = token + return jsonify(dict(APP.myp.abrp)) + + +@app.after_request +def after_request(response): + header = response.headers + header['Access-Control-Allow-Origin'] = '*' + return response + + +@app.route('/horn//') +def horn(vin, count): + try: + return jsonify(APP.myp.remote_client.horn(vin, count)) + except RateLimitException: + return jsonify({"error": "Horn rate limit exceeded"}) + + +@app.route('/lights//') +def lights(vin, duration): + try: + return jsonify(APP.myp.remote_client.lights(vin, duration)) + except RateLimitException: + return jsonify({"error": "Lights rate limit exceeded"}) + + +@app.route('/lock_door//') +def lock_door(vin, lock): + try: + return jsonify(APP.myp.remote_client.lock_door(vin, lock)) + except RateLimitException: + return jsonify({"error": "Locks rate limit exceeded"}) diff --git a/psa_car_controller/web/view/views.py b/psa_car_controller/web/view/views.py index 07bf0ec..6f50e60 100644 --- a/psa_car_controller/web/view/views.py +++ b/psa_car_controller/web/view/views.py @@ -1,4 +1,3 @@ -import json from datetime import datetime from typing import List from urllib.parse import parse_qs, urlparse @@ -7,13 +6,12 @@ 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 import time +from flask import request from psa_car_controller.common.mylogger import CustomLogger from psa_car_controller.psacc.application.car_controller import PSACarController from psa_car_controller.psacc.model.car import Cars, Car -from psa_car_controller.common.utils import RateLimitException from psa_car_controller.psacc.model.charge import Charge from psa_car_controller.psacc.repository.trips import Trips @@ -21,7 +19,7 @@ from psa_car_controller.psacc.repository.trips import Trips from psa_car_controller.psacc.application.charging import Charging from psa_car_controller.web import figures -from psa_car_controller.web.app import app, dash_app +from psa_car_controller.web.app import dash_app from psa_car_controller.psacc.repository.db import Database from psa_car_controller.web.tools.utils import diff_dashtable, unix_time_millis, get_marks_from_start_end, create_card from psa_car_controller.web.view.config_views import log_layout, config_layout @@ -31,6 +29,8 @@ from psa_car_controller.web.tools.figurefilter import FigureFilter from psa_car_controller.web.view.control import get_control_tabs from psa_car_controller import __version__ +from psa_car_controller.web.view import api # pylint: disable=unused-import + logger = CustomLogger.getLogger(__name__) EMPTY_DIV = "empty-div" @@ -174,147 +174,6 @@ def create_callback(): # noqa: MC0001 CALLBACK_CREATED = True -@app.route('/get_vehicles') -def get_vehicules(): - response = app.response_class( - response=json.dumps(APP.myp.get_vehicles(), default=lambda car: car.to_dict()), - status=200, - mimetype='application/json' - ) - return response - - -@app.route('/get_vehicleinfo/') -def get_vehicle_info(vin): - from_cache = int(request.args.get('from_cache', 0)) == 1 - response = app.response_class( - response=json.dumps(APP.myp.get_vehicle_info(vin, from_cache).to_dict(), default=str), - status=200, - mimetype='application/json' - ) - return response - - -STYLE_CACHE = None - - -@app.route("/style.json") -def get_style(): - global STYLE_CACHE - if not STYLE_CACHE: - with open(app.root_path + "/assets/style.json", "r", encoding="utf-8") as f: - res = json.loads(f.read()) - STYLE_CACHE = res - url_root = request.url_root - STYLE_CACHE["sprite"] = url_root + "assets/sprites/osm-liberty" - return jsonify(STYLE_CACHE) - - -@app.route('/charge_now//') -def charge_now(vin, charge): - return jsonify(APP.myp.remote_client.charge_now(vin, charge != 0)) - - -@app.route('/charge_hour') -def change_charge_hour(): - return jsonify(APP.myp.remote_client.change_charge_hour(request.args['vin'], - request.args['hour'], - request.args['minute'])) - - -@app.route('/wakeup/') -def wakeup(vin): - try: - return jsonify(APP.myp.remote_client.wakeup(vin)) - except RateLimitException: - return jsonify({"error": "Wakeup rate limit exceeded"}) - - -@app.route('/preconditioning//') -def preconditioning(vin, activate): - return jsonify(APP.myp.remote_client.preconditioning(vin, activate)) - - -@app.route('/position/') -def get_position(vin): - res = APP.myp.get_vehicle_info(vin) - try: - coordinates = res.last_position.geometry.coordinates - except AttributeError: - return jsonify({'error': 'last_position not available from api'}) - longitude, latitude = coordinates[:2] - if len(coordinates) == 3: # altitude is not always available - altitude = coordinates[2] - else: - altitude = None - return jsonify( - {"longitude": longitude, "latitude": latitude, "altitude": altitude, - "url": f"https://maps.google.com/maps?q={latitude},{longitude}"}) - - -# Set a battery threshold and schedule an hour to stop the charge -@app.route('/charge_control') -def get_charge_control(): - logger.info(request) - vin = request.args['vin'] - charge_control = APP.chc.get(vin) - if charge_control is None: - return jsonify("error: VIN not in list") - if 'hour' in request.args and 'minute' in request.args: - charge_control.set_stop_hour([int(request.args["hour"]), int(request.args["minute"])]) - if 'percentage' in request.args: - charge_control.percentage_threshold = int(request.args['percentage']) - APP.chc.save_config() - return jsonify(charge_control.get_dict()) - - -@app.route('/positions') -def get_recorded_position(): - return FlaskResponse(Database.get_recorded_position(), mimetype='application/json') - - -@app.route('/abrp') -def abrp(): - vin = request.args.get('vin', None) - enable = request.args.get('enable', None) - token = request.args.get('token', None) - if vin is not None and enable is not None: - if enable == '1': - APP.myp.abrp.abrp_enable_vin.add(vin) - else: - APP.myp.abrp.abrp_enable_vin.discard(vin) - if token is not None: - APP.myp.abrp.token = token - return jsonify(dict(APP.myp.abrp)) - - -@app.after_request -def after_request(response): - header = response.headers - header['Access-Control-Allow-Origin'] = '*' - return response - -@app.route('/horn//') -def horn(vin, count): - try: - return jsonify(APP.myp.remote_client.horn(vin, count)) - except RateLimitException: - return jsonify({"error": "Horn rate limit exceeded"}) - -@app.route('/lights//') -def lights(vin, duration): - try: - return jsonify(APP.myp.remote_client.lights(vin, duration)) - except RateLimitException: - return jsonify({"error": "Lights rate limit exceeded"}) - -@app.route('/lock_door//') -def lock_door(vin, lock): - try: - return jsonify(APP.myp.remote_client.lock_door(vin, lock)) - except RateLimitException: - return jsonify({"error": "Locks rate limit exceeded"}) - def update_trips(): global trips, chargings, cached_layout, min_date, max_date, min_millis, max_millis, step, marks logger.info("update_data")