Browse Source

Merge pull request #1 from Madiwka4/Madi

Madi
main
Madiwka 1 year ago
committed by GitHub
parent
commit
5a63267ea4
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 255 additions and 240 deletions
  1. +1
    -1
      README.md
  2. +0
    -1
      app/alembic/README
  3. +0
    -83
      app/alembic/env.py
  4. +0
    -26
      app/alembic/script.py.mako
  5. +0
    -60
      app/alembic/versions/9a0214838ac8_create_user_and_vehicle_tables.py
  6. +49
    -14
      app/apis/v1/route_auth.py
  7. +12
    -3
      app/apis/v1/route_user.py
  8. +62
    -10
      app/apis/v1/route_vehicle.py
  9. +3
    -1
      app/core/auth.py
  10. +23
    -1
      app/core/config.py
  11. +3
    -3
      app/db/base.py
  12. +14
    -0
      app/db/base_class.py
  13. +0
    -0
      app/db/models/assignment.py
  14. +4
    -2
      app/db/models/user.py
  15. +4
    -3
      app/db/models/vehicle.py
  16. +7
    -0
      app/db/repository/user.py
  17. +47
    -22
      app/db/repository/vehicle.py
  18. +6
    -2
      app/main.py
  19. +5
    -0
      app/schemas/token.py
  20. +4
    -2
      app/schemas/user.py
  21. +11
    -6
      app/schemas/vehicle.py

+ 1
- 1
README.md View File

@@ -14,7 +14,7 @@
## How to launch ## How to launch
First, make sure that python is installed. Create a virtual environment (if you want to) First, make sure that python is installed. Create a virtual environment (if you want to)
``` ```
python -m venv venv
python/python3 -m venv venv
source /venv/bin/activate source /venv/bin/activate
``` ```
(not necessary) (not necessary)


+ 0
- 1
app/alembic/README View File

@@ -1 +0,0 @@
Generic single-database configuration.

+ 0
- 83
app/alembic/env.py View File

@@ -1,83 +0,0 @@
from logging.config import fileConfig

from sqlalchemy import engine_from_config
from sqlalchemy import pool

from alembic import context

from core.config import settings
from db.base import Base


# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config


config.set_main_option("sqlalchemy.url", settings.DATABASE_URL)

# Interpret the config file for Python logging.
# This line sets up loggers basically.
if config.config_file_name is not None:
fileConfig(config.config_file_name)

# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
target_metadata = Base.metadata

# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.


def run_migrations_offline() -> None:
"""Run migrations in 'offline' mode.

This configures the context with just a URL
and not an Engine, though an Engine is acceptable
here as well. By skipping the Engine creation
we don't even need a DBAPI to be available.

Calls to context.execute() here emit the given string to the
script output.

"""
url = config.get_main_option("sqlalchemy.url")
context.configure(
url=url,
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
)

with context.begin_transaction():
context.run_migrations()


def run_migrations_online() -> None:
"""Run migrations in 'online' mode.

In this scenario we need to create an Engine
and associate a connection with the context.

"""
connectable = engine_from_config(
config.get_section(config.config_ini_section, {}),
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)

with connectable.connect() as connection:
context.configure(connection=connection, target_metadata=target_metadata)

with context.begin_transaction():
context.run_migrations()


if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()

+ 0
- 26
app/alembic/script.py.mako View File

@@ -1,26 +0,0 @@
"""${message}

Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}

"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa
${imports if imports else ""}

# revision identifiers, used by Alembic.
revision: str = ${repr(up_revision)}
down_revision: Union[str, None] = ${repr(down_revision)}
branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}


def upgrade() -> None:
${upgrades if upgrades else "pass"}


def downgrade() -> None:
${downgrades if downgrades else "pass"}

+ 0
- 60
app/alembic/versions/9a0214838ac8_create_user_and_vehicle_tables.py View File

@@ -1,60 +0,0 @@
"""create User and Vehicle tables

Revision ID: 9a0214838ac8
Revises:
Create Date: 2023-09-01 13:31:01.324861

"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision: str = '9a0214838ac8'
down_revision: Union[str, None] = None
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('users',
sa.Column('Id', sa.Integer(), nullable=False),
sa.Column('Name', sa.String(), nullable=False),
sa.Column('MiddleName', sa.String(), nullable=True),
sa.Column('LastName', sa.String(), nullable=False),
sa.Column('BirthDate', sa.DateTime(), nullable=False),
sa.Column('ContactNumber', sa.String(), nullable=False),
sa.Column('Email', sa.String(), nullable=False),
sa.Column('Role', sa.String(), nullable=False),
sa.Column('DrivingLicenseNumber', sa.String(), nullable=True),
sa.Column('HashedPassword', sa.String(), nullable=False),
sa.PrimaryKeyConstraint('Id')
)
op.create_index(op.f('ix_users_Id'), 'users', ['Id'], unique=False)
op.create_table('vehicles',
sa.Column('Id', sa.Integer(), nullable=False),
sa.Column('Model', sa.String(), nullable=False),
sa.Column('Year', sa.Integer(), nullable=False),
sa.Column('LicensePlate', sa.String(), nullable=False),
sa.Column('Type', sa.String(), nullable=False),
sa.Column('AssignedDriverIds', sa.ARRAY(sa.Integer()), nullable=True),
sa.Column('CurrentLocation', sa.ARRAY(sa.String()), nullable=True),
sa.Column('Fuel', sa.Integer(), nullable=False),
sa.Column('Mileage', sa.Integer(), nullable=False),
sa.Column('MaintenanceNotes', sa.ARRAY(sa.String()), nullable=True),
sa.PrimaryKeyConstraint('Id')
)
op.create_index(op.f('ix_vehicles_Id'), 'vehicles', ['Id'], unique=False)
# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f('ix_vehicles_Id'), table_name='vehicles')
op.drop_table('vehicles')
op.drop_index(op.f('ix_users_Id'), table_name='users')
op.drop_table('users')
# ### end Alembic commands ###

+ 49
- 14
app/apis/v1/route_auth.py View File

@@ -1,3 +1,4 @@
from datetime import datetime
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from fastapi import Depends, APIRouter from fastapi import Depends, APIRouter
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
@@ -7,20 +8,23 @@ from db.session import get_db
from core.hashing import Hasher from core.hashing import Hasher
from core.config import settings from core.config import settings
from jose import JWTError, jwt from jose import JWTError, jwt
from schemas.token import Token
from schemas.token import Token, TokenPayload
from db.repository.user import get_user_by_email, get_user_by_phone from db.repository.user import get_user_by_email, get_user_by_phone
from core.auth import create_access_token from core.auth import create_access_token



router = APIRouter() router = APIRouter()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")
oauth2_scheme = OAuth2PasswordBearer(
tokenUrl="/token",
)




def authenticate_user(login: str, password: str, db: Session): def authenticate_user(login: str, password: str, db: Session):
print("Trying to auth...") print("Trying to auth...")
user = None user = None
if ("@" in login):
if "@" in login:
user = get_user_by_email(email=login, db=db) user = get_user_by_email(email=login, db=db)
elif ("+" in login):
elif "+" in login:
user = get_user_by_phone(phone=login, db=db) user = get_user_by_phone(phone=login, db=db)
else: else:
return False return False
@@ -31,30 +35,61 @@ def authenticate_user(login: str, password: str, db: Session):
return user return user




def get_current_user(token: Annotated[str, Depends(oauth2_scheme)], db: Annotated[Session, Depends(get_db)]):
def get_current_user(
token: Annotated[str, Depends(oauth2_scheme)],
db: Annotated[Session, Depends(get_db)],
):
print("Getting current user...") print("Getting current user...")
try: try:
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
payload = jwt.decode(
token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM]
)
token_data = TokenPayload(**payload)

if datetime.fromtimestamp(token_data.exp) < datetime.now():
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Session expired. Please login again.",
)
username: str = payload.get("sub") username: str = payload.get("sub")
if username is None: if username is None:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials")
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
)
except JWTError: except JWTError:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials")
if ("@" in username):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
)
if "@" in username:
user = get_user_by_email(email=username, db=db) user = get_user_by_email(email=username, db=db)
elif ("+" in username):
elif "+" in username:
user = get_user_by_phone(phone=username, db=db) user = get_user_by_phone(phone=username, db=db)
else: else:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials")
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
)
return user return user




@router.post("/token", response_model=Token) @router.post("/token", response_model=Token)
def access_token(form_data: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db)):
def access_token(
form_data: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db)
):
print("Getting token...") print("Getting token...")
user = authenticate_user(form_data.username, form_data.password, db) user = authenticate_user(form_data.username, form_data.password, db)
print(user) print(user)
if not user: if not user:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid username or password")
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid username or password",
)
access_token = create_access_token(data={"sub": user.Email}) access_token = create_access_token(data={"sub": user.Email})
return {"access_token": access_token, "token_type": "bearer"}
print("TOKENS ARE: ")
print(access_token)
return {
"access_token": access_token,
"token_type": "bearer",
}

+ 12
- 3
app/apis/v1/route_user.py View File

@@ -14,9 +14,15 @@ router = APIRouter()




@router.post("/", response_model=ShowUser, status_code=status.HTTP_201_CREATED) @router.post("/", response_model=ShowUser, status_code=status.HTTP_201_CREATED)
def create_user(user: UserCreate, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)):
if current_user.Role != "Admin":
def create_user(
user: UserCreate,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
if (current_user.Role != "Admin"):
raise HTTPException(status_code=403, detail="You are not authorized to perform this action") raise HTTPException(status_code=403, detail="You are not authorized to perform this action")
# if current_user.Role != "Admin":
# raise HTTPException(status_code=403, detail="You are not authorized to perform this action")
user = create_new_user(user=user, db=db) user = create_new_user(user=user, db=db)
return user return user


@@ -31,7 +37,10 @@ def get_all_users(db: Session = Depends(get_db), role: str = None):




@router.get("/me", response_model=ShowUser, status_code=status.HTTP_200_OK) @router.get("/me", response_model=ShowUser, status_code=status.HTTP_200_OK)
def get_user_me(current_user: Annotated[User, Depends(get_current_user)], db: Annotated[Session, Depends(get_db)]):
def get_user_me(
current_user: Annotated[User, Depends(get_current_user)],
db: Annotated[Session, Depends(get_db)],
):
print("Getting current user...") print("Getting current user...")
return current_user return current_user




+ 62
- 10
app/apis/v1/route_vehicle.py View File

@@ -3,7 +3,7 @@ from sqlalchemy.orm import Session
from fastapi import Depends from fastapi import Depends
from typing import List from typing import List
from db.session import get_db from db.session import get_db
from schemas.vehicle import OutputVehicle, CreateVehicle, UpdateVehicle
from schemas.vehicle import OutputVehicle, CreateVehicle, UpdateVehicle, VehicleLocation
from db.repository.vehicle import ( from db.repository.vehicle import (
create_new_vehicle, create_new_vehicle,
assign_vehicle_driver, assign_vehicle_driver,
@@ -11,6 +11,7 @@ from db.repository.vehicle import (
get_vehicle_by_id, get_vehicle_by_id,
replace_vehicle_data, replace_vehicle_data,
delete_vehicle_data, delete_vehicle_data,
update_vehicle_geoloc,
) )
from db.models.user import User from db.models.user import User
from apis.v1.route_auth import get_current_user from apis.v1.route_auth import get_current_user
@@ -19,9 +20,16 @@ router = APIRouter()




@router.post("/", response_model=OutputVehicle, status_code=status.HTTP_201_CREATED) @router.post("/", response_model=OutputVehicle, status_code=status.HTTP_201_CREATED)
async def create_vehicle(vehicle: CreateVehicle, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)):
async def create_vehicle(
vehicle: CreateVehicle,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
print(current_user.Role)
if current_user.Role != "Admin": if current_user.Role != "Admin":
raise HTTPException(status_code=403, detail="You are not authorized to perform this action")
raise HTTPException(
status_code=403, detail="You are not authorized to perform this action"
)
vehicle = create_new_vehicle(vehicle=vehicle, db=db) vehicle = create_new_vehicle(vehicle=vehicle, db=db)
return vehicle return vehicle


@@ -52,9 +60,16 @@ async def create_vehicle(vehicle: CreateVehicle, db: Session = Depends(get_db),
response_model=OutputVehicle, response_model=OutputVehicle,
status_code=status.HTTP_200_OK, status_code=status.HTTP_200_OK,
) )
async def assign_driver(vehicle_id: int, driver_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)):
async def assign_driver(
vehicle_id: int,
driver_id: int,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
if current_user.Role != "Admin": if current_user.Role != "Admin":
raise HTTPException(status_code=403, detail="You are not authorized to perform this action")
raise HTTPException(
status_code=403, detail="You are not authorized to perform this action"
)
vehicle = assign_vehicle_driver(vehicle_id=vehicle_id, driver_id=driver_id, db=db) vehicle = assign_vehicle_driver(vehicle_id=vehicle_id, driver_id=driver_id, db=db)
if vehicle == "nodriver": if vehicle == "nodriver":
raise HTTPException( raise HTTPException(
@@ -67,7 +82,7 @@ async def assign_driver(vehicle_id: int, driver_id: int, db: Session = Depends(g
if vehicle == "alreadyassigned": if vehicle == "alreadyassigned":
raise HTTPException( raise HTTPException(
status_code=400, status_code=400,
detail=f"Driver with id {driver_id} is already assigned to vehicle with id {vehicle_id}",
detail=f"A driver is already assigned to vehicle with id {vehicle_id}",
) )
return vehicle return vehicle


@@ -92,23 +107,60 @@ async def get_vehicle(vehicle_id: int, db: Session = Depends(get_db)):
"/{vehicle_id}", response_model=OutputVehicle, status_code=status.HTTP_200_OK "/{vehicle_id}", response_model=OutputVehicle, status_code=status.HTTP_200_OK
) )
def update_vehicle( def update_vehicle(
vehicle_id: int, vehicle: UpdateVehicle, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)
vehicle_id: int,
vehicle: UpdateVehicle,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
): ):
if current_user.Role != "Admin": if current_user.Role != "Admin":
raise HTTPException(status_code=403, detail="You are not authorized to perform this action")
raise HTTPException(
status_code=403, detail="You are not authorized to perform this action"
)
vehicleRes = replace_vehicle_data(id=vehicle_id, vehicle=vehicle, db=db) vehicleRes = replace_vehicle_data(id=vehicle_id, vehicle=vehicle, db=db)
if vehicleRes == "vehicleNotFound": if vehicleRes == "vehicleNotFound":
raise HTTPException(status_code=404, detail="Vehicle not found") raise HTTPException(status_code=404, detail="Vehicle not found")
elif vehicleRes == "badreq": elif vehicleRes == "badreq":
raise HTTPException(status_code=502, detail="Bad request") raise HTTPException(status_code=502, detail="Bad request")
elif vehicleRes == "driverNotFound":
raise HTTPException(status_code=404, detail="Driver not found")
return vehicleRes return vehicleRes




@router.delete("/{vehicle_id}", status_code=status.HTTP_200_OK) @router.delete("/{vehicle_id}", status_code=status.HTTP_200_OK)
def delete_vehicle(vehicle_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)):
def delete_vehicle(
vehicle_id: int,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
if current_user.Role != "Admin": if current_user.Role != "Admin":
raise HTTPException(status_code=403, detail="You are not authorized to perform this action")
raise HTTPException(
status_code=403, detail="You are not authorized to perform this action"
)
result = delete_vehicle_data(id=vehicle_id, db=db) result = delete_vehicle_data(id=vehicle_id, db=db)
if result == "vehicleNotFound": if result == "vehicleNotFound":
raise HTTPException(status_code=404, detail="Vehicle not found") raise HTTPException(status_code=404, detail="Vehicle not found")
return {"msg": "Vehicle deleted successfully"} return {"msg": "Vehicle deleted successfully"}


@router.post("/{vehicle_id}/location", status_code=status.HTTP_200_OK)
def update_vehicle_location(
vehicle_id: int,
location: VehicleLocation,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db),
):
print(current_user)
print(current_user.Name)
if current_user.Role != "Driver":
raise HTTPException(
status_code=403, detail="You are not authorized to perform this action"
)
if current_user.AssignedVehicle != vehicle_id:
raise HTTPException(
status_code=403, detail="You are not the correct car driver"
)
print("FUNNY")
vehicle = update_vehicle_geoloc(vehicle_id=vehicle_id, location=location, db=db)
if vehicle == "vehiclenotfound":
raise HTTPException(status_code=404, detail="Vehicle not found")
return vehicle

+ 3
- 1
app/core/auth.py View File

@@ -12,5 +12,7 @@ def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
else: else:
expire = datetime.utcnow() + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE) expire = datetime.utcnow() + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE)
to_encode.update({"exp": expire}) to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
encoded_jwt = jwt.encode(
to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM
)
return encoded_jwt return encoded_jwt

+ 23
- 1
app/core/config.py View File

@@ -7,9 +7,31 @@ class Settings:
POSTGRES_PORT: str = "5432" POSTGRES_PORT: str = "5432"
POSTGRES_DB: str = "VMSData" POSTGRES_DB: str = "VMSData"
DATABASE_URL = f"postgresql://{POSTGRES_USER}:{POSTGRES_PASSWORD}@{POSTGRES_SERVER}:{POSTGRES_PORT}/{POSTGRES_DB}" DATABASE_URL = f"postgresql://{POSTGRES_USER}:{POSTGRES_PASSWORD}@{POSTGRES_SERVER}:{POSTGRES_PORT}/{POSTGRES_DB}"
ACCESS_TOKEN_EXPIRE: int = 30
ACCESS_TOKEN_EXPIRE: int = 60 * 24 * 7 # 7 days
SECRET_KEY: str = "tH357aC6oA7ofCaN3yTffYkRh" SECRET_KEY: str = "tH357aC6oA7ofCaN3yTffYkRh"
ALGORITHM: str = "HS256" ALGORITHM: str = "HS256"




settings = Settings() settings = Settings()


def createAdminAcc():
from db.session import SessionLocal
from db.repository.user import create_new_user
from schemas.user import UserCreate
from db.models.user import User

db = SessionLocal()
user = UserCreate(
Email="madi.turgunov@nu.edu.kz",
Password="1234567",
Name="Madi",
LastName="Turgunov",
ContactNumber="+77071234567",
Role="Admin",
BirthDate="2000-01-01T00:00:00+06:00",
)
if db.query(User).filter(User.Email == user.Email).first():
return False
create_new_user(user=user, db=db)
return True

+ 3
- 3
app/db/base.py View File

@@ -1,4 +1,4 @@
# Base class for all sqlalchemy models # Base class for all sqlalchemy models
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
from db.base_class import Base
from db.models.user import User
from db.models.vehicle import Vehicle

+ 14
- 0
app/db/base_class.py View File

@@ -0,0 +1,14 @@
from typing import Any

from sqlalchemy.ext.declarative import as_declarative, declared_attr


@as_declarative()
class Base:
id: Any
__name__: str

# Generate __tablename__ automatically
@declared_attr
def __tablename__(cls) -> str:
return cls.__name__.lower()

+ 0
- 0
app/db/models/assignment.py View File


+ 4
- 2
app/db/models/user.py View File

@@ -1,10 +1,10 @@
# PostgreSQL table model for users # PostgreSQL table model for users
from sqlalchemy import Column, Integer, String, DateTime
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey
from sqlalchemy.orm import relationship
from db.base import Base from db.base import Base




class User(Base): class User(Base):
__tablename__ = "users"
Id = Column(Integer, primary_key=True, index=True) Id = Column(Integer, primary_key=True, index=True)
Name = Column(String, nullable=False) Name = Column(String, nullable=False)
MiddleName = Column(String, nullable=True) MiddleName = Column(String, nullable=True)
@@ -14,4 +14,6 @@ class User(Base):
Email = Column(String, nullable=False) Email = Column(String, nullable=False)
Role = Column(String, nullable=False) Role = Column(String, nullable=False)
DrivingLicenseNumber = Column(String, nullable=True) DrivingLicenseNumber = Column(String, nullable=True)
AssignedVehicle = Column(Integer, ForeignKey("vehicle.Id"), nullable=True)
vehicle = relationship("Vehicle", back_populates="driver")
HashedPassword = Column(String, nullable=False) HashedPassword = Column(String, nullable=False)

+ 4
- 3
app/db/models/vehicle.py View File

@@ -5,18 +5,19 @@ from sqlalchemy import (
String, String,
ARRAY, ARRAY,
) )
from sqlalchemy.orm import relationship
from db.base import Base from db.base import Base




class Vehicle(Base): class Vehicle(Base):
__tablename__ = "vehicles"
Id = Column(Integer, primary_key=True, index=True) Id = Column(Integer, primary_key=True, index=True)
Model = Column(String, nullable=False) Model = Column(String, nullable=False)
Year = Column(Integer, nullable=False) Year = Column(Integer, nullable=False)
LicensePlate = Column(String, nullable=False) LicensePlate = Column(String, nullable=False)
Type = Column(String, nullable=False)
AssignedDriverIds = Column(ARRAY(Integer), nullable=True)
CurrentLocation = Column(ARRAY(String), nullable=True) CurrentLocation = Column(ARRAY(String), nullable=True)
Fuel = Column(Integer, nullable=False) Fuel = Column(Integer, nullable=False)
Mileage = Column(Integer, nullable=False) Mileage = Column(Integer, nullable=False)
Status = Column(String, nullable=False)
Capacity = Column(Integer, nullable=False)
MaintenanceNotes = Column(ARRAY(String), nullable=True) MaintenanceNotes = Column(ARRAY(String), nullable=True)
driver = relationship("User", back_populates="vehicle")

+ 7
- 0
app/db/repository/user.py View File

@@ -47,6 +47,13 @@ def verify_driver_exists(driver_id: int, db: Session):
return True return True




def get_car_driver(vehicle_id: int, db: Session):
driver = db.query(User).filter(User.AssignedVehicle == vehicle_id).first()
if not driver:
return False
return driver


def list_users(db: Session, role: str = "Any"): def list_users(db: Session, role: str = "Any"):
users = db.query(User).filter((User.Role == role) | (role == "Any")).all() users = db.query(User).filter((User.Role == role) | (role == "Any")).all()
return users return users

+ 47
- 22
app/db/repository/vehicle.py View File

@@ -1,51 +1,72 @@
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from schemas.vehicle import CreateVehicle, UpdateVehicle
from schemas.vehicle import CreateVehicle, UpdateVehicle, VehicleLocation
from db.models.vehicle import Vehicle from db.models.vehicle import Vehicle
from db.repository.user import verify_driver_exists
from db.base import User
from db.repository.user import get_car_driver




def create_new_vehicle(vehicle: CreateVehicle, db: Session): def create_new_vehicle(vehicle: CreateVehicle, db: Session):
vehicle_object = Vehicle( vehicle_object = Vehicle(
**vehicle.model_dump(), **vehicle.model_dump(),
Fuel=0, Fuel=0,
AssignedDriverIds=[],
Status="Inactive",
CurrentLocation=[], CurrentLocation=[],
MaintenanceNotes=[] MaintenanceNotes=[]
) )
db.add(vehicle_object) db.add(vehicle_object)
db.commit() db.commit()
db.refresh(vehicle_object) db.refresh(vehicle_object)
return vehicle
return vehicle_object


def update_vehicle_geoloc(vehicle_id: int, location: VehicleLocation, db: Session):
vehicle_db = db.query(Vehicle).filter(Vehicle.Id == vehicle_id)
vehicle_object = vehicle_db.first()
if not vehicle_object:
return "vehiclenotfound"
print("Location: " + str(location.CurrentLocation))
vehicle_object.CurrentLocation = location.CurrentLocation
db.add(vehicle_object)
db.commit()
db.refresh(vehicle_object)
return vehicle_object




def assign_vehicle_driver(vehicle_id: int, driver_id: int, db: Session): def assign_vehicle_driver(vehicle_id: int, driver_id: int, db: Session):
vehicledb = db.query(Vehicle).filter(Vehicle.Id == vehicle_id)
vehicle = vehicledb.first()
driver = (
db.query(User).filter((User.Id == driver_id) & (User.Role == "Driver")).first()
)
print(driver)
vehicle = db.query(Vehicle).filter(Vehicle.Id == vehicle_id).first()
if not vehicle: if not vehicle:
return "novehicle" 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"
if not driver:
return "nodriver"
driver.AssignedVehicle = vehicle_id
db.add(driver)
db.commit()
return vehicle




def list_vehicles(db: Session): def list_vehicles(db: Session):
vehicles = db.query(Vehicle).all() vehicles = db.query(Vehicle).all()
# assign AssignedDriver to all vehicles based on their drivers
for vehicle in vehicles:
driver = get_car_driver(vehicle.Id, db)
if driver:
vehicle.AssignedDriver = driver.Id
else:
vehicle.AssignedDriver = None
return vehicles return vehicles




def get_vehicle_by_id(vehicle_id: int, db: Session): def get_vehicle_by_id(vehicle_id: int, db: Session):
vehicle = db.query(Vehicle).filter(Vehicle.Id == vehicle_id).first() vehicle = db.query(Vehicle).filter(Vehicle.Id == vehicle_id).first()
driver = get_car_driver(vehicle.Id, db)
if driver:
vehicle.AssignedDriver = driver.Id
else:
vehicle.AssignedDriver = None
return vehicle return vehicle




@@ -54,18 +75,22 @@ def replace_vehicle_data(id: int, vehicle: UpdateVehicle, db: Session):
vehicle_object = vehicle_db.first() vehicle_object = vehicle_db.first()
if not vehicle_object: if not vehicle_object:
return "vehiclenotfound" return "vehiclenotfound"
vehicle_object.AssignedDriverIds = vehicle.AssignedDriverIds
vehicle_object.CurrentLocation = vehicle.CurrentLocation vehicle_object.CurrentLocation = vehicle.CurrentLocation
vehicle_object.Fuel = vehicle.Fuel vehicle_object.Fuel = vehicle.Fuel
vehicle_object.LicensePlate = vehicle.LicensePlate vehicle_object.LicensePlate = vehicle.LicensePlate
vehicle_object.MaintenanceNotes = vehicle.MaintenanceNotes vehicle_object.MaintenanceNotes = vehicle.MaintenanceNotes
vehicle_object.Mileage = vehicle.Mileage vehicle_object.Mileage = vehicle.Mileage
vehicle_object.Model = vehicle.Model vehicle_object.Model = vehicle.Model
vehicle_object.Type = vehicle.Type
vehicle_object.Status = vehicle.Status
res = assign_vehicle_driver(id, vehicle.AssignedDriver, db)
if res == "nodriver":
return "driverNotFound"
vehicle_object.Capacity = vehicle.Capacity
vehicle_object.Year = vehicle.Year vehicle_object.Year = vehicle.Year
print(vehicle_object) print(vehicle_object)
db.add(vehicle_object) db.add(vehicle_object)
db.commit() db.commit()
vehicle_object.AssignedDriver = vehicle.AssignedDriver
return vehicle_object return vehicle_object






+ 6
- 2
app/main.py View File

@@ -1,5 +1,5 @@
from fastapi import FastAPI from fastapi import FastAPI
from core.config import settings
from core.config import settings, createAdminAcc
from db.session import engine from db.session import engine
from db.base import Base from db.base import Base
from apis.base import api_router from apis.base import api_router
@@ -10,8 +10,12 @@ def include_routes(app): # include all routes from our api/v1/




def startup(): # start the project, and create the tables def startup(): # start the project, and create the tables
app = FastAPI(title=settings.PROJECT_NAME, version=settings.PROJECT_VERSION)
app = FastAPI(
title=settings.PROJECT_NAME,
version=settings.PROJECT_VERSION,
)
Base.metadata.create_all(bind=engine) Base.metadata.create_all(bind=engine)
createAdminAcc()
include_routes(app) include_routes(app)
return app return app




+ 5
- 0
app/schemas/token.py View File

@@ -4,3 +4,8 @@ from pydantic import BaseModel
class Token(BaseModel): class Token(BaseModel):
access_token: str access_token: str
token_type: str token_type: str


class TokenPayload(BaseModel):
sub: str = None
exp: int = None

+ 4
- 2
app/schemas/user.py View File

@@ -8,7 +8,7 @@ class UserCreate(BaseModel):
Email: EmailStr Email: EmailStr
Password: str = Field(..., min_length=7, max_length=20) Password: str = Field(..., min_length=7, max_length=20)
Name: str = Field(..., min_length=3, max_length=50) Name: str = Field(..., min_length=3, max_length=50)
MiddleName: str = Field(None, min_length=3, max_length=50)
MiddleName: str = Field(None)
LastName: str = Field(..., min_length=3, max_length=50) LastName: str = Field(..., min_length=3, max_length=50)
ContactNumber: str = Field(..., min_length=12, max_length=12) ContactNumber: str = Field(..., min_length=12, max_length=12)
BirthDate: datetime = Field(...) BirthDate: datetime = Field(...)
@@ -19,12 +19,14 @@ class UserCreate(BaseModel):
class ShowUser(BaseModel): class ShowUser(BaseModel):
Id: int Id: int
Name: str Name: str
MiddleName: str
MiddleName: str | None
LastName: str LastName: str
ContactNumber: str ContactNumber: str
BirthDate: datetime BirthDate: datetime
Email: EmailStr Email: EmailStr
Role: str Role: str
AssignedVehicle: int | None


class Config: class Config:
orm_mode = True orm_mode = True
validate_assignment = True

+ 11
- 6
app/schemas/vehicle.py View File

@@ -3,12 +3,11 @@ from pydantic import BaseModel




class CreateVehicle(BaseModel): class CreateVehicle(BaseModel):
Id: int
Model: str Model: str
Year: int Year: int
LicensePlate: str LicensePlate: str
Type: str
Mileage: int Mileage: int
Capacity: int




class OutputVehicle(BaseModel): class OutputVehicle(BaseModel):
@@ -16,21 +15,27 @@ class OutputVehicle(BaseModel):
Model: str Model: str
Year: int Year: int
LicensePlate: str LicensePlate: str
Type: str
Mileage: int Mileage: int
CurrentLocation: Optional[list[str]] = None CurrentLocation: Optional[list[str]] = None
Fuel: Optional[int] = 0 Fuel: Optional[int] = 0
MaintenanceNotes: Optional[list[str]] = None MaintenanceNotes: Optional[list[str]] = None
AssignedDriverIds: Optional[list[int]] = None
AssignedDriver: Optional[int] = None
Capacity: int
Status: str




class UpdateVehicle(BaseModel): class UpdateVehicle(BaseModel):
Model: str Model: str
Year: int Year: int
LicensePlate: str LicensePlate: str
Type: str
Capacity: int
Mileage: int Mileage: int
Status: str
CurrentLocation: Optional[list[str]] = None CurrentLocation: Optional[list[str]] = None
Fuel: Optional[int] = 0 Fuel: Optional[int] = 0
MaintenanceNotes: Optional[list[str]] = None MaintenanceNotes: Optional[list[str]] = None
AssignedDriverIds: Optional[list[int]] = None
AssignedDriver: Optional[int] = None


class VehicleLocation(BaseModel):
CurrentLocation: list[str]

Loading…
Cancel
Save