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 #76 from SELab-2/backend
Browse files Browse the repository at this point in the history
Add more routes
  • Loading branch information
msathieu authored Mar 12, 2024
2 parents 9a21c41 + 7b3a36d commit 4772015
Show file tree
Hide file tree
Showing 22 changed files with 331 additions and 119 deletions.
37 changes: 32 additions & 5 deletions backend/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
from starlette.requests import Request
from starlette.responses import JSONResponse

from db.errors.database_errors import ActionAlreadyPerformedError, ItemNotFoundError
from routes.errors.authentication import InvalidRoleCredentialsError, StudentNotEnrolledError
from db.errors.database_errors import ActionAlreadyPerformedError, ItemNotFoundError, NoSuchRelationError
from routes.errors.authentication import InvalidRoleCredentialsError, NoAccessToSubjectError
from routes.group import group_router
from routes.project import project_router
from routes.student import student_router
from routes.subject import subject_router
Expand All @@ -20,7 +21,25 @@
app.include_router(users_router, prefix="/api")
app.include_router(project_router, prefix="/api")
app.include_router(subject_router, prefix="/api")
app.include_router(group_router, prefix="/api")

DEBUG = False # Should always be false in repo

if DEBUG:
from fastapi.middleware.cors import CORSMiddleware
origins = [
"https://localhost",
"https://localhost:8080",
"http://localhost:5173",
]

app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)

# Koppel de exception handlers
@app.exception_handler(InvalidRoleCredentialsError)
Expand All @@ -39,10 +58,10 @@ def item_not_found_error_handler(request: Request, exc: ItemNotFoundError) -> JS
)


@app.exception_handler(StudentNotEnrolledError)
def student_already_enrolled_error_handler(request: Request, exc: StudentNotEnrolledError) -> JSONResponse:
@app.exception_handler(NoAccessToSubjectError)
def no_access_to_subject_error_handler(request: Request, exc: NoAccessToSubjectError) -> JSONResponse:
return JSONResponse(
status_code=status.HTTP_400_BAD_REQUEST,
status_code=status.HTTP_403_FORBIDDEN,
content={"detail": str(exc)},
)

Expand All @@ -55,5 +74,13 @@ def action_already_performed_error_handler(request: Request, exc: ActionAlreadyP
)


@app.exception_handler(NoSuchRelationError)
def no_such_relation_error_handler(request: Request, exc: NoSuchRelationError) -> JSONResponse:
return JSONResponse(
status_code=status.HTTP_400_BAD_REQUEST,
content={"detail": str(exc)},
)


if __name__ == "__main__":
uvicorn.run("app:app")
6 changes: 5 additions & 1 deletion backend/db/errors/database_errors.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
class ItemNotFoundError(Exception):

def __init__(self, message: str) -> None:
super().__init__(message)


class ActionAlreadyPerformedError(Exception):
def __init__(self, message: str) -> None:
super().__init__(message)


class NoSuchRelationError(Exception):
def __init__(self, message: str) -> None:
super().__init__(message)
15 changes: 9 additions & 6 deletions backend/db/models/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,13 @@ def to_domain_model(self) -> UserDataclass:


@dataclass()
class Admin(User):
class Admin(Base, AbstractModel):
__tablename__ = "admins"
id: Mapped[int] = mapped_column(ForeignKey(User.id), primary_key=True)
user: Mapped[User] = relationship()

def to_domain_model(self) -> AdminDataclass:
return AdminDataclass(id=self.id, name=self.name, email=self.email)
return AdminDataclass(id=self.id, name=self.user.name, email=self.user.email)


teachers_subjects = Table(
Expand All @@ -68,25 +69,27 @@ def to_domain_model(self) -> AdminDataclass:


@dataclass()
class Teacher(User):
class Teacher(Base, AbstractModel):
__tablename__ = "teachers"
id: Mapped[int] = mapped_column(ForeignKey(User.id), primary_key=True)
user: Mapped[User] = relationship()
subjects: Mapped[list["Subject"]] = relationship(secondary=teachers_subjects, back_populates="teachers")

def to_domain_model(self) -> TeacherDataclass:
return TeacherDataclass(id=self.id, name=self.name, email=self.email)
return TeacherDataclass(id=self.id, name=self.user.name, email=self.user.email)


@dataclass()
class Student(User):
class Student(Base, AbstractModel):
__tablename__ = "students"
id: Mapped[int] = mapped_column(ForeignKey(User.id), primary_key=True)
user: Mapped[User] = relationship()
subjects: Mapped[list["Subject"]] = relationship(secondary=students_subjects, back_populates="students")
groups: Mapped[list["Group"]] = relationship(secondary=students_groups, back_populates="students")
submissions: Mapped[list["Submission"]] = relationship(back_populates="student")

def to_domain_model(self) -> StudentDataclass:
return StudentDataclass(id=self.id, name=self.name, email=self.email)
return StudentDataclass(id=self.id, name=self.user.name, email=self.user.email)


@dataclass()
Expand Down
8 changes: 5 additions & 3 deletions backend/domain/logic/admin.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
from sqlalchemy.orm import Session

from db.models.models import Admin
from db.models.models import Admin, User
from domain.logic.basic_operations import get, get_all
from domain.models.AdminDataclass import AdminDataclass


def create_admin(session: Session, name: str, email: str) -> AdminDataclass:
new_admin: Admin = Admin(name=name, email=email)
new_user: User = User(name=name, email=email)
session.add(new_user)
session.commit()
new_admin: Admin = Admin(id=new_user.id)
session.add(new_admin)
session.commit()
return new_admin.to_domain_model()
Expand All @@ -20,7 +23,6 @@ def get_all_admins(session: Session) -> list[AdminDataclass]:
return [admin.to_domain_model() for admin in get_all(session, Admin)]



def is_user_admin(session: Session, user_id: int) -> bool:
admin = session.get(Admin, user_id)
return admin is not None
15 changes: 13 additions & 2 deletions backend/domain/logic/group.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@

from sqlalchemy.orm import Session

from db.errors.database_errors import ActionAlreadyPerformedError
from db.errors.database_errors import ActionAlreadyPerformedError, NoSuchRelationError
from db.models.models import Group, Project, Student
from domain.logic.basic_operations import get, get_all
from domain.models.GroupDataclass import GroupDataclass
Expand Down Expand Up @@ -51,6 +50,18 @@ def add_student_to_group(session: Session, student_id: int, group_id: int) -> No
session.commit()


def remove_student_from_group(session: Session, student_id: int, group_id: int) -> None:
student: Student = get(session, Student, ident=student_id)
group: Group = get(session, Group, ident=group_id)

if student not in group.students:
msg = f"Student with id {student_id} is not in group with id {group_id}"
raise NoSuchRelationError(msg)

group.students.remove(student)
session.commit()


def get_students_of_group(session: Session, group_id: int) -> list[StudentDataclass]:
group: Group = get(session, Group, ident=group_id)
students: list[Student] = group.students
Expand Down
37 changes: 27 additions & 10 deletions backend/domain/logic/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,21 @@

from sqlalchemy.orm import Session

from db.models.models import Project, Subject
from db.models.models import Project, Student, Subject, Teacher
from domain.logic.basic_operations import get, get_all
from domain.models.ProjectDataclass import ProjectDataclass


def create_project(
session: Session,
subject_id: int,
name: str,
deadline: datetime,
archived: bool,
description: str,
requirements: str,
visible: bool,
max_students: int,
session: Session,
subject_id: int,
name: str,
deadline: datetime,
archived: bool,
description: str,
requirements: str,
visible: bool,
max_students: int,
) -> ProjectDataclass:
subject: Subject = get(session, Subject, subject_id)

Expand Down Expand Up @@ -49,3 +49,20 @@ def get_projects_of_subject(session: Session, subject_id: int) -> list[ProjectDa
projects: list[Project] = subject.projects
return [project.to_domain_model() for project in projects]


def get_projects_of_student(session: Session, user_id: int) -> list[ProjectDataclass]:
student = get(session, Student, ident=user_id)
subjects = student.subjects
projects = []
for i in subjects:
projects += i.projects
return [project.to_domain_model() for project in projects]


def get_projects_of_teacher(session: Session, user_id: int) -> list[ProjectDataclass]:
teacher = get(session, Teacher, ident=user_id)
subjects = teacher.subjects
projects = []
for i in subjects:
projects += i.projects
return [project.to_domain_model() for project in projects]
7 changes: 5 additions & 2 deletions backend/domain/logic/student.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
from sqlalchemy.orm import Session

from db.models.models import Student
from db.models.models import Student, User
from domain.logic.basic_operations import get, get_all
from domain.models.StudentDataclass import StudentDataclass


def create_student(session: Session, name: str, email: str) -> StudentDataclass:
new_student: Student = Student(name=name, email=email)
new_user: User = User(name=name, email=email)
session.add(new_user)
session.commit()
new_student: Student = Student(id=new_user.id)
session.add(new_student)
session.commit()
return new_student.to_domain_model()
Expand Down
13 changes: 13 additions & 0 deletions backend/domain/logic/subject.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from db.errors.database_errors import ActionAlreadyPerformedError
from db.models.models import Student, Subject, Teacher
from domain.logic.basic_operations import get, get_all
from domain.logic.student import is_user_student
from domain.logic.teacher import is_user_teacher
from domain.models.SubjectDataclass import SubjectDataclass


Expand Down Expand Up @@ -55,3 +57,14 @@ def get_subjects_of_student(session: Session, student_id: int) -> list[SubjectDa
student: Student = get(session, Student, ident=student_id)
subjects: list[Subject] = student.subjects
return [vak.to_domain_model() for vak in subjects]


def is_user_authorized_for_subject(subject_id: int, session: Session, uid: int) -> bool:
subjects = []
if is_user_teacher(session, uid):
subjects += get_subjects_of_teacher(session, uid)
if is_user_student(session, uid):
subjects += get_subjects_of_student(session, uid)
if subject_id in [subject.id for subject in subjects]:
return True
return False
7 changes: 5 additions & 2 deletions backend/domain/logic/teacher.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
from sqlalchemy.orm import Session

from db.models.models import Teacher
from db.models.models import Teacher, User
from domain.logic.basic_operations import get, get_all
from domain.models.TeacherDataclass import TeacherDataclass


def create_teacher(session: Session, name: str, email: str) -> TeacherDataclass:
new_teacher: Teacher = Teacher(name=name, email=email)
new_user: User = User(name=name, email=email)
session.add(new_user)
session.commit()
new_teacher: Teacher = Teacher(id=new_user.id)
session.add(new_teacher)
session.commit()
return new_teacher.to_domain_model()
Expand Down
19 changes: 18 additions & 1 deletion backend/domain/logic/user.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from sqlalchemy.orm import Session

from db.models.models import User
from db.models.models import Admin, Student, Teacher, User
from domain.logic.admin import is_user_admin
from domain.logic.basic_operations import get, get_all
from domain.logic.role_enum import Role
Expand Down Expand Up @@ -31,3 +31,20 @@ def get_user(session: Session, user_id: int) -> UserDataclass:

def get_all_users(session: Session) -> list[UserDataclass]:
return [user.to_domain_model() for user in get_all(session, User)]


def modify_user_roles(session: Session, uid: int, roles: list[Role]) -> None:
# Er is geen ondersteuning om een student/teacher role af te nemen,
# want dit zou problemen geven met relaties in de databank
if Role.STUDENT in roles and session.get(Student, uid) is None:
student = Student(id=uid)
session.add(student)
if Role.TEACHER in roles and session.get(Teacher, uid) is None:
teacher = Teacher(id=uid)
session.add(teacher)
if Role.ADMIN in roles and session.get(Admin, uid) is None:
admin = Admin(id=uid)
session.add(admin)
if Role.ADMIN not in roles and session.get(Admin, uid) is not None:
session.delete(get(session, Admin, uid))
session.commit()
10 changes: 10 additions & 0 deletions backend/domain/models/ProjectDataclass.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,13 @@ class ProjectDataclass(BaseModel):
visible: bool
max_students: PositiveInt
subject_id: int


class ProjectInput(BaseModel):
name: str
deadline: datetime
archived: bool
description: str
requirements: str
visible: bool
max_students: PositiveInt
4 changes: 4 additions & 0 deletions backend/domain/models/SubjectDataclass.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@
class SubjectDataclass(BaseModel):
id: int
name: str


class SubjectInput(BaseModel):
name: str
4 changes: 3 additions & 1 deletion backend/fill_database_mock.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from domain.logic.group import add_student_to_group, create_group
from domain.logic.project import create_project
from domain.logic.student import create_student
from domain.logic.subject import add_teacher_to_subject, create_subject
from domain.logic.subject import add_student_to_subject, add_teacher_to_subject, create_subject
from domain.logic.teacher import create_teacher

if __name__ == "__main__":
Expand Down Expand Up @@ -83,6 +83,8 @@
add_teacher_to_subject(session, teacher2.id, algoritmen.id)
add_teacher_to_subject(session, teacher3.id, webtech.id)

add_student_to_subject(session, student1.id, objeprog.id)

# Add students to groups
add_student_to_group(session, student1.id, groep1_objprog.id)
add_student_to_group(session, student2.id, groep1_objprog.id)
Expand Down
Loading

0 comments on commit 4772015

Please sign in to comment.