Skip to content
This repository has been archived by the owner on Sep 27, 2024. It is now read-only.

Commit

Permalink
Merge pull request #41 from SELab-2/add-routes
Browse files Browse the repository at this point in the history
Add routes
  • Loading branch information
msathieu authored Mar 5, 2024
2 parents b7fcd09 + 56772a1 commit f32147b
Show file tree
Hide file tree
Showing 21 changed files with 315 additions and 45 deletions.
8 changes: 6 additions & 2 deletions backend/app.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import uvicorn
from fastapi import FastAPI

from routes.teachers import teachers_router
from routes.projects import projects_router
from routes.subjects import subjects_router
from routes.users import users_router

app = FastAPI()

# Koppel routes uit andere modules.
app.include_router(teachers_router)
app.include_router(subjects_router, prefix="/api")
app.include_router(users_router, prefix="/api")
app.include_router(projects_router, prefix="/api")

if __name__ == "__main__":
uvicorn.run("app:app")
5 changes: 5 additions & 0 deletions backend/db/implementation/SqlAdminDAO.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,8 @@ def create_admin(self, name: str, email: str) -> AdminDataclass:
session.add(new_admin)
session.commit()
return new_admin.to_domain_model()

def is_user_admin(self, user_id: int) -> bool:
with Session(engine) as session:
admin = session.get(Admin, user_id)
return admin is not None
43 changes: 43 additions & 0 deletions backend/db/implementation/SqlDAOProvider.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from db.implementation.SqlAdminDAO import SqlAdminDAO
from db.implementation.SqlGroupDAO import SqlGroupDAO
from db.implementation.SqlProjectDAO import SqlProjectDAO
from db.implementation.SqlStudentDAO import SqlStudentDAO
from db.implementation.SqlSubjectDAO import SqlSubjectDAO
from db.implementation.SqlSubmissionDAO import SqlSubmissionDAO
from db.implementation.SqlTeacherDAO import SqlTeacherDAO
from db.implementation.SqlUserDAO import SqlUserDAO
from db.interface.AdminDAO import AdminDAO
from db.interface.DAOProvider import DAOProvider
from db.interface.GroupDAO import GroupDAO
from db.interface.ProjectDAO import ProjectDAO
from db.interface.StudentDAO import StudentDAO
from db.interface.SubjectDAO import SubjectDAO
from db.interface.SubmissionDAO import SubmissionDAO
from db.interface.TeacherDAO import TeacherDAO
from db.interface.UserDAO import UserDAO


class SqlDAOProvider(DAOProvider):
def get_admin_dao(self) -> AdminDAO:
return SqlAdminDAO()

def get_group_dao(self) -> GroupDAO:
return SqlGroupDAO()

def get_project_dao(self) -> ProjectDAO:
return SqlProjectDAO()

def get_student_dao(self) -> StudentDAO:
return SqlStudentDAO()

def get_subject_dao(self) -> SubjectDAO:
return SqlSubjectDAO()

def get_submission_dao(self) -> SubmissionDAO:
return SqlSubmissionDAO()

def get_teacher_dao(self) -> TeacherDAO:
return SqlTeacherDAO()

def get_user_dao(self) -> UserDAO:
return SqlUserDAO()
5 changes: 5 additions & 0 deletions backend/db/implementation/SqlStudentDAO.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,8 @@ def create_student(self, name: str, email: str) -> StudentDataclass:
session.add(new_student)
session.commit()
return new_student.to_domain_model()

def is_user_student(self, user_id: int) -> bool:
with Session(engine) as session:
student = session.get(Student, user_id)
return student is not None
5 changes: 5 additions & 0 deletions backend/db/implementation/SqlTeacherDAO.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,8 @@ def create_teacher(self, name: str, email: str) -> TeacherDataclass:
session.add(new_teacher)
session.commit()
return new_teacher.to_domain_model()

def is_user_teacher(self, user_id: int) -> bool:
with Session(engine) as session:
teacher = session.get(Teacher, user_id)
return teacher is not None
4 changes: 4 additions & 0 deletions backend/db/interface/AdminDAO.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ def create_admin(self, name: str, email: str) -> AdminDataclass:
:return: De nieuwe admin
"""
raise NotImplementedError

@abstractmethod
def is_user_admin(self, user_id: int) -> bool:
raise NotImplementedError
44 changes: 44 additions & 0 deletions backend/db/interface/DAOProvider.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from abc import ABC, abstractmethod

from db.interface.AdminDAO import AdminDAO
from db.interface.GroupDAO import GroupDAO
from db.interface.ProjectDAO import ProjectDAO
from db.interface.StudentDAO import StudentDAO
from db.interface.SubjectDAO import SubjectDAO
from db.interface.SubmissionDAO import SubmissionDAO
from db.interface.TeacherDAO import TeacherDAO
from db.interface.UserDAO import UserDAO


class DAOProvider(ABC):
@abstractmethod
def get_admin_dao(self) -> AdminDAO:
raise NotImplementedError

@abstractmethod
def get_group_dao(self) -> GroupDAO:
raise NotImplementedError

@abstractmethod
def get_project_dao(self) -> ProjectDAO:
raise NotImplementedError

@abstractmethod
def get_student_dao(self) -> StudentDAO:
raise NotImplementedError

@abstractmethod
def get_subject_dao(self) -> SubjectDAO:
raise NotImplementedError

@abstractmethod
def get_submission_dao(self) -> SubmissionDAO:
raise NotImplementedError

@abstractmethod
def get_teacher_dao(self) -> TeacherDAO:
raise NotImplementedError

@abstractmethod
def get_user_dao(self) -> UserDAO:
raise NotImplementedError
4 changes: 4 additions & 0 deletions backend/db/interface/StudentDAO.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ def create_student(self, name: str, email: str) -> StudentDataclass:
:returns: De nieuw aangemaakte student
"""
raise NotImplementedError

@abstractmethod
def is_user_student(self, user_id: int) -> bool:
raise NotImplementedError
10 changes: 8 additions & 2 deletions backend/db/interface/SubmissionDAO.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,14 @@

class SubmissionDAO(AbstractDAO[Submission, SubmissionDataclass]):
@abstractmethod
def create_submission(self, student_id: int, group_id: int, message: str,
state: SubmissionState, date_time: datetime) -> SubmissionDataclass:
def create_submission(
self,
student_id: int,
group_id: int,
message: str,
state: SubmissionState,
date_time: datetime,
) -> SubmissionDataclass:
"""
Creëert een nieuw SubmissionDataClass in de database en associeert het met een StudentDataclass en een
GroupDataClass.
Expand Down
4 changes: 4 additions & 0 deletions backend/db/interface/TeacherDAO.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ def create_teacher(self, name: str, email: str) -> TeacherDataclass:
:returns: De nieuw aangemaakte teacher.
"""
raise NotImplementedError

@abstractmethod
def is_user_teacher(self, user_id: int) -> bool:
raise NotImplementedError
1 change: 0 additions & 1 deletion backend/db/interface/UserDAO.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

from db.interface.AbstractDAO import AbstractDAO
from db.models.models import User
from domain.models.UserDataclass import UserDataclass
Expand Down
17 changes: 17 additions & 0 deletions backend/domain/logic/SubjectLogic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from db.interface.DAOProvider import DAOProvider
from domain.models.SubjectDataclass import SubjectDataclass
from domain.models.UserDataclass import UserDataclass


def is_user_authorized_for_subject(subject: SubjectDataclass, user: UserDataclass, dao_provider: DAOProvider) -> bool:
teacher_dao = dao_provider.get_teacher_dao()
student_dao = dao_provider.get_student_dao()
subject_dao = dao_provider.get_subject_dao()

if teacher_dao.is_user_teacher(user.id) and subject in subject_dao.get_subjects_of_teacher(user.id):
return True

if student_dao.is_user_student(user.id) and subject in subject_dao.get_subjects_of_student(user.id):
return True

return False
18 changes: 18 additions & 0 deletions backend/domain/logic/UserLogic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from db.interface.DAOProvider import DAOProvider
from domain.models.APIUser import APIUser
from domain.models.UserDataclass import UserDataclass


def convert_user(user: UserDataclass, dao_provider: DAOProvider) -> APIUser:
api_user = APIUser(id=user.id, name=user.name, email=user.email, roles=[])

if dao_provider.get_teacher_dao().is_user_teacher(user.id):
api_user.roles.append("teacher")

if dao_provider.get_admin_dao().is_user_admin(user.id):
api_user.roles.append("admin")

if dao_provider.get_student_dao().is_user_student(user.id):
api_user.roles.append("student")

return api_user
8 changes: 8 additions & 0 deletions backend/domain/models/APIUser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from pydantic import BaseModel, EmailStr


class APIUser(BaseModel):
id: int
name: str
email: EmailStr
roles: list[str]
6 changes: 4 additions & 2 deletions backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,9 @@ ignore = [
"TCH001", # Move application import `...` into a type-checking block (dit is enkel voor performance)
"RUF009", # Do not perform function call `...` in dataclass defaults but needed for sql alchemy
"PLR0913", # Too many arguments in function
"FBT001" # Boolean-typed positional argument in function dfinition
"FBT001", # Boolean-typed positional argument in function dfinition
"FBT002",
"B008"
]

# Allow fix for all enabled rules (when `--fix`) is provided.
Expand Down Expand Up @@ -125,4 +127,4 @@ docstring-code-format = false
#
# This only has an effect when the `docstring-code-format` setting is
# enabled.
docstring-code-line-length = "dynamic"
docstring-code-line-length = "dynamic"
6 changes: 6 additions & 0 deletions backend/routes/db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from db.implementation.SqlDAOProvider import SqlDAOProvider
from db.interface.DAOProvider import DAOProvider


def get_dao_provider() -> DAOProvider:
return SqlDAOProvider()
12 changes: 12 additions & 0 deletions backend/routes/login.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from domain.models.UserDataclass import UserDataclass
from routes.db import get_dao_provider


def get_authenticated_user() -> UserDataclass:
return get_dao_provider().get_user_dao().get(1) # Actually authenticate user


def is_user_admin() -> bool:
user = get_authenticated_user()
admin_dao = get_dao_provider().get_admin_dao()
return admin_dao.is_user_admin(user.id)
42 changes: 42 additions & 0 deletions backend/routes/projects.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from fastapi import APIRouter, HTTPException, status

from db.errors.database_errors import ItemNotFoundError
from domain.logic.SubjectLogic import is_user_authorized_for_subject
from domain.models.ProjectDataclass import ProjectDataclass
from routes.db import get_dao_provider
from routes.login import get_authenticated_user

projects_router = APIRouter()


@projects_router.get("/projects")
def get_subjects(teacher: bool = False) -> list[ProjectDataclass]:
user = get_authenticated_user()
project_dao = get_dao_provider().get_project_dao()
subject_dao = get_dao_provider().get_subject_dao()
try:
if teacher:
subjects = subject_dao.get_subjects_of_teacher(user.id)
else:
subjects = subject_dao.get_subjects_of_student(user.id)
projects = []
for i in subjects:
projects += project_dao.get_projects_of_subject(i.id)
except ItemNotFoundError as err:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) from err
return projects


@projects_router.get("/projects/{project_id}")
def get_project(project_id: int) -> ProjectDataclass:
project_dao = get_dao_provider().get_project_dao()
subject_dao = get_dao_provider().get_subject_dao()
user = get_authenticated_user()
try:
project = project_dao.get(project_id)
except ItemNotFoundError as err:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) from err
subject = subject_dao.get(project.subject_id)
if not is_user_authorized_for_subject(subject, user, get_dao_provider()):
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN)
return project
44 changes: 44 additions & 0 deletions backend/routes/subjects.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from fastapi import APIRouter, HTTPException, status

from db.errors.database_errors import ItemNotFoundError
from domain.logic.SubjectLogic import is_user_authorized_for_subject
from domain.models.ProjectDataclass import ProjectDataclass
from domain.models.SubjectDataclass import SubjectDataclass
from routes.db import get_dao_provider
from routes.login import get_authenticated_user

subjects_router = APIRouter()


@subjects_router.get("/subjects")
def get_subjects(teacher: bool = False) -> list[SubjectDataclass]:
user = get_authenticated_user()
subject_dao = get_dao_provider().get_subject_dao()
try:
if teacher:
return subject_dao.get_subjects_of_teacher(user.id)
return subject_dao.get_subjects_of_student(user.id)
except ItemNotFoundError as err:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) from err


@subjects_router.get("/subjects/{subject_id}")
def get_subject(subject_id: int) -> SubjectDataclass:
subject_dao = get_dao_provider().get_subject_dao()
try:
return subject_dao.get(subject_id)
except ItemNotFoundError as err:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) from err


@subjects_router.get("/subjects/{subject_id}/projects")
def get_subject_projects(subject_id: int) -> list[ProjectDataclass]:
subject_dao = get_dao_provider().get_subject_dao()
project_dao = get_dao_provider().get_project_dao()
try:
subject = subject_dao.get(subject_id)
if not is_user_authorized_for_subject(subject, get_authenticated_user(), get_dao_provider()):
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN)
return project_dao.get_projects_of_subject(subject_id)
except ItemNotFoundError as err:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) from err
38 changes: 0 additions & 38 deletions backend/routes/teachers.py

This file was deleted.

Loading

0 comments on commit f32147b

Please sign in to comment.