Skip to content

Commit

Permalink
ft: add frontend
Browse files Browse the repository at this point in the history
  • Loading branch information
Crispy-rw committed Jun 1, 2023
1 parent 2650346 commit a626fe1
Show file tree
Hide file tree
Showing 25 changed files with 334 additions and 204 deletions.
3 changes: 0 additions & 3 deletions backend/api/v1/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
'''
Our Main api routes
'''
from functools import wraps
from flask import jsonify, request

Expand Down
2 changes: 0 additions & 2 deletions backend/api/v1/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,8 @@ def token_info(token):
claims = jwt.decode(token.split(' ')[1], 'test')
claims.validate()
except ExpiredTokenError as e:
print("=>", e)
return False
except Exception as e: # noqa: E722
print("===>", e)
return False
return claims

Expand Down
77 changes: 77 additions & 0 deletions backend/api/v1/inputs/inputs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
''' Input Validation Classes '''
from api.v1.validations import Validations


# Registration validations
REGISTER_DRIVER = [
{'name': [('string', True), ('minimum', 1),
('maximum', 30), ('required', True)]},
{'email': [('minimum', 6), ('maximum', 30),
('required', True), ('email', True)]},
{'phone': [('minimum', 8), ('maximum', 10), ('required', True)]},
{'address': [('minimum', 6), ('maximum', 30)]},
{'license_number': [('minimum', 6), ('maximum', 30)]},
{'license_expiry': [('minimum', 6), ('maximum', 30)]},
{'motocycle_make': [('minimum', 6), ('maximum', 30)]},
{'motocycle_model': [('minimum', 6), ('maximum', 30)]},
{'motocycle_year': [('minimum', 2), ('maximum', 4)]},
]
# Login validation
LOGIN_RULES = [
{'email': [('minimum', 6), ('maximum', 30),
('required', True), ('email', True)]},
{'password': [('minimum', 6), ('required', True)]},
{'station': [('minimum', 1)]}
]
# Change password validations
REGISTER_STATION = [
{'name': [('minimum', 3), ('maximum', 30), ('required', True)]},
{'location': [('minimum', 3), ('maximum', 30), ('required', True)]},
]
# Reset password validations
REGISTER_BATTERY_RULES = [
{'battery_type': [('minimum', 3), ('maximum', 14),
('required', True)]},
{'manufacture_date': [('minimum', 2), ('maximum', 4), ('required', True)]},
{'serial_number': [('minimum', 5), ('maximum', 30),
('required', True)]},
{'station': [('minimum', 1), ('maximum',10),
('required', True)]},
]

# Reset password validations
REGISTER_SWAP_RULE = [
{'battery': [('required', True), ('minimum', 1),
('maximum', 10)]},
{'driver': [('required', True), ('minimum', 1),
('maximum', 10)]}
]

REGISTER_MOVEMENT_RULE = [
{'lat': [('required', True)]},
{'long': [('required', True)]},
{'battery_percentage': [('required', True), ('minimum', 1),
('maximum', 2)]}
]


def validate(inputs, all_rules):
''' Register validation method '''
error_bag = {}
valid = Validations(inputs)
for rules in all_rules:
for key in rules:
rule_key = key
for rule in rules[rule_key]:
execute = getattr(valid, rule[0])(rule_key, rule[1])
if execute is True:
pass
if execute is not True:
if rule_key in error_bag:
error_bag[rule_key].append(execute)
else:
error_bag[rule_key] = []
error_bag[rule_key].append(execute)
if len(error_bag) is not 0:
return error_bag
return True
61 changes: 61 additions & 0 deletions backend/api/v1/validations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
'''
Validations Methods Class
'''

import re


class Validations():
'''Validations class'''

def __init__(self, all_inputs):
''' All inputs dictionary should be available to the class'''
for key, value in all_inputs.items():
if (all_inputs[key] is not None and
not isinstance(all_inputs[key], int)):
if str(all_inputs[key]).strip() == '':
all_inputs[key] = None
self.all = all_inputs

def string(self, key, string):
'''Check if input is required'''
if key in self.all and self.all[key] is not None:
if not re.match(r"[^[a-zA-Z0-9]+$", self.all[key]):
return True
return key.capitalize() + " should be string"
return True

def minimum(self, key, minimum):
'''Check required character size'''
if key in self.all and self.all[key] is not None:
if len(str(self.all[key])) < int(minimum):
return "{} should not be less than {} characters".format(
key.capitalize(), str(minimum))
return True
return True

def maximum(self, key, maximum):
'''Check required character size'''
if key in self.all and self.all[key] is not None:
if len(str(self.all[key])) > int(maximum):
return "{} should not be greater than {} characters".format(
key.capitalize(), str(maximum)
)
return True
return True

def email(self, key, email):
'''Check required character size'''
if key in self.all and self.all[key] is not None:
if not re.match(r"[^@\s]+@[^@\s]+\.[a-zA-Z]+$", self.all[key]):
return "Invalid email address"
return True
return True

def required(self, key, is_required=True):
'''Check input it is required'''
if key in self.all:
if self.all[key] is None or str(self.all[key]).strip() == '':
return key.capitalize() + " should not be empty"
return True
return key.capitalize() + " is required"
15 changes: 12 additions & 3 deletions backend/api/v1/views/batteries.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,35 @@
from flask import request, jsonify
from api.v1.views import app_views
from api.v1.models.battery import Battery
from api.v1.inputs.inputs import REGISTER_BATTERY_RULES, validate


@app_views.route('batteries/addbattery', methods=['POST'], strict_slashes=False)
def create_battery():
try:
sent_data = request.get_json()
print(sent_data)

valid = validate(sent_data, REGISTER_BATTERY_RULES)

if valid is not True:
return jsonify(
status='error',
message="Please provide valid details",
errors=valid),400

resp = Battery.save({
'battery_type': sent_data['battery_type'],
'manufacture_date': sent_data['manufacture_date'],
'serial_number': sent_data['serial_number'],
'station_id': sent_data['station_id']
'station_id': sent_data['station']
})
return jsonify({'status': 'Ok',
'message': 'Registered new battery',
'data': resp.serialize_one}), 201
except Exception as e:
return jsonify({
'status': "Error",
"message": "Error adding a battery: {}".format(e)
"message": "Error adding a battery: {}".format(e)
}), 400


Expand Down
34 changes: 27 additions & 7 deletions backend/api/v1/views/drivers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
from api.v1 import auth
from sqlalchemy import desc, func

from api.v1.inputs.inputs import LOGIN_RULES, REGISTER_DRIVER, validate


creds = {
"[email protected]": {
Expand All @@ -27,25 +29,34 @@

@app_views.route("/login", methods=["POST"], strict_slashes=False)
def login():
sent_data = request.get_json()
sent_data = request.get_json(force=True)

valid = validate(sent_data, LOGIN_RULES)

if valid is not True:
return jsonify(
status='error',
message="Please provide valid details",
errors=valid), 400

email = sent_data["email"]
password = sent_data["password"]
station_id = sent_data.get("station_id", None)
station = sent_data.get("station", None)

if email in creds and password == creds[email]["password"]:

# check if he is an admin
if station_id is None and creds[email]["role"] == "admin":
if station is None and creds[email]["role"] == "admin":
# Generate JWT token
token = get_token(creds[email])

# Return the token as a JSON response
return jsonify({"email": email, "token": token})

# check if a user is a manager
elif station_id is not None and creds[email]["role"] == "manager":
elif station is not None and creds[email]["role"] == "manager":
# append the station_id to the user obj
creds[email]["station_id"] = station_id
creds[email]["station_id"] = station

# Generate JWT token
token = get_token(creds[email])
Expand All @@ -61,7 +72,16 @@ def login():
@app_views.route("drivers/addriver", methods=["POST"], strict_slashes=False)
def create_driver():
try:
sent_data = request.get_json()
sent_data = request.get_json(force=True)

valid = validate(sent_data, REGISTER_DRIVER)

if valid is not True:
return jsonify(
status='error',
message="Please provide valid details",
errors=valid), 400

data = {
"name": sent_data.get("name"),
"email": sent_data.get("email"),
Expand All @@ -83,7 +103,7 @@ def create_driver():
return jsonify({
"status": "error",
"message":(
"You have already " "registered driver with the same name"),
"You have alreadyregistered driver with the same name"),
}), 400

resp = Driver.save(data)
Expand Down
11 changes: 11 additions & 0 deletions backend/api/v1/views/movements.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,22 @@
from api.v1.models.swap import Swap
from sqlalchemy import and_

from api.v1.inputs.inputs import REGISTER_MOVEMENT_RULE, validate


@app_views.route('movements/<serial_number>', methods=['POST'], strict_slashes=False)
def update_movement(serial_number):
try:
sent_data = request.get_json()

valid = validate(sent_data, REGISTER_MOVEMENT_RULE)

if valid is not True:
return jsonify(
status='error',
message="Please provide valid details",
errors=valid),400

battery_check = Battery.query.filter(
Battery.serial_number == serial_number).first()
if battery_check is None:
Expand Down
31 changes: 25 additions & 6 deletions backend/api/v1/views/stations.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,41 @@
from api.v1.models.station import Station
from api.v1.views import app_views
from api.v1.models.swap import Swap
from api.v1.inputs.inputs import REGISTER_STATION, validate
from api.v1 import auth




@app_views.route('stations/addstation', methods=['POST'], strict_slashes=False)
def create_station():
try:
sent_data = request.get_json()
sent_data = request.get_json(force=True)

valid = validate(sent_data, REGISTER_STATION)

if valid is not True:
response = jsonify(
status='error',
message="Please provide valid details",
errors=valid)
response.status_code = 400
return response


resp = Station.save({'name': sent_data['name'], 'location': sent_data['location']})
return jsonify({'status': 'Ok', 'message': 'New Station registered', 'data': resp.serialize_one})
return jsonify({'status': 'Ok',
'message': 'New Station registered',
'data': resp.serialize_one})
except Exception as e:
return jsonify({
'status': "Error",
"message": "Error creating a station: {}".format(e)
}), 400
return jsonify(
status = "Error",
message = "Error creating a station: {}".format(e)
), 400



@auth
@app_views.route("/stations", methods=["GET"], strict_slashes=False)
def get_stations():
try:
Expand Down
16 changes: 13 additions & 3 deletions backend/api/v1/views/swaps.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,26 @@
from api.v1.models.swap import Swap
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


@app_views.route("swaps/addswap", methods=["POST"], strict_slashes=False)
def create_battery_swap():
try:
sent_data = request.get_json()

valid = validate(sent_data, REGISTER_SWAP_RULE)

if valid is not True:
return jsonify(
status='error',
message="Please provide valid details",
errors=valid),400

user_info = token_info(request.headers.get("Authorization"))
check_battery = (
Swap.query
.filter(Swap.battery_id == sent_data["battery_id"])
.filter(Swap.battery_id == sent_data["battery"])
.filter(Swap.end_time == None)
.first()
)
Expand All @@ -36,8 +46,8 @@ def create_battery_swap():

swap = Swap.save(
{
"battery_id": sent_data["battery_id"],
"driver_id": sent_data["driver_id"],
"battery_id": sent_data["battery"],
"driver_id": sent_data["driver"],
"station_id": user_info["station_id"],
}
)
Expand Down
4 changes: 2 additions & 2 deletions backend/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ class ProductionConfig(Config):
DEBUG = False
TESTING = False
# SQLAlchemy Config
# SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URI')
SQLALCHEMY_DATABASE_URI = "mysql://root:root@batteryswap-db:3308/energize_swap_db"
SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URI')
# SQLALCHEMY_DATABASE_URI = "mysql://root:root@batteryswap-db:3308/energize_swap_db"
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_ECHO = True
SQLALCHEMY_POOL_TIMEOUT = 10
Expand Down
Loading

0 comments on commit a626fe1

Please sign in to comment.