From 68571cb888552e79858330017c2deebc828b88a0 Mon Sep 17 00:00:00 2001 From: Christian Date: Fri, 2 Jun 2023 14:05:52 +0200 Subject: [PATCH] fix: python decorator --- README.md | 113 +++++++++++++++++++++++++ backend/Dockerfile | 4 - backend/README.md | 37 ++++---- backend/api/v1/helpers.py | 18 ++-- backend/api/v1/views/batteries.py | 9 ++ backend/api/v1/views/drivers.py | 19 +++-- backend/api/v1/views/movements.py | 5 ++ backend/api/v1/views/stations.py | 14 ++- backend/api/v1/views/swaps.py | 31 +++++-- backend/app.py | 13 +-- frontend/README.md | 7 +- frontend/src/pages/Login/LoginPage.jsx | 2 +- 12 files changed, 212 insertions(+), 60 deletions(-) diff --git a/README.md b/README.md index e69de29..013abdb 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,113 @@ +## Design and implement a system that will track the assets in motion. Battery Swap, telematics onboard. +### How would you organize the data? What technologies would you use? Where would you see key risks? + +To track assets in motion, including battery swaps and telematics onboard, the data can be organized as follows: + +1. **Asset in motion Data**: This includes information about each asset, such as asset ID, type, current location, status, and associated battery details. +2. **Battery Data**: This includes battery information, such as battery ID, current charge level, health status, and history of swaps. +3. **Driver Data**: This includes driver details, such as driver ID, name, contact information, and driving history. +4. **Station Data**: This includes information about the battery swap stations, including station ID, location, available batteries, and swap history. + +Technologies that was used to implement this system include: +- Backend Framework: Flask (Python) for developing the RESTful API and handling request/response logic. +- Relational Database: MySQL for storing and managing asset in motion, battery data, driver, and station data. +- frontend framework: ReactJs is a popular framework used to build modern SPAs + +Key Risks: +- **Data Accuracy and Consistency**: Ensuring accurate and consistent real-time tracking data is crucial to maintain the reliability of the system. +- **Data Security**: Safeguarding sensitive asset information and implementing secure communication between devices, backend, and frontend components. +- **Scalability and Performance**: As the number of assets and data volume increases, the system should handle scalability and performance requirements effectively. + +## How would you calculate the distance travelled by each driver? Total energy consumed by each driver. Total energy theoretically consumed by each driver. + +To calculate the distance travelled by each driver and the total energy consumed by each driver, the following approach can be taken: + +1. **Distance Calculation**: +- Utilize the GPS coordinates captured by the telematics devices on an asset. +- Apply distance calculation algorithms to calculate the distance between each set of consecutive GPS coordinates. +- Sum up the distances to calculate the total distance travelled by each driver. + +2. **Energy Consumption Calculation**: +- Leverage the battery data and telematics data. +- Calculate the energy consumption based on factors such as vehicle speed, acceleration, and deceleration. +- Use energy consumption models specific to the vehicle type and battery characteristics. +- Sum up the energy consumption values to calculate the total energy consumed by each driver. + +3. **Theoretical Energy Consumption Calculation**: +- Determine the theoretical energy consumption for each driver based on various factors, such as motocycle type, battery capacity, distance travelled, and energy efficiency. + + +## What's a good way to predict and optimize for how many batteries should be at a given station? + +To predict and optimize the number of batteries at a given station, the following approach can be considered: + +- Analyze historical data on battery swaps, station usage, and demands. +- Use statistical analysis techniques to identify peak demand periods and popular swap times +- Consider factors such as time of day, day of the week, seasonality, and any other relevant variables that affect battery demand. +- Incorporate external factors like events, holidays, and special occasions that may impact battery usage. + +## Project Setup + +To run the project using Docker, follow these steps: + +### Prerequisites + +Make sure you have the following installed on your system: +- Docker: [Download and install Docker](https://www.docker.com/get-started) + +### 1. Clone the Repository + +Clone the project repository from GitHub: + +```bash +git clone https://github.com/Crispy-rw/energize-swap +``` + +### 2. Build the Docker Images + +Navigate to the project's root directory: + +```bash +cd energize-swap +``` + +Build the Docker images for the backend and frontend services: + +```bash +docker-compose build +``` + +### 3. Start the Containers + +Start the Docker containers using Docker Compose: + +```bash +docker-compose up +``` + +This will start the backend, frontend, and database services defined in the `docker-compose.yml` file. + +### 4. Access the Application + +Once the containers are up and running, you can access the application in your web browser: + +- Backend API: `http://localhost:5000/` +- Frontend: `http://localhost:5173/` + +### 5. Stop the Containers + +To stop the Docker containers, press `Ctrl + C` in the terminal where the containers are running. + +Alternatively, you can run the following command in the project's root directory: + +```bash +docker-compose down +``` + +This will stop and remove the containers, networks, and volumes created by Docker Compose. + +That's it! You now have the project running using Docker. + +Note: Make sure to customize the Docker Compose configuration (`docker-compose.yml`) according to your project's needs, such as environment variables, volumes, or additional services. + +Please let me know if you need further assistance! \ No newline at end of file diff --git a/backend/Dockerfile b/backend/Dockerfile index 3f51f33..d022b87 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -24,8 +24,4 @@ RUN pip install --upgrade pip && pip install -r requirements.txt COPY . /app -ADD entrypoint.sh /entrypoint.sh - -RUN ["chmod", "+x", "/entrypoint.sh"] - CMD /wait && flask db reset && flask run --host=0.0.0.0 \ No newline at end of file diff --git a/backend/README.md b/backend/README.md index a844ad7..f243e1c 100644 --- a/backend/README.md +++ b/backend/README.md @@ -1,6 +1,3 @@ ---- -title: Energize Swap Backend API ---- # Energize Swap Backend API @@ -34,22 +31,22 @@ The API will be accessible at `http://localhost:5000`. ## API ENDPOINTS -| Ressource URL | Methods | Description | Authentication required | -| -------------------------------- | ------- | ----------------------------------------------- | ----------------------- | -| /api/v1/batteries | GET | Get a list of all batteries | Yes | -| /api/v1/batteries/addbattery | POST | Create a new battery | Yes | -| /api/v1/drivers | GET | GET a list of drivers | Yes | -| /api/v1/drivers/addriver | POST | Create a new driver | Yes | -| /api/v1/stations | GET | Get a list of stations | No | -| /api/v1/stations/addstation | POST | Create a new Station | Yes | -| /api/v1/stations/batteries | GET | Get all batteries of a specific stations | Yes | -| /api/v1/swaps | GET | Get a list of all battery exchange | Yes | -| /api/v1/swaps/addswap | POST | Create a new battery exchange | Yes | -| /api/v1/swaps/stopswap/ | PUT | Modify/Edit a specific swap to finish it | Yes | -| /api/v1/swaps/totalswappedbattery| GET | Get a list of total battery exchanged | Yes | -| /api/v1/swaps/ | GET | Get information of one battery exchange | Yes | -| /api/v1/movements/| POST | Update the movement of an active battery | Yes | -| /api/v1/login | POST | Authentication | No | +| Ressource URL | Methods | Description | Authentication required | Role | +| -------------------------------- | ------- | ----------------------------------------------- | ----------------------- | ---------------| +| /api/v1/batteries | GET | Get a list of all batteries | Yes | Admin | +| /api/v1/batteries/addbattery | POST | Create a new battery | Yes | Admin | +| /api/v1/drivers | GET | GET a list of drivers | Yes | Admin | +| /api/v1/drivers/addriver | POST | Create a new driver | Yes | Admin | +| /api/v1/stations | GET | Get a list of stations | No | Admin | +| /api/v1/stations/addstation | POST | Create a new Station | Yes | Admin | +| /api/v1/stations/batteries | GET | Get all batteries of a specific stations | Yes | Manager | +| /api/v1/swaps | GET | Get a list of all battery exchange | Yes | Admin, Manager | +| /api/v1/swaps/addswap | POST | Create a new battery exchange | Yes | Manager | +| /api/v1/swaps/stopswap/ | PUT | Modify/Edit a specific swap to finish it | Yes | Manager | +| /api/v1/swaps/totalswappedbattery| GET | Get a list of total battery exchanged | Yes | Manager, Admin | +| /api/v1/swaps/ | GET | Get information of one battery exchange | Yes | Manager | +| /api/v1/movements/| POST | Update the movement of an active battery | Yes | Driver | +| /api/v1/login | POST | Authentication | No | | ## Deployment @@ -75,4 +72,4 @@ The Energize Swap Backend API is released under the [MIT License](https://openso This project was developed as part of the Energize Swap initiative to promote electric vehicle adoption and provide efficient battery swapping services. We would like to thank all the contributors and supporters who have made this project possible. -For any inquiries or further information, please contact [Project Team Email]. \ No newline at end of file +For any inquiries or further information, please contact [nshimyumukizachristian@gmail.com]. \ No newline at end of file diff --git a/backend/api/v1/helpers.py b/backend/api/v1/helpers.py index 38827b6..1f3721e 100644 --- a/backend/api/v1/helpers.py +++ b/backend/api/v1/helpers.py @@ -8,11 +8,15 @@ # Assuming RADIUS is a global constant def toRad(degrees): - # Converts degrees to radians + ''' + Converts degrees to radians + ''' return degrees * math.pi / 180 def getDistance(from_, to): - # Calculates the distance between two points on a sphere + ''' + Calculates the distance between two points on a sphere + ''' fromLat = from_[0] fromLon = from_[1] toLat = to[0] @@ -29,7 +33,9 @@ def getDistance(from_, to): return RADIUS * c def measurePath(points): - # Measures the total distance of a path given by a list of points + ''' + Measures the total distance of a path given by a list of points + ''' lastPoint = None distance = 0 for point in points: @@ -41,15 +47,15 @@ def measurePath(points): def token_info(token): ''' - Check token if token is valid this returns ID aapended to it + Check token if token is valid this returns infor apended to it ''' try: claims = jwt.decode(token.split(' ')[1], 'test') claims.validate() - except ExpiredTokenError as e: + except ExpiredTokenError: return False - except Exception as e: # noqa: E722 + except Exception: # noqa: E722 return False return claims diff --git a/backend/api/v1/views/batteries.py b/backend/api/v1/views/batteries.py index 0c52e56..2361336 100644 --- a/backend/api/v1/views/batteries.py +++ b/backend/api/v1/views/batteries.py @@ -2,10 +2,15 @@ from api.v1.views import app_views from api.v1.models.battery import Battery from api.v1.inputs.inputs import REGISTER_BATTERY_RULES, validate +from api.v1 import auth @app_views.route('batteries/addbattery', methods=['POST'], strict_slashes=False) +@auth def create_battery(): + ''' + Create a new battery + ''' try: sent_data = request.get_json() @@ -34,7 +39,11 @@ def create_battery(): @app_views.route("/batteries", methods=["GET"], strict_slashes=False) +@auth def get_all_batteries(): + ''' + Get all batteries with their information + ''' try: batteries = Battery.query.all() return jsonify({ diff --git a/backend/api/v1/views/drivers.py b/backend/api/v1/views/drivers.py index bf993bb..a8424ff 100644 --- a/backend/api/v1/views/drivers.py +++ b/backend/api/v1/views/drivers.py @@ -29,6 +29,9 @@ @app_views.route("/login", methods=["POST"], strict_slashes=False) def login(): + ''' + User authentication + ''' sent_data = request.get_json(force=True) valid = validate(sent_data, LOGIN_RULES) @@ -68,9 +71,12 @@ def login(): return jsonify({"error": "Invalid email or password"}), 401 -@auth @app_views.route("drivers/addriver", methods=["POST"], strict_slashes=False) +@auth def create_driver(): + ''' + Create a new driver + ''' try: sent_data = request.get_json(force=True) @@ -118,17 +124,18 @@ def create_driver(): }), 400 -@auth @app_views.route("/drivers", methods=["GET"], strict_slashes=False) +@auth def get_drivers(): + ''' + Get all drivers information + ''' try: - return jsonify( - { + return jsonify({ "status": "Ok", "message": "All Drivers informations", "data": [driver.serialize_one for driver in Driver.query.all()], - } - ) + }) except Exception as e: return jsonify({ 'status': "Error", diff --git a/backend/api/v1/views/movements.py b/backend/api/v1/views/movements.py index 5a1c36b..09fe8bf 100644 --- a/backend/api/v1/views/movements.py +++ b/backend/api/v1/views/movements.py @@ -6,10 +6,15 @@ from sqlalchemy import and_ from api.v1.inputs.inputs import REGISTER_MOVEMENT_RULE, validate +from api.v1 import auth @app_views.route('movements/', methods=['POST'], strict_slashes=False) +@auth def update_movement(serial_number): + ''' + Update the movement of a swapped battery + ''' try: sent_data = request.get_json() diff --git a/backend/api/v1/views/stations.py b/backend/api/v1/views/stations.py index 4ee3bd9..39221d5 100644 --- a/backend/api/v1/views/stations.py +++ b/backend/api/v1/views/stations.py @@ -11,7 +11,11 @@ @app_views.route('stations/addstation', methods=['POST'], strict_slashes=False) +@auth def create_station(): + ''' + Create a new station + ''' try: sent_data = request.get_json(force=True) @@ -38,9 +42,12 @@ def create_station(): -@auth + @app_views.route("/stations", methods=["GET"], strict_slashes=False) def get_stations(): + ''' + Get all stations + ''' try: data = [] for station in Station.query.all(): @@ -59,8 +66,13 @@ def get_stations(): 'message': "Erro" + e, }), 400 + @app_views.route("/stations/batteries", methods=["GET"], strict_slashes=False) +@auth def get_batteries_per_station(): + ''' + Get all batteries of a specific station + ''' try: user_info = token_info(request.headers.get("Authorization")) diff --git a/backend/api/v1/views/swaps.py b/backend/api/v1/views/swaps.py index 4b840f2..06c805f 100644 --- a/backend/api/v1/views/swaps.py +++ b/backend/api/v1/views/swaps.py @@ -3,10 +3,15 @@ from api.v1.views import app_views from api.v1.helpers import measurePath, token_info from api.v1.inputs.inputs import REGISTER_SWAP_RULE, validate +from api.v1 import auth @app_views.route("swaps/addswap", methods=["POST"], strict_slashes=False) +@auth def create_battery_swap(): + ''' + Create a new battery exchange + ''' try: sent_data = request.get_json() @@ -63,7 +68,11 @@ def create_battery_swap(): @app_views.route("swaps", methods=["GET"], strict_slashes=False) +@auth def get_ongoing_swaps(): + ''' + Get all active battery exchange + ''' try: swaps = Swap.query.filter(Swap.end_time == None).all() @@ -82,15 +91,18 @@ def get_ongoing_swaps(): @app_views.route("swaps/stopswap/", methods=["PUT"], strict_slashes=False) +@auth def stop_battery_swap(swap_id): + ''' + Stop an active battery movememnt + ''' try: user_info = token_info(request.headers.get("Authorization")) if user_info["station_id"]: swap = Swap.stop_swap(swap_id, user_info["station_id"]) if swap.end_time is not None: - return jsonify( - { + return jsonify({ "status": "Ok", "message": "Battery returned successfully", "data": swap.serialize_one, @@ -109,7 +121,12 @@ def stop_battery_swap(swap_id): @app_views.route("swaps/totalswappedbattery", methods=["GET"], strict_slashes=False) +@auth def get_finished_swaps(): + ''' + Get all swapped(exchanged) batteries + + ''' try: user_info = token_info(request.headers.get("Authorization")) @@ -150,7 +167,11 @@ def get_finished_swaps(): @app_views.route("swaps/", methods=["GET"], strict_slashes=False) +@auth def get_swap_information(swap_id): + ''' + Get specific battery exchange information + ''' try: swap = Swap.query.filter(Swap.id == swap_id).first() distance = round( @@ -160,11 +181,9 @@ def get_swap_information(swap_id): data = swap.serialize_one data["distance"] = distance - response = jsonify( + return jsonify( {"status": "Ok", "message": "Swap information returned", "data": data} - ) - response.status_code = 200 - return response + ), 200 except Exception as e: return jsonify({ 'status': "Error", diff --git a/backend/app.py b/backend/app.py index b849fb5..f424a03 100644 --- a/backend/app.py +++ b/backend/app.py @@ -1,18 +1,10 @@ from flask import jsonify from waitress import serve from api import create_app -from api.v1.models import db APP = create_app('production') -def migrate(): - with APP.app_context(): - try: - db.create_all() - except Exception as e: - pass - @APP.teardown_appcontext def teardown_db(exception): """closes the storage on teardown""" @@ -32,6 +24,5 @@ def index(): return jsonify({'message': 'welcome'}), 200 if __name__ == '__main__': - # migrate() - APP.run(debug=True, host='0.0.0.0', port=5000) - # serve(APP, host='0.0.0.0', port=5000, threads=1) + # APP.run(debug=True, host='0.0.0.0', port=5000) + serve(APP, host='0.0.0.0', port=5000, threads=1) diff --git a/frontend/README.md b/frontend/README.md index 746d902..0080788 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -1,6 +1,3 @@ ---- -title: Energize Swap Frontend ---- # Energize Swap Frontend @@ -8,7 +5,7 @@ The Energize Swap Frontend is a user interface built using modern web technologi ## Features -- **Driver Management:** Register new drivers, view driver information, and update driver details. +- **Driver Management:** Register new drivers and view drivers information - **Battery Management:** Add new batteries, track battery locations, and monitor battery status. - **Swap Station Management:** Create and manage swap stations, view station details, and assign batteries to stations. - **Battery Swap Operations:** Perform battery swaps, initiate battery movement, and track ongoing and completed swaps. @@ -47,7 +44,7 @@ The frontend project follows a modular folder structure, allowing easy organizat The Energize Swap Frontend can be deployed to various hosting platforms or integrated into existing web applications. It's recommended to build the production version of the frontend before deployment. -To build the production version, run: `npm run build` +To build the production version, run: `yarn build` This command will generate optimized and minified static files in the `build` directory. You can then deploy these files to a static file server or integrate them with your backend server. diff --git a/frontend/src/pages/Login/LoginPage.jsx b/frontend/src/pages/Login/LoginPage.jsx index dc4c458..4e4a7ed 100644 --- a/frontend/src/pages/Login/LoginPage.jsx +++ b/frontend/src/pages/Login/LoginPage.jsx @@ -107,7 +107,7 @@ function LoginPage() { return res.json(); } res.json().then( res => { - toast.error(res?.error, { + toast.error(res?.error||res?.message, { position: toast.POSITION.TOP_RIGHT }); })