@@ -33,7 +33,19 @@ OR | |||||
``` | ``` | ||||
python3 -m uvicorn main:app --reload | python3 -m uvicorn main:app --reload | ||||
``` | ``` | ||||
## Explaining the file structure | |||||
``` | |||||
app //main app | |||||
|-alembic //Alembic -- database migration manager. Helps keep databases in sync and changeable (handles all SQL logic and keeps everything up-to-date) | |||||
| |- //All files irrelevant | |||||
|-apis //Stores the actual API Endpoints. | |||||
| |-v1 //Main API endpoints | |||||
|-core //Core project components | |||||
|-db //Stores database information and data | |||||
| |-models //Stores database models (represents SQL Tables) | |||||
| |-repository //Stores scripts to create, update and interact with the database | |||||
|-schemas //Stores Pydantic data models (Kind of like db/models except for use in the API itself, not just the database. Uses for data validation and stuff) | |||||
``` | |||||
## Git Guide | ## Git Guide | ||||
- [Git Reference](http://git.github.io/git-reference): despite its name it is more of a user guide. | - [Git Reference](http://git.github.io/git-reference): despite its name it is more of a user guide. | ||||
- ["Pro Git" Book](http://git-scm.com/book): by a core developer, also a thorough user guide. | - ["Pro Git" Book](http://git-scm.com/book): by a core developer, also a thorough user guide. | ||||
@@ -1,7 +1,8 @@ | |||||
# Base API router -- collecting all APIs here to not clutter main.py | # Base API router -- collecting all APIs here to not clutter main.py | ||||
from fastapi import APIRouter | from fastapi import APIRouter | ||||
from apis.v1 import route_user | |||||
from apis.v1 import route_user, route_vehicle | |||||
api_router = APIRouter() | api_router = APIRouter() | ||||
api_router.include_router(route_user.router, prefix="/user", tags=["users"]) | api_router.include_router(route_user.router, prefix="/user", tags=["users"]) | ||||
api_router.include_router(route_vehicle.router, prefix="/vehicle", tags=["vehicles"]) |
@@ -2,10 +2,11 @@ | |||||
from fastapi import APIRouter, status | from fastapi import APIRouter, status | ||||
from sqlalchemy.orm import Session | from sqlalchemy.orm import Session | ||||
from fastapi import Depends | from fastapi import Depends | ||||
from typing import List | |||||
from schemas.user import UserCreate, ShowUser | from schemas.user import UserCreate, ShowUser | ||||
from db.session import get_db | from db.session import get_db | ||||
from db.repository.user import create_new_user | |||||
from db.repository.user import create_new_user, list_users | |||||
router = APIRouter() | router = APIRouter() | ||||
@@ -15,3 +16,12 @@ router = APIRouter() | |||||
def create_user(user: UserCreate, db: Session = Depends(get_db)): | def create_user(user: UserCreate, db: Session = Depends(get_db)): | ||||
user = create_new_user(user=user, db=db) | user = create_new_user(user=user, db=db) | ||||
return user | return user | ||||
@router.get("/", response_model=List[ShowUser], status_code=status.HTTP_200_OK) | |||||
def get_all_users(db: Session = Depends(get_db), role: str = None): | |||||
if role == None: | |||||
users = list_users(db=db) | |||||
return users | |||||
users = list_users(db=db, role=role) | |||||
return users |
@@ -0,0 +1,48 @@ | |||||
from fastapi import APIRouter, status, HTTPException | |||||
from sqlalchemy.orm import Session | |||||
from fastapi import Depends | |||||
from typing import List | |||||
from db.session import get_db | |||||
from schemas.vehicle import OutputVehicle, CreateVehicle | |||||
from db.repository.vehicle import ( | |||||
create_new_vehicle, | |||||
assign_vehicle_driver, | |||||
list_vehicles, | |||||
) | |||||
router = APIRouter() | |||||
@router.post("/new", response_model=OutputVehicle, status_code=status.HTTP_201_CREATED) | |||||
async def create_vehicle(vehicle: CreateVehicle, db: Session = Depends(get_db)): | |||||
vehicle = create_new_vehicle(vehicle=vehicle, db=db) | |||||
return vehicle | |||||
@router.get( | |||||
"/assign/{vehicle_id}/{driver_id}", | |||||
response_model=OutputVehicle, | |||||
status_code=status.HTTP_200_OK, | |||||
) | |||||
async def assign_drver(vehicle_id: int, driver_id: int, db: Session = Depends(get_db)): | |||||
vehicle = assign_vehicle_driver(vehicle_id=vehicle_id, driver_id=driver_id, db=db) | |||||
if vehicle == "nodriver": | |||||
raise HTTPException( | |||||
status_code=404, detail=f"Driver with id {driver_id} not found" | |||||
) | |||||
if vehicle == "novehicle": | |||||
raise HTTPException( | |||||
status_code=404, detail=f"Vehicle with id {vehicle_id} not found" | |||||
) | |||||
if vehicle == "alreadyassigned": | |||||
raise HTTPException( | |||||
status_code=400, | |||||
detail=f"Driver with id {driver_id} is already assigned to vehicle with id {vehicle_id}", | |||||
) | |||||
return vehicle | |||||
@router.get("/", response_model=List[OutputVehicle], status_code=status.HTTP_200_OK) | |||||
async def get_all_vehicles(db: Session = Depends(get_db)): | |||||
vehicles = list_vehicles(db=db) | |||||
return vehicles |
@@ -21,3 +21,17 @@ def create_new_user(user: UserCreate, db: Session): | |||||
db.commit() | db.commit() | ||||
db.refresh(user) | db.refresh(user) | ||||
return user | return user | ||||
def verify_driver_exists(driver_id: int, db: Session): | |||||
driver = db.query(User).filter(User.Id == driver_id).first() | |||||
if not driver: | |||||
return False | |||||
if driver.Role != "Driver": | |||||
return False | |||||
return True | |||||
def list_users(db: Session, role: str = "Any"): | |||||
users = db.query(User).filter((User.Role == role) | (role == "Any")).all() | |||||
return users |
@@ -0,0 +1,46 @@ | |||||
from sqlalchemy.orm import Session | |||||
from schemas.vehicle import CreateVehicle, OutputVehicle | |||||
from db.models.vehicle import Vehicle | |||||
from db.repository.user import verify_driver_exists | |||||
def create_new_vehicle(vehicle: CreateVehicle, db: Session): | |||||
vehicle = Vehicle( | |||||
Model=vehicle.model, | |||||
Year=vehicle.year, | |||||
LicensePlate=vehicle.license_plate, | |||||
Type=vehicle.type, | |||||
Mileage=vehicle.mileage, | |||||
AssignedDriverIds=[], | |||||
CurrentLocation=[], | |||||
Fuel=0, | |||||
MaintenanceNotes=[], | |||||
) | |||||
db.add(vehicle) | |||||
db.commit() | |||||
db.refresh(vehicle) | |||||
return vehicle | |||||
def assign_vehicle_driver(vehicle_id: int, driver_id: int, db: Session): | |||||
vehicledb = db.query(Vehicle).filter(Vehicle.Id == vehicle_id) | |||||
vehicle = vehicledb.first() | |||||
if not vehicle: | |||||
return "novehicle" | |||||
if driver_id in vehicle.AssignedDriverIds: | |||||
return "alreadyassigned" | |||||
if verify_driver_exists(driver_id=driver_id, db=db): | |||||
print(vehicle.AssignedDriverIds) | |||||
vehicledb.update({"AssignedDriverIds": vehicle.AssignedDriverIds + [driver_id]}) | |||||
print(vehicle.AssignedDriverIds) | |||||
db.add(vehicle) | |||||
db.commit() | |||||
db.refresh(vehicle) | |||||
return vehicle | |||||
# return a 404 error if the driver does not exist | |||||
return "nodriver" | |||||
def list_vehicles(db: Session): | |||||
vehicles = db.query(Vehicle).all() | |||||
return vehicles |
@@ -0,0 +1,24 @@ | |||||
from typing import Optional | |||||
from pydantic import BaseModel, root_validator | |||||
from datetime import datetime | |||||
class CreateVehicle(BaseModel): | |||||
id: int | |||||
model: str | |||||
year: int | |||||
license_plate: str | |||||
type: str | |||||
mileage: int | |||||
class OutputVehicle(BaseModel): | |||||
Model: str | |||||
Year: int | |||||
LicensePlate: str | |||||
Type: str | |||||
Mileage: int | |||||
CurrentLocation: Optional[list[str]] = None | |||||
Fuel: Optional[int] = 0 | |||||
MaintenanceNotes: Optional[list[str]] = None | |||||
AssignedDriverIds: Optional[list[int]] = None |