From 5dcd569a2db789aa5e20f5ea22f9543635cbdb27 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Tue, 5 Mar 2024 21:45:00 +0100 Subject: [PATCH 01/16] omzetten vanuit DAO: groep #45 --- backend/domain/logic/group.py | 50 +++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 backend/domain/logic/group.py diff --git a/backend/domain/logic/group.py b/backend/domain/logic/group.py new file mode 100644 index 00000000..4f280946 --- /dev/null +++ b/backend/domain/logic/group.py @@ -0,0 +1,50 @@ +from typing import Type + +from sqlalchemy.orm import Session + +from db.errors.database_errors import UniqueConstraintError +from db.models.models import Project, Group, Student +from domain.logic.basic_operations import get +from domain.models.GroupDataclass import GroupDataclass +from domain.models.StudentDataclass import StudentDataclass + + +def create_group(session: Session, project_id: int) -> GroupDataclass: + project: Project = get(session, Type[Project], project_id) + new_group: Group = Group(project_id=project_id) + project.groups.append(new_group) + + session.add(new_group) + session.commit() + + return new_group.to_domain_model() + + +def get_groups_of_project(session: Session, project_id: int) -> list[GroupDataclass]: + project: Project = get(session, Type[Project], project_id) + groups: list[Group] = project.groups + return [group.to_domain_model() for group in groups] + + +def get_groups_of_student(session: Session, student_id: int) -> list[GroupDataclass]: + student: Student = get(session, Type[Student], ident=student_id) + groups: list[Group] = student.groups + return [group.to_domain_model() for group in groups] + + +def add_student_to_group(session: Session, student_id: int, group_id: int) -> None: + student: Student = get(session, Type[Student], ident=student_id) + group: Group = get(session, Type[Group], ident=group_id) + + if student in group.students: + msg = f"Student with id {student_id} already in group with id {group_id}" + raise UniqueConstraintError(msg) + + group.students.append(student) + session.commit() + + +def get_students_of_group(session: Session, group_id: int) -> list[StudentDataclass]: + group: Group = get(session, Type[Group], ident=group_id) + students: list[Student] = group.students + return [student.to_domain_model() for student in students] From 508fb2102d4c9693b913ac89bf21d6ba74d0bf08 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Tue, 5 Mar 2024 21:52:03 +0100 Subject: [PATCH 02/16] omzetten vanuit DAO: project #45 --- backend/domain/logic/project.py | 44 +++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 backend/domain/logic/project.py diff --git a/backend/domain/logic/project.py b/backend/domain/logic/project.py new file mode 100644 index 00000000..61ec971f --- /dev/null +++ b/backend/domain/logic/project.py @@ -0,0 +1,44 @@ +from datetime import datetime +from typing import Type + +from sqlalchemy.orm import Session + +from db.models.models import Subject, Project +from domain.logic.basic_operations import get +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, +) -> ProjectDataclass: + subject: Subject = get(session, Type[Subject], subject_id) + + new_project: Project = Project( + name=name, + deadline=deadline, + archived=archived, + description=description, + requirements=requirements, + visible=visible, + max_students=max_students, + ) + + subject.projects.append(new_project) + session.commit() + + return new_project.to_domain_model() + + +def get_projects_of_subject(session: Session, subject_id: int) -> list[ProjectDataclass]: + subject: Subject = get(session, Type[Subject], ident=subject_id) + projects: list[Project] = subject.projects + return [project.to_domain_model() for project in projects] + From e2495f434b24deede8ca1cc7a203cff816deb95b Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Tue, 5 Mar 2024 21:54:08 +0100 Subject: [PATCH 03/16] omzetten vanuit DAO: student #45 --- backend/domain/logic/student.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 backend/domain/logic/student.py diff --git a/backend/domain/logic/student.py b/backend/domain/logic/student.py new file mode 100644 index 00000000..a6b33583 --- /dev/null +++ b/backend/domain/logic/student.py @@ -0,0 +1,16 @@ +from sqlalchemy.orm import Session + +from db.models.models import Student +from domain.models.StudentDataclass import StudentDataclass + + +def create_student(session: Session, name: str, email: str) -> StudentDataclass: + new_student: Student = Student(name=name, email=email) + session.add(new_student) + session.commit() + return new_student.to_domain_model() + + +def is_user_student(session: Session, user_id: int) -> bool: + student = session.get(Student, user_id) + return student is not None From 2cf54430c119d5658c37ef35d4b34baa1cc7f87f Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Tue, 5 Mar 2024 22:01:29 +0100 Subject: [PATCH 04/16] omzetten vanuit DAO: subject #45 --- backend/domain/logic/subject.py | 66 +++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 backend/domain/logic/subject.py diff --git a/backend/domain/logic/subject.py b/backend/domain/logic/subject.py new file mode 100644 index 00000000..6a3420c3 --- /dev/null +++ b/backend/domain/logic/subject.py @@ -0,0 +1,66 @@ +from typing import Type + +from sqlalchemy.orm import Session + +from db.errors.database_errors import UniqueConstraintError +from db.models.models import Subject, Teacher, Student +from domain.logic.basic_operations import get +from domain.models.SubjectDataclass import SubjectDataclass +from domain.models.UserDataclass import UserDataclass + + +def is_user_authorized_for_subject(subject: SubjectDataclass, user: UserDataclass) -> bool: + teacher_dao = dao_provider.get_teacher_dao() + student_dao = dao_provider.get_student_dao() + subject_dao = dao_provider.get_subject_dao() # TODO + + 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 + + +def create_subject(session: Session, name: str) -> SubjectDataclass: + new_subject = Subject(name=name) + session.add(new_subject) + session.commit() + return new_subject.to_domain_model() + + +def get_subjects_of_teacher(session: Session, teacher_id: int) -> list[SubjectDataclass]: + teacher: Teacher = get(session, Type[Teacher], ident=teacher_id) + subjects: list[Subject] = teacher.subjects + return [vak.to_domain_model() for vak in subjects] + + +def add_student_to_subject(session: Session, student_id: int, subject_id: int) -> None: + student: Student = get(session, Type[Student], ident=student_id) + subject: Subject = get(session, Type[Subject], ident=subject_id) + + if subject in student.subjects: + msg = f"Student with id {student_id} already has subject with id {subject_id}" + raise UniqueConstraintError(msg) + + student.subjects.append(subject) + session.commit() + + +def add_teacher_to_subject(session: Session, teacher_id: int, subject_id: int) -> None: + teacher: Teacher | None = get(session, Type[Teacher], ident=teacher_id) + subject: Subject | None = get(session, Type[Subject], ident=subject_id) + + if subject in teacher.subjects: + msg = f"Teacher with id {teacher_id} already has subject with id {subject_id}" + raise UniqueConstraintError(msg) + + teacher.subjects.append(subject) + session.commit() + + +def get_subjects_of_student(session: Session, student_id: int) -> list[SubjectDataclass]: + student: Student = get(session, Type[Student], ident=student_id) + subjects: list[Subject] = student.subjects + return [vak.to_domain_model() for vak in subjects] From 3470b9397d0d23cc9314a9c4ac9ec7523a454e32 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Tue, 5 Mar 2024 22:09:16 +0100 Subject: [PATCH 05/16] omzetten vanuit DAO: submission #45 --- backend/domain/logic/submission.py | 43 ++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 backend/domain/logic/submission.py diff --git a/backend/domain/logic/submission.py b/backend/domain/logic/submission.py new file mode 100644 index 00000000..c4229ebe --- /dev/null +++ b/backend/domain/logic/submission.py @@ -0,0 +1,43 @@ +from datetime import datetime +from typing import Type + +from sqlalchemy.orm import Session + +from db.models.models import Student, Group, Submission +from domain.logic.basic_operations import get +from domain.models.SubmissionDataclass import SubmissionDataclass, SubmissionState + + +def create_submission( + session: Session, + student_id: int, + group_id: int, + message: str, + state: SubmissionState, + date_time: datetime +) -> SubmissionDataclass: + student: Student = get(session, Type[Student], ident=student_id) + group: Group = get(session, Type[Group], ident=group_id) + + new_submission: Submission = Submission( + student_id=student.id, + group_id=group.id, + message=message, + state=state, + date_time=date_time + ) + session.add(new_submission) + session.commit() + return new_submission.to_domain_model() + + +def get_submissions_of_student(session: Session, student_id: int) -> list[SubmissionDataclass]: + student: Student = get(session, Type[Student], ident=student_id) + submissions: list[Submission] = student.submissions + return [submission.to_domain_model() for submission in submissions] + + +def get_submissions_of_group(session: Session, group_id: int) -> list[SubmissionDataclass]: + group: Group = get(session, Type[Group], ident=group_id) + submissions: list[Submission] = group.submissions + return [submission.to_domain_model() for submission in submissions] From 885f89575630e08f17cb0c5f83573f55436c1c3e Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Tue, 5 Mar 2024 22:10:59 +0100 Subject: [PATCH 06/16] omzetten vanuit DAO: teacher #45 --- backend/domain/logic/teacher.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 backend/domain/logic/teacher.py diff --git a/backend/domain/logic/teacher.py b/backend/domain/logic/teacher.py new file mode 100644 index 00000000..229320ee --- /dev/null +++ b/backend/domain/logic/teacher.py @@ -0,0 +1,16 @@ +from sqlalchemy.orm import Session + +from db.models.models import Teacher +from domain.models.TeacherDataclass import TeacherDataclass + + +def create_teacher(session: Session, name: str, email: str) -> TeacherDataclass: + new_teacher: Teacher = Teacher(name=name, email=email) + session.add(new_teacher) + session.commit() + return new_teacher.to_domain_model() + + +def is_user_teacher(session: Session, user_id: int) -> bool: + teacher = session.get(Teacher, user_id) + return teacher is not None From 28cdf5899baf5939ef3fdf1a3503f84e1d13d6b8 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Tue, 5 Mar 2024 22:13:00 +0100 Subject: [PATCH 07/16] omzetten vanuit DAO: admin #45 --- backend/domain/logic/admin.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 backend/domain/logic/admin.py diff --git a/backend/domain/logic/admin.py b/backend/domain/logic/admin.py new file mode 100644 index 00000000..be239d11 --- /dev/null +++ b/backend/domain/logic/admin.py @@ -0,0 +1,16 @@ +from sqlalchemy.orm import Session + +from db.models.models import Admin +from domain.models.AdminDataclass import AdminDataclass + + +def create_admin(session: Session, name: str, email: str) -> AdminDataclass: + new_admin: Admin = Admin(name=name, email=email) + session.add(new_admin) + session.commit() + return new_admin.to_domain_model() + + +def is_user_admin(session: Session, user_id: int) -> bool: + admin = session.get(Admin, user_id) + return admin is not None From a5e833d8a808e3b27d52979cef282c9bd7841617 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Tue, 5 Mar 2024 22:13:36 +0100 Subject: [PATCH 08/16] omzetten vanuit DAO: basic get operaties die geen domeinmodellen teruggeven #45 --- backend/domain/logic/basic_operations.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 backend/domain/logic/basic_operations.py diff --git a/backend/domain/logic/basic_operations.py b/backend/domain/logic/basic_operations.py new file mode 100644 index 00000000..fda2a77d --- /dev/null +++ b/backend/domain/logic/basic_operations.py @@ -0,0 +1,23 @@ +from typing import TypeVar, Type + +from sqlalchemy import select +from sqlalchemy.orm import Session + +from db.errors.database_errors import ItemNotFoundError +from db.models.models import AbstractModel + +T = TypeVar("T", bound=AbstractModel) + + +def get(session: Session, object_type: Type, ident: int) -> T: + generic_object: T | None = session.get(object_type, ident) + + if not generic_object: + msg = f"object with id {ident} not found" + raise ItemNotFoundError(msg) + + return generic_object.to_domain_model() + + +def get_all(session: Session) -> list[T]: + return list(session.scalars(select(T)).all()) From 171a1736849350e234424daf171ce187ff64c305 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Wed, 6 Mar 2024 22:08:13 +0100 Subject: [PATCH 09/16] Volledige refactor DAOs #45 --- backend/app.py | 49 +++++++- backend/db/errors/database_errors.py | 2 +- backend/db/implementation/SqlAbstractDAO.py | 32 ----- backend/db/implementation/SqlAdminDAO.py | 24 ---- backend/db/implementation/SqlDAOProvider.py | 43 ------- backend/db/implementation/SqlGroupDAO.py | 69 ----------- backend/db/implementation/SqlProjectDAO.py | 56 --------- backend/db/implementation/SqlStudentDAO.py | 24 ---- backend/db/implementation/SqlSubjectDAO.py | 74 ------------ backend/db/implementation/SqlSubmissionDAO.py | 51 -------- backend/db/implementation/SqlTeacherDAO.py | 24 ---- backend/db/implementation/SqlUserDAO.py | 9 -- backend/db/interface/AbstractDAO.py | 16 --- backend/db/interface/AdminDAO.py | 22 ---- backend/db/interface/DAOProvider.py | 44 ------- backend/db/interface/GroupDAO.py | 60 ---------- backend/db/interface/ProjectDAO.py | 46 ------- backend/db/interface/StudentDAO.py | 22 ---- backend/db/interface/SubjectDAO.py | 59 --------- backend/db/interface/SubmissionDAO.py | 49 -------- backend/db/interface/TeacherDAO.py | 22 ---- backend/db/interface/UserDAO.py | 7 -- backend/db/sessions.py | 15 +++ backend/domain/logic/SubjectLogic.py | 17 --- backend/domain/logic/UserLogic.py | 18 --- backend/domain/logic/admin.py | 10 ++ backend/domain/logic/basic_operations.py | 10 +- backend/domain/logic/group.py | 29 +++-- backend/domain/logic/project.py | 17 ++- backend/domain/logic/role_enum.py | 7 ++ backend/domain/logic/student.py | 9 ++ backend/domain/logic/subject.py | 50 ++++---- backend/domain/logic/submission.py | 25 ++-- backend/domain/logic/teacher.py | 9 ++ backend/domain/logic/user.py | 33 +++++ backend/domain/models/APIUser.py | 4 +- backend/fill_database_mock.py | 113 ++++++++++++------ backend/pyproject.toml | 1 + backend/routes/db.py | 6 - .../routes/dependencies/role_dependencies.py | 54 +++++++++ backend/routes/errors/authentication.py | 18 +++ backend/routes/exception_handlers.py | 38 ++++++ backend/routes/group.py | 0 backend/routes/login.py | 12 -- backend/routes/projects.py | 42 ------- backend/routes/student.py | 36 ++++++ backend/routes/subjects.py | 44 ------- backend/routes/teacher.py | 31 +++++ backend/routes/user.py | 33 +++++ backend/routes/users.py | 36 ------ 50 files changed, 499 insertions(+), 1022 deletions(-) delete mode 100644 backend/db/implementation/SqlAbstractDAO.py delete mode 100644 backend/db/implementation/SqlAdminDAO.py delete mode 100644 backend/db/implementation/SqlDAOProvider.py delete mode 100644 backend/db/implementation/SqlGroupDAO.py delete mode 100644 backend/db/implementation/SqlProjectDAO.py delete mode 100644 backend/db/implementation/SqlStudentDAO.py delete mode 100644 backend/db/implementation/SqlSubjectDAO.py delete mode 100644 backend/db/implementation/SqlSubmissionDAO.py delete mode 100644 backend/db/implementation/SqlTeacherDAO.py delete mode 100644 backend/db/implementation/SqlUserDAO.py delete mode 100644 backend/db/interface/AbstractDAO.py delete mode 100644 backend/db/interface/AdminDAO.py delete mode 100644 backend/db/interface/DAOProvider.py delete mode 100644 backend/db/interface/GroupDAO.py delete mode 100644 backend/db/interface/ProjectDAO.py delete mode 100644 backend/db/interface/StudentDAO.py delete mode 100644 backend/db/interface/SubjectDAO.py delete mode 100644 backend/db/interface/SubmissionDAO.py delete mode 100644 backend/db/interface/TeacherDAO.py delete mode 100644 backend/db/interface/UserDAO.py create mode 100644 backend/db/sessions.py delete mode 100644 backend/domain/logic/SubjectLogic.py delete mode 100644 backend/domain/logic/UserLogic.py create mode 100644 backend/domain/logic/role_enum.py create mode 100644 backend/domain/logic/user.py delete mode 100644 backend/routes/db.py create mode 100644 backend/routes/dependencies/role_dependencies.py create mode 100644 backend/routes/errors/authentication.py create mode 100644 backend/routes/exception_handlers.py create mode 100644 backend/routes/group.py delete mode 100644 backend/routes/projects.py create mode 100644 backend/routes/student.py delete mode 100644 backend/routes/subjects.py create mode 100644 backend/routes/teacher.py create mode 100644 backend/routes/user.py delete mode 100644 backend/routes/users.py diff --git a/backend/app.py b/backend/app.py index d488b4c7..d53e552f 100644 --- a/backend/app.py +++ b/backend/app.py @@ -1,16 +1,55 @@ import uvicorn from fastapi import FastAPI +from starlette import status +from starlette.requests import Request +from starlette.responses import JSONResponse -from routes.projects import projects_router -from routes.subjects import subjects_router -from routes.users import users_router +from db.errors.database_errors import ActionAlreadyPerformedError, ItemNotFoundError +from routes.errors.authentication import InvalidRoleCredentialsError, StudentNotEnrolledError +from routes.student import student_router +from routes.teacher import teacher_router +from routes.user import users_router app = FastAPI() # Koppel routes uit andere modules. -app.include_router(subjects_router, prefix="/api") +app.include_router(student_router, prefix="/api") +app.include_router(teacher_router, prefix="/api") app.include_router(users_router, prefix="/api") -app.include_router(projects_router, prefix="/api") + + +# Koppel de exception handlers +@app.exception_handler(InvalidRoleCredentialsError) +def invalid_admin_credentials_error_handler(request: Request, exc: InvalidRoleCredentialsError) -> JSONResponse: + return JSONResponse( + status_code=status.HTTP_403_FORBIDDEN, + content={"message": exc.ERROR_MESSAGE}, + ) + + +@app.exception_handler(ItemNotFoundError) +def item_not_found_error_handler(request: Request, exc: ItemNotFoundError) -> JSONResponse: + return JSONResponse( + status_code=status.HTTP_404_NOT_FOUND, + content={"detail": str(exc)}, + ) + + +@app.exception_handler(StudentNotEnrolledError) +def student_already_enrolled_error_handler(request: Request, exc: StudentNotEnrolledError) -> JSONResponse: + return JSONResponse( + status_code=status.HTTP_400_BAD_REQUEST, + content={"detail": str(exc)}, + ) + + +@app.exception_handler(ActionAlreadyPerformedError) +def action_already_performed_error_handler(request: Request, exc: ActionAlreadyPerformedError) -> JSONResponse: + return JSONResponse( + status_code=status.HTTP_400_BAD_REQUEST, + content={"detail": str(exc)}, + ) + if __name__ == "__main__": uvicorn.run("app:app") diff --git a/backend/db/errors/database_errors.py b/backend/db/errors/database_errors.py index eb45dc51..e2805cc3 100644 --- a/backend/db/errors/database_errors.py +++ b/backend/db/errors/database_errors.py @@ -4,6 +4,6 @@ def __init__(self, message: str) -> None: super().__init__(message) -class UniqueConstraintError(Exception): +class ActionAlreadyPerformedError(Exception): def __init__(self, message: str) -> None: super().__init__(message) diff --git a/backend/db/implementation/SqlAbstractDAO.py b/backend/db/implementation/SqlAbstractDAO.py deleted file mode 100644 index 540fbc2b..00000000 --- a/backend/db/implementation/SqlAbstractDAO.py +++ /dev/null @@ -1,32 +0,0 @@ -from typing import Generic, TypeVar - -from pydantic import BaseModel -from sqlalchemy import select -from sqlalchemy.orm import Session - -from db.errors.database_errors import ItemNotFoundError -from db.extensions import engine -from db.models.models import AbstractModel - -T = TypeVar("T", bound=AbstractModel) -D = TypeVar("D", bound=BaseModel) - - -class SqlAbstractDAO(Generic[T, D]): - def __init__(self) -> None: - self.model_class: type[T] - - def get(self, ident: int) -> D: - with Session(engine) as session: - generic_object: T | None = session.get(self.model_class, ident) - - if not generic_object: - msg = f"object with id {ident} not found" - raise ItemNotFoundError(msg) - - return generic_object.to_domain_model() - - def get_all(self) -> list[D]: - with Session(engine) as session: - generic_objects: list[T] = list(session.scalars(select(self.model_class)).all()) - return [generic_object.to_domain_model() for generic_object in generic_objects] diff --git a/backend/db/implementation/SqlAdminDAO.py b/backend/db/implementation/SqlAdminDAO.py deleted file mode 100644 index e9a213f7..00000000 --- a/backend/db/implementation/SqlAdminDAO.py +++ /dev/null @@ -1,24 +0,0 @@ -from sqlalchemy.orm import Session - -from db.extensions import engine -from db.implementation.SqlAbstractDAO import SqlAbstractDAO -from db.interface.AdminDAO import AdminDAO -from db.models.models import Admin -from domain.models.AdminDataclass import AdminDataclass - - -class SqlAdminDAO(SqlAbstractDAO[Admin, AdminDataclass], AdminDAO): - def __init__(self) -> None: - self.model_class = Admin - - def create_admin(self, name: str, email: str) -> AdminDataclass: - with Session(engine) as session: - new_admin: Admin = Admin(name=name, email=email) - 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 diff --git a/backend/db/implementation/SqlDAOProvider.py b/backend/db/implementation/SqlDAOProvider.py deleted file mode 100644 index e8b2cb9f..00000000 --- a/backend/db/implementation/SqlDAOProvider.py +++ /dev/null @@ -1,43 +0,0 @@ -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() diff --git a/backend/db/implementation/SqlGroupDAO.py b/backend/db/implementation/SqlGroupDAO.py deleted file mode 100644 index 7f7fae72..00000000 --- a/backend/db/implementation/SqlGroupDAO.py +++ /dev/null @@ -1,69 +0,0 @@ -from sqlalchemy.orm import Session - -from db.errors.database_errors import ItemNotFoundError, UniqueConstraintError -from db.extensions import engine -from db.implementation.SqlAbstractDAO import SqlAbstractDAO -from db.interface.GroupDAO import GroupDAO -from db.models.models import Group, Project, Student -from domain.models.GroupDataclass import GroupDataclass -from domain.models.StudentDataclass import StudentDataclass - - -class SqlGroupDAO(SqlAbstractDAO[Group, GroupDataclass], GroupDAO): - def __init__(self) -> None: - self.model_class = Group - - def create_group(self, project_id: int) -> GroupDataclass: - with Session(engine) as session: - project: Project | None = session.get(Project, ident=project_id) - if not project: - msg = f"Project with id {project} not found" - raise ItemNotFoundError(msg) - new_group: Group = Group(project_id=project_id) - session.add(new_group) - session.commit() - return new_group.to_domain_model() - - def get_groups_of_project(self, project_id: int) -> list[GroupDataclass]: - with Session(engine) as session: - project: Project | None = session.get(Project, ident=project_id) - if not project: - msg = f"Project with id {project} not found" - raise ItemNotFoundError(msg) - groups: list[Group] = project.groups - return [group.to_domain_model() for group in groups] - - def get_groups_of_student(self, student_id: int) -> list[GroupDataclass]: - with Session(engine) as session: - student: Student | None = session.get(Student, ident=student_id) - if not student: - msg = f"Student with id {student_id} not found" - raise ItemNotFoundError(msg) - groups: list[Group] = student.groups - return [group.to_domain_model() for group in groups] - - def add_student_to_group(self, student_id: int, group_id: int) -> None: - with Session(engine) as session: - student: Student | None = session.get(Student, ident=student_id) - group: Group | None = session.get(Group, ident=group_id) - if not student: - msg = f"Student with id {student_id} not found" - raise ItemNotFoundError(msg) - if not group: - msg = f"Group with id {group_id} not found" - raise ItemNotFoundError(msg) - if student in group.students: - msg = f"Student with id {student_id} already in group with id {group_id}" - raise UniqueConstraintError(msg) - - group.students.append(student) - session.commit() - - def get_students_of_group(self, group_id: int) -> list[StudentDataclass]: - with Session(engine) as session: - group: Group | None = session.get(Group, ident=group_id) - if not group: - msg = f"Group with id {group_id} not found" - raise ItemNotFoundError(msg) - students: list[Student] = group.students - return [student.to_domain_model() for student in students] diff --git a/backend/db/implementation/SqlProjectDAO.py b/backend/db/implementation/SqlProjectDAO.py deleted file mode 100644 index c3ae7674..00000000 --- a/backend/db/implementation/SqlProjectDAO.py +++ /dev/null @@ -1,56 +0,0 @@ -from datetime import datetime - -from sqlalchemy.orm import Session - -from db.errors.database_errors import ItemNotFoundError -from db.extensions import engine -from db.implementation.SqlAbstractDAO import SqlAbstractDAO -from db.interface.ProjectDAO import ProjectDAO -from db.models.models import Project, Subject -from domain.models.ProjectDataclass import ProjectDataclass - - -class SqlProjectDAO(SqlAbstractDAO[Project, ProjectDataclass], ProjectDAO): - def __init__(self) -> None: - self.model_class = Project - - def create_project( - self, - subject_id: int, - name: str, - deadline: datetime, - archived: bool, - description: str, - requirements: str, - visible: bool, - max_students: int, - ) -> ProjectDataclass: - with Session(engine) as session: - subject: Subject | None = session.get(Subject, subject_id) - if not subject: - msg = f"Subject with id {subject_id} not found" - raise ItemNotFoundError(msg) - - new_project: Project = Project( - subject_id=subject_id, - name=name, - deadline=deadline, - archived=archived, - description=description, - requirements=requirements, - visible=visible, - max_students=max_students, - ) - - session.add(new_project) - session.commit() - return new_project.to_domain_model() - - def get_projects_of_subject(self, subject_id: int) -> list[ProjectDataclass]: - with Session(engine) as session: - subject: Subject | None = session.get(Subject, ident=subject_id) - if not subject: - msg = f"Subject with id {subject_id} not found" - raise ItemNotFoundError(msg) - projects: list[Project] = subject.projects - return [project.to_domain_model() for project in projects] diff --git a/backend/db/implementation/SqlStudentDAO.py b/backend/db/implementation/SqlStudentDAO.py deleted file mode 100644 index 2dfd6fe0..00000000 --- a/backend/db/implementation/SqlStudentDAO.py +++ /dev/null @@ -1,24 +0,0 @@ -from sqlalchemy.orm import Session - -from db.extensions import engine -from db.implementation.SqlAbstractDAO import SqlAbstractDAO -from db.interface.StudentDAO import StudentDAO -from db.models.models import Student -from domain.models.StudentDataclass import StudentDataclass - - -class SqlStudentDAO(SqlAbstractDAO[Student, StudentDataclass], StudentDAO): - def __init__(self) -> None: - self.model_class = Student - - def create_student(self, name: str, email: str) -> StudentDataclass: - with Session(engine) as session: - new_student: Student = Student(name=name, email=email) - 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 diff --git a/backend/db/implementation/SqlSubjectDAO.py b/backend/db/implementation/SqlSubjectDAO.py deleted file mode 100644 index 5d368901..00000000 --- a/backend/db/implementation/SqlSubjectDAO.py +++ /dev/null @@ -1,74 +0,0 @@ -from sqlalchemy.orm import Session - -from db.errors.database_errors import ItemNotFoundError, UniqueConstraintError -from db.extensions import engine -from db.implementation.SqlAbstractDAO import SqlAbstractDAO -from db.interface.SubjectDAO import SubjectDAO -from db.models.models import Student, Subject, Teacher -from domain.models.SubjectDataclass import SubjectDataclass - - -class SqlSubjectDAO(SqlAbstractDAO[Subject, SubjectDataclass], SubjectDAO): - def __init__(self) -> None: - self.model_class = Subject - - def create_subject(self, name: str) -> SubjectDataclass: - with Session(engine) as session: - new_subject = Subject(name=name) - session.add(new_subject) - session.commit() - return new_subject.to_domain_model() - - def get_subjects_of_teacher(self, teacher_id: int) -> list[SubjectDataclass]: - with Session(engine) as session: - teacher: Teacher | None = session.get(Teacher, ident=teacher_id) - if not teacher: - msg = f"Teacher with id {teacher_id} not found" - raise ItemNotFoundError(msg) - subjects: list[Subject] = teacher.subjects - return [vak.to_domain_model() for vak in subjects] - - def add_student_to_subject(self, student_id: int, subject_id: int) -> None: - with Session(engine) as session: - student: Student | None = session.get(Student, ident=student_id) - subject: Subject | None = session.get(Subject, ident=subject_id) - - if not student: - msg = f"Student with id {student_id} not found" - raise ItemNotFoundError(msg) - if not subject: - msg = f"Subject with id {subject_id} not found" - raise ItemNotFoundError(msg) - if subject in student.subjects: - msg = f"Student with id {student_id} already has subject with id {subject_id}" - raise UniqueConstraintError(msg) - - student.subjects.append(subject) - session.commit() - - def add_teacher_to_subject(self, teacher_id: int, subject_id: int) -> None: - with Session(engine) as session: - teacher: Teacher | None = session.get(Teacher, ident=teacher_id) - subject: Subject | None = session.get(Subject, ident=subject_id) - - if not teacher: - msg = f"Teacher with id {teacher_id} not found" - raise ItemNotFoundError(msg) - if not subject: - msg = f"Subject with id {subject_id} not found" - raise ItemNotFoundError(msg) - if subject in teacher.subjects: - msg = f"Teacher with id {teacher_id} already has subject with id {subject_id}" - raise UniqueConstraintError(msg) - - teacher.subjects.append(subject) - session.commit() - - def get_subjects_of_student(self, student_id: int) -> list[SubjectDataclass]: - with Session(engine) as session: - student: Student | None = session.get(Student, ident=student_id) - if not student: - msg = f"Student with id {student_id} not found" - raise ItemNotFoundError(msg) - subjects: list[Subject] = student.subjects - return [vak.to_domain_model() for vak in subjects] diff --git a/backend/db/implementation/SqlSubmissionDAO.py b/backend/db/implementation/SqlSubmissionDAO.py deleted file mode 100644 index 432296dd..00000000 --- a/backend/db/implementation/SqlSubmissionDAO.py +++ /dev/null @@ -1,51 +0,0 @@ -from datetime import datetime - -from sqlalchemy.orm import Session - -from db.errors.database_errors import ItemNotFoundError -from db.extensions import engine -from db.implementation.SqlAbstractDAO import SqlAbstractDAO -from db.interface.SubmissionDAO import SubmissionDAO -from db.models.models import Group, Student, Submission -from domain.models.SubmissionDataclass import SubmissionDataclass, SubmissionState - - -class SqlSubmissionDAO(SqlAbstractDAO[Submission, SubmissionDataclass], SubmissionDAO): - def __init__(self) -> None: - self.model_class = Submission - - def create_submission(self, student_id: int, group_id: int, message: str, state: SubmissionState, - date_time: datetime) -> SubmissionDataclass: - with Session(engine) as session: - student: Student | None = session.get(Student, ident=student_id) - group: Group | None = session.get(Group, ident=group_id) - if not student: - msg = f"Student with id {student_id} not found" - raise ItemNotFoundError(msg) - if not group: - msg = f"Group with id {group_id} not found" - raise ItemNotFoundError(msg) - new_submission: Submission = Submission(student_id=student_id, - group_id=group_id, message=message, state=state, - date_time=date_time) - session.add(new_submission) - session.commit() - return new_submission.to_domain_model() - - def get_submissions_of_student(self, student_id: int) -> list[SubmissionDataclass]: - with Session(engine) as session: - student: Student | None = session.get(Student, ident=student_id) - if not student: - msg = f"Student with id {student_id} not found" - raise ItemNotFoundError(msg) - submissions: list[Submission] = student.submissions - return [submission.to_domain_model() for submission in submissions] - - def get_submissions_of_group(self, group_id: int) -> list[SubmissionDataclass]: - with Session(engine) as session: - group: Group | None = session.get(Group, ident=group_id) - if not group: - msg = f"Group with id {group_id} not found" - raise ItemNotFoundError(msg) - submissions: list[Submission] = group.submissions - return [submission.to_domain_model() for submission in submissions] diff --git a/backend/db/implementation/SqlTeacherDAO.py b/backend/db/implementation/SqlTeacherDAO.py deleted file mode 100644 index 08fd49d4..00000000 --- a/backend/db/implementation/SqlTeacherDAO.py +++ /dev/null @@ -1,24 +0,0 @@ -from sqlalchemy.orm import Session - -from db.extensions import engine -from db.implementation.SqlAbstractDAO import SqlAbstractDAO -from db.interface.TeacherDAO import TeacherDAO -from db.models.models import Teacher -from domain.models.TeacherDataclass import TeacherDataclass - - -class SqlTeacherDAO(SqlAbstractDAO[Teacher, TeacherDataclass], TeacherDAO): - def __init__(self) -> None: - self.model_class = Teacher - - def create_teacher(self, name: str, email: str) -> TeacherDataclass: - with Session(engine) as session: - new_teacher = Teacher(name=name, email=email) - 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 diff --git a/backend/db/implementation/SqlUserDAO.py b/backend/db/implementation/SqlUserDAO.py deleted file mode 100644 index 85e3dd18..00000000 --- a/backend/db/implementation/SqlUserDAO.py +++ /dev/null @@ -1,9 +0,0 @@ -from db.implementation.SqlAbstractDAO import SqlAbstractDAO -from db.interface.UserDAO import UserDAO -from db.models.models import User -from domain.models.UserDataclass import UserDataclass - - -class SqlUserDAO(SqlAbstractDAO[User, UserDataclass], UserDAO): - def __init__(self) -> None: - self.model_class = User diff --git a/backend/db/interface/AbstractDAO.py b/backend/db/interface/AbstractDAO.py deleted file mode 100644 index 2c4ade30..00000000 --- a/backend/db/interface/AbstractDAO.py +++ /dev/null @@ -1,16 +0,0 @@ -from abc import ABC, abstractmethod -from typing import Generic, TypeVar - -T = TypeVar("T") -D = TypeVar("D") - - -class AbstractDAO(Generic[T, D], ABC): - - @abstractmethod - def get(self, ident: int) -> D: - raise NotImplementedError - - @abstractmethod - def get_all(self) -> list[D]: - raise NotImplementedError diff --git a/backend/db/interface/AdminDAO.py b/backend/db/interface/AdminDAO.py deleted file mode 100644 index 7b78c652..00000000 --- a/backend/db/interface/AdminDAO.py +++ /dev/null @@ -1,22 +0,0 @@ -from abc import abstractmethod - -from db.interface.AbstractDAO import AbstractDAO -from db.models.models import Admin -from domain.models.AdminDataclass import AdminDataclass - - -class AdminDAO(AbstractDAO[Admin, AdminDataclass]): - @abstractmethod - def create_admin(self, name: str, email: str) -> AdminDataclass: - """ - Maakt een nieuwe admin aan. - - :param name: De naam van de nieuwe admin. - :param email: De email van de nieuwe admin. - :return: De nieuwe admin - """ - raise NotImplementedError - - @abstractmethod - def is_user_admin(self, user_id: int) -> bool: - raise NotImplementedError diff --git a/backend/db/interface/DAOProvider.py b/backend/db/interface/DAOProvider.py deleted file mode 100644 index 6ebe43c5..00000000 --- a/backend/db/interface/DAOProvider.py +++ /dev/null @@ -1,44 +0,0 @@ -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 diff --git a/backend/db/interface/GroupDAO.py b/backend/db/interface/GroupDAO.py deleted file mode 100644 index b1b9e9d3..00000000 --- a/backend/db/interface/GroupDAO.py +++ /dev/null @@ -1,60 +0,0 @@ -from abc import abstractmethod - -from db.interface.AbstractDAO import AbstractDAO -from db.models.models import Group -from domain.models.GroupDataclass import GroupDataclass -from domain.models.StudentDataclass import StudentDataclass - - -class GroupDAO(AbstractDAO[Group, GroupDataclass]): - @abstractmethod - def create_group(self, project_id: int) -> GroupDataclass: - """ - Creëert een nieuw GroupDataClass in de database en associeert het met een ProjectDataClass. - - :param project_id: Id van het project dat gelinkt is aan de groep - :raises: ItemNotFoundException: Als er geen ProjectDataClass met de opgegeven `project_id` in de database is. - :returns: De nieuw aangemaakte groep - """ - raise NotImplementedError - - @abstractmethod - def get_groups_of_project(self, project_id: int) -> list[GroupDataclass]: - """ - Haalt alle groepen op die bij een bepaald project horen. - - :param project_id: Het subject waarvan de projecten opgehaald moeten worden. - :return: Een lijst van projecten die bij een bepaald project horen. - """ - raise NotImplementedError - - @abstractmethod - def get_groups_of_student(self, student_id: int) -> list[GroupDataclass]: - """ - Haalt alle groepen op die bij een bepaalde student horen. - - :param student_id: De student waarvan de groepen opgehaald moeten worden. - :return: Een lijst van groepen die bij een bepaald student horen. - """ - raise NotImplementedError - - @abstractmethod - def add_student_to_group(self, student_id: int, group_id: int) -> None: - """ - Gaat een student toevoegen aan een groep - - :param student_id: De student die aan de groep moet toegevoegd worden. - :param group_id: De groep waaraan de student moet toegevoegd worden. - :raises ItemNotFoundException: Als er geen group/student met gegeven id in de databank zit. - """ - raise NotImplementedError - - @abstractmethod - def get_students_of_group(self, group_id: int) -> list[StudentDataclass]: - """ - Gaat alle studenten geven die in een bepaalde groep zitten - - :param group_id: De groep waarvan de studenten worden opgeroepen - :raises ItemNotFoundException: Als er geen group met gegeven id in de databank zit. - """ - raise NotImplementedError diff --git a/backend/db/interface/ProjectDAO.py b/backend/db/interface/ProjectDAO.py deleted file mode 100644 index ed2d1e6a..00000000 --- a/backend/db/interface/ProjectDAO.py +++ /dev/null @@ -1,46 +0,0 @@ -from abc import abstractmethod -from datetime import datetime - -from db.interface.AbstractDAO import AbstractDAO -from db.models.models import Project -from domain.models.ProjectDataclass import ProjectDataclass - - -class ProjectDAO(AbstractDAO[Project, ProjectDataclass]): - @abstractmethod - def create_project( - self, - subject_id: int, - name: str, - deadline: datetime, - archived: bool, - description: str, - requirements: str, - visible: bool, - max_students: int, - ) -> ProjectDataclass: - """ - Creëert een nieuw ProjectDataClass in de database en associeert het met een SubjectDataClass. - - :param max_students: maximaal aantal studenten per groep per project - :param visible: of het project zichtbaar is voor de studentent - :param description: Beschrijving van het project - :param requirements: Vereisten van het project - :param archived: Of het project gearchiveerd is - :param name: De naam van het project - :param deadline: De deadline van het project - :param subject_id: De identificatie van de SubjectDataClass waarmee het ProjectDataClass geassocieerd wordt. - :raises: ItemNotFoundException: Als er geen SubjectDataClass met de opgegeven `teacher_id` in de database is. - :returns: Het nieuw aangemaakte project - """ - raise NotImplementedError - - @abstractmethod - def get_projects_of_subject(self, subject_id: int) -> list[ProjectDataclass]: - """ - Haalt alle projecten op die bij een bepaald subject horen. - - :param subject_id: Het subject waarvan de projecten opgehaald moeten worden. - :return: Een lijst van projecten die bij een bepaald subject horen. - """ - raise NotImplementedError diff --git a/backend/db/interface/StudentDAO.py b/backend/db/interface/StudentDAO.py deleted file mode 100644 index e7f9e8da..00000000 --- a/backend/db/interface/StudentDAO.py +++ /dev/null @@ -1,22 +0,0 @@ -from abc import abstractmethod - -from db.interface.AbstractDAO import AbstractDAO -from db.models.models import Student -from domain.models.StudentDataclass import StudentDataclass - - -class StudentDAO(AbstractDAO[Student, StudentDataclass]): - @abstractmethod - def create_student(self, name: str, email: str) -> StudentDataclass: - """ - Maakt een nieuwe student aan. - - :param name: De naam van de nieuwe student - :param email: De email van de nieuwe student - :returns: De nieuw aangemaakte student - """ - raise NotImplementedError - - @abstractmethod - def is_user_student(self, user_id: int) -> bool: - raise NotImplementedError diff --git a/backend/db/interface/SubjectDAO.py b/backend/db/interface/SubjectDAO.py deleted file mode 100644 index b759ab12..00000000 --- a/backend/db/interface/SubjectDAO.py +++ /dev/null @@ -1,59 +0,0 @@ -from abc import abstractmethod - -from db.interface.AbstractDAO import AbstractDAO -from db.models.models import Subject -from domain.models.SubjectDataclass import SubjectDataclass - - -class SubjectDAO(AbstractDAO[Subject, SubjectDataclass]): - @abstractmethod - def create_subject(self, name: str) -> SubjectDataclass: - """ - Creëert een nieuw SubjectDataclass in de database. - - :param name: De naam van het nieuwe vak. - :returns: Het nieuw aangemaakte subject. - """ - raise NotImplementedError - - @abstractmethod - def get_subjects_of_teacher(self, teacher_id: int) -> list[SubjectDataclass]: - """ - Haalt de subjects op die door een bepaalde teacher worden gegeven. - - :param teacher_id: De teacher waarvan de subjects opgehaald moeten worden. - :return: Een lijst van subjects die door de gegeven teacher worden gegeven. - """ - raise NotImplementedError - - @abstractmethod - def get_subjects_of_student(self, student_id: int) -> list[SubjectDataclass]: - """ - Haalt de subjects op die door een bepaalde student worden gevolgd. - - :param student_id: De student waarvan de subjects opgehaald moeten worden. - :return: Een lijst van subjects die door de gegeven student worden gegeven. - """ - raise NotImplementedError - - @abstractmethod - def add_student_to_subject(self, student_id: int, subject_id: int) -> None: - """ - Voegt een student toe aan een vak. - - :param subject_id: De id van subject die door de student wordt gevolgd. - :param student_id: De student die subject volgt. - :raises: ItemNotFoundException: Als er geen student/subject met de opgegeven id in de database is. - """ - raise NotImplementedError - - @abstractmethod - def add_teacher_to_subject(self, teacher_id: int, subject_id: int) -> None: - """ - Voegt een teacher toe aan een vak. - - :param subject_id: De id van subject die door de teacher gegeven wordt. - :param teacher_id: De teacher die dit subject geeft. - :raises: ItemNotFoundException: Als er geen teacher/subject met de opgegeven id in de database is. - """ - raise NotImplementedError diff --git a/backend/db/interface/SubmissionDAO.py b/backend/db/interface/SubmissionDAO.py deleted file mode 100644 index c5fd33c2..00000000 --- a/backend/db/interface/SubmissionDAO.py +++ /dev/null @@ -1,49 +0,0 @@ -from abc import abstractmethod -from datetime import datetime - -from db.interface.AbstractDAO import AbstractDAO -from db.models.models import Submission -from domain.models.SubmissionDataclass import SubmissionDataclass, SubmissionState - - -class SubmissionDAO(AbstractDAO[Submission, SubmissionDataclass]): - @abstractmethod - 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. - - :param student_id: De identificatie van de StudentDataclass waarmee het SubmissionDataClass geassocieerd wordt. - :param group_id: De identificatie van de GroupDataClass waarmee het SubmissionDataClass geassocieerd wordt - :raises: ItemNotFoundException: Als er geen StudentDataclass of GroupDataClass met de opgegeven `student_id` of - `group_id` in de database is. - :return: De nieuw aangemaakte submission - """ - raise NotImplementedError - - @abstractmethod - def get_submissions_of_student(self, student_id: int) -> list[SubmissionDataclass]: - """ - Haalt alle projecten op die bij een bepaalde student horen. - - :param student_id: De student waarvan de submissions opgehaald moeten worden. - :return: Een lijst van submissions die bij een bepaalde student horen. - """ - raise NotImplementedError - - @abstractmethod - def get_submissions_of_group(self, group_id: int) -> list[SubmissionDataclass]: - """ - Haalt alle projecten op die bij een bepaalde groep horen. - - :param group_id: De groep waarvan de submissions opgehaald moeten worden. - :return: Een lijst van submissions die bij een bepaalde groep horen. - """ - raise NotImplementedError diff --git a/backend/db/interface/TeacherDAO.py b/backend/db/interface/TeacherDAO.py deleted file mode 100644 index 99bea93c..00000000 --- a/backend/db/interface/TeacherDAO.py +++ /dev/null @@ -1,22 +0,0 @@ -from abc import abstractmethod - -from db.interface.AbstractDAO import AbstractDAO -from db.models.models import Teacher -from domain.models.TeacherDataclass import TeacherDataclass - - -class TeacherDAO(AbstractDAO[Teacher, TeacherDataclass]): - @abstractmethod - def create_teacher(self, name: str, email: str) -> TeacherDataclass: - """ - Maakt een nieuwe teacher aan. - - :param name: De naam van de nieuwe teacher. - :param email: De email van de nieuwe teacher. - :returns: De nieuw aangemaakte teacher. - """ - raise NotImplementedError - - @abstractmethod - def is_user_teacher(self, user_id: int) -> bool: - raise NotImplementedError diff --git a/backend/db/interface/UserDAO.py b/backend/db/interface/UserDAO.py deleted file mode 100644 index 6769baba..00000000 --- a/backend/db/interface/UserDAO.py +++ /dev/null @@ -1,7 +0,0 @@ -from db.interface.AbstractDAO import AbstractDAO -from db.models.models import User -from domain.models.UserDataclass import UserDataclass - - -class UserDAO(AbstractDAO[User, UserDataclass]): - pass diff --git a/backend/db/sessions.py b/backend/db/sessions.py new file mode 100644 index 00000000..da201a92 --- /dev/null +++ b/backend/db/sessions.py @@ -0,0 +1,15 @@ +from collections.abc import Generator + +from sqlalchemy.orm import Session, sessionmaker + +from db.extensions import engine + +SessionLocal: sessionmaker[Session] = sessionmaker(autocommit=False, autoflush=False, bind=engine) + + +def get_session() -> Generator[Session, None, None]: + db = SessionLocal() + try: + yield db + finally: + db.close() diff --git a/backend/domain/logic/SubjectLogic.py b/backend/domain/logic/SubjectLogic.py deleted file mode 100644 index 7475ee63..00000000 --- a/backend/domain/logic/SubjectLogic.py +++ /dev/null @@ -1,17 +0,0 @@ -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 diff --git a/backend/domain/logic/UserLogic.py b/backend/domain/logic/UserLogic.py deleted file mode 100644 index 428465f6..00000000 --- a/backend/domain/logic/UserLogic.py +++ /dev/null @@ -1,18 +0,0 @@ -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 diff --git a/backend/domain/logic/admin.py b/backend/domain/logic/admin.py index be239d11..aaa83f61 100644 --- a/backend/domain/logic/admin.py +++ b/backend/domain/logic/admin.py @@ -1,6 +1,7 @@ from sqlalchemy.orm import Session from db.models.models import Admin +from domain.logic.basic_operations import get, get_all from domain.models.AdminDataclass import AdminDataclass @@ -11,6 +12,15 @@ def create_admin(session: Session, name: str, email: str) -> AdminDataclass: return new_admin.to_domain_model() +def get_admin(session: Session, admin_id: int) -> AdminDataclass: + return get(session, Admin, admin_id).to_domain_model() + + +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 diff --git a/backend/domain/logic/basic_operations.py b/backend/domain/logic/basic_operations.py index fda2a77d..94fb15e3 100644 --- a/backend/domain/logic/basic_operations.py +++ b/backend/domain/logic/basic_operations.py @@ -1,4 +1,4 @@ -from typing import TypeVar, Type +from typing import TypeVar from sqlalchemy import select from sqlalchemy.orm import Session @@ -9,15 +9,15 @@ T = TypeVar("T", bound=AbstractModel) -def get(session: Session, object_type: Type, ident: int) -> T: +def get(session: Session, object_type: type[T], ident: int) -> T: generic_object: T | None = session.get(object_type, ident) if not generic_object: msg = f"object with id {ident} not found" raise ItemNotFoundError(msg) - return generic_object.to_domain_model() + return generic_object -def get_all(session: Session) -> list[T]: - return list(session.scalars(select(T)).all()) +def get_all(session: Session, object_type: type[T]) -> list[T]: + return list(session.scalars(select(object_type)).all()) diff --git a/backend/domain/logic/group.py b/backend/domain/logic/group.py index 4f280946..76c2582c 100644 --- a/backend/domain/logic/group.py +++ b/backend/domain/logic/group.py @@ -1,16 +1,15 @@ -from typing import Type from sqlalchemy.orm import Session -from db.errors.database_errors import UniqueConstraintError -from db.models.models import Project, Group, Student -from domain.logic.basic_operations import get +from db.errors.database_errors import ActionAlreadyPerformedError +from db.models.models import Group, Project, Student +from domain.logic.basic_operations import get, get_all from domain.models.GroupDataclass import GroupDataclass from domain.models.StudentDataclass import StudentDataclass def create_group(session: Session, project_id: int) -> GroupDataclass: - project: Project = get(session, Type[Project], project_id) + project: Project = get(session, Project, project_id) new_group: Group = Group(project_id=project_id) project.groups.append(new_group) @@ -20,31 +19,39 @@ def create_group(session: Session, project_id: int) -> GroupDataclass: return new_group.to_domain_model() +def get_group(session: Session, group_id: int) -> GroupDataclass: + return get(session, Group, group_id).to_domain_model() + + +def get_all_groups(session: Session) -> list[GroupDataclass]: + return [group.to_domain_model() for group in get_all(session, Group)] + + def get_groups_of_project(session: Session, project_id: int) -> list[GroupDataclass]: - project: Project = get(session, Type[Project], project_id) + project: Project = get(session, Project, project_id) groups: list[Group] = project.groups return [group.to_domain_model() for group in groups] def get_groups_of_student(session: Session, student_id: int) -> list[GroupDataclass]: - student: Student = get(session, Type[Student], ident=student_id) + student: Student = get(session, Student, ident=student_id) groups: list[Group] = student.groups return [group.to_domain_model() for group in groups] def add_student_to_group(session: Session, student_id: int, group_id: int) -> None: - student: Student = get(session, Type[Student], ident=student_id) - group: Group = get(session, Type[Group], ident=group_id) + student: Student = get(session, Student, ident=student_id) + group: Group = get(session, Group, ident=group_id) if student in group.students: msg = f"Student with id {student_id} already in group with id {group_id}" - raise UniqueConstraintError(msg) + raise ActionAlreadyPerformedError(msg) group.students.append(student) session.commit() def get_students_of_group(session: Session, group_id: int) -> list[StudentDataclass]: - group: Group = get(session, Type[Group], ident=group_id) + group: Group = get(session, Group, ident=group_id) students: list[Student] = group.students return [student.to_domain_model() for student in students] diff --git a/backend/domain/logic/project.py b/backend/domain/logic/project.py index 61ec971f..835d7d6b 100644 --- a/backend/domain/logic/project.py +++ b/backend/domain/logic/project.py @@ -1,10 +1,9 @@ from datetime import datetime -from typing import Type from sqlalchemy.orm import Session -from db.models.models import Subject, Project -from domain.logic.basic_operations import get +from db.models.models import Project, Subject +from domain.logic.basic_operations import get, get_all from domain.models.ProjectDataclass import ProjectDataclass @@ -19,7 +18,7 @@ def create_project( visible: bool, max_students: int, ) -> ProjectDataclass: - subject: Subject = get(session, Type[Subject], subject_id) + subject: Subject = get(session, Subject, subject_id) new_project: Project = Project( name=name, @@ -37,8 +36,16 @@ def create_project( return new_project.to_domain_model() +def get_project(session: Session, project_id: int) -> ProjectDataclass: + return get(session, Project, project_id).to_domain_model() + + +def get_all_projects(session: Session) -> list[ProjectDataclass]: + return [project.to_domain_model() for project in get_all(session, Project)] + + def get_projects_of_subject(session: Session, subject_id: int) -> list[ProjectDataclass]: - subject: Subject = get(session, Type[Subject], ident=subject_id) + subject: Subject = get(session, Subject, ident=subject_id) projects: list[Project] = subject.projects return [project.to_domain_model() for project in projects] diff --git a/backend/domain/logic/role_enum.py b/backend/domain/logic/role_enum.py new file mode 100644 index 00000000..94479eb4 --- /dev/null +++ b/backend/domain/logic/role_enum.py @@ -0,0 +1,7 @@ +from enum import Enum + + +class Role(Enum): + ADMIN = "ADMIN" + TEACHER = "TEACHER" + STUDENT = "STUDENT" diff --git a/backend/domain/logic/student.py b/backend/domain/logic/student.py index a6b33583..59c4049e 100644 --- a/backend/domain/logic/student.py +++ b/backend/domain/logic/student.py @@ -1,6 +1,7 @@ from sqlalchemy.orm import Session from db.models.models import Student +from domain.logic.basic_operations import get, get_all from domain.models.StudentDataclass import StudentDataclass @@ -11,6 +12,14 @@ def create_student(session: Session, name: str, email: str) -> StudentDataclass: return new_student.to_domain_model() +def get_student(session: Session, student_id: int) -> StudentDataclass: + return get(session, Student, student_id).to_domain_model() + + +def get_all_students(session: Session) -> list[StudentDataclass]: + return [student.to_domain_model() for student in get_all(session, Student)] + + def is_user_student(session: Session, user_id: int) -> bool: student = session.get(Student, user_id) return student is not None diff --git a/backend/domain/logic/subject.py b/backend/domain/logic/subject.py index 6a3420c3..38e470e9 100644 --- a/backend/domain/logic/subject.py +++ b/backend/domain/logic/subject.py @@ -1,24 +1,22 @@ -from typing import Type - from sqlalchemy.orm import Session -from db.errors.database_errors import UniqueConstraintError -from db.models.models import Subject, Teacher, Student -from domain.logic.basic_operations import get +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 from domain.models.UserDataclass import UserDataclass -def is_user_authorized_for_subject(subject: SubjectDataclass, user: UserDataclass) -> bool: - teacher_dao = dao_provider.get_teacher_dao() - student_dao = dao_provider.get_student_dao() - subject_dao = dao_provider.get_subject_dao() # TODO - - if teacher_dao.is_user_teacher(user.id) and subject in subject_dao.get_subjects_of_teacher(user.id): - return True +def is_user_authorized_for_subject(session: Session, subject: SubjectDataclass, user: UserDataclass) -> bool: + if is_user_teacher(session, user.id): + subjects_of_teacher: list[SubjectDataclass] = get_subjects_of_teacher(session, subject.id) + return subject in subjects_of_teacher - if student_dao.is_user_student(user.id) and subject in subject_dao.get_subjects_of_student(user.id): - return True + if is_user_student(session, user.id): + subjects_of_student: list[SubjectDataclass] = get_subjects_of_student(session, subject.id) + return subject in subjects_of_student return False @@ -30,37 +28,45 @@ def create_subject(session: Session, name: str) -> SubjectDataclass: return new_subject.to_domain_model() +def get_subject(session: Session, subject_id: int) -> SubjectDataclass: + return get(session, Subject, subject_id).to_domain_model() + + +def get_all_subjects(session: Session) -> list[SubjectDataclass]: + return [subject.to_domain_model() for subject in get_all(session, Subject)] + + def get_subjects_of_teacher(session: Session, teacher_id: int) -> list[SubjectDataclass]: - teacher: Teacher = get(session, Type[Teacher], ident=teacher_id) + teacher: Teacher = get(session, Teacher, ident=teacher_id) subjects: list[Subject] = teacher.subjects return [vak.to_domain_model() for vak in subjects] def add_student_to_subject(session: Session, student_id: int, subject_id: int) -> None: - student: Student = get(session, Type[Student], ident=student_id) - subject: Subject = get(session, Type[Subject], ident=subject_id) + student: Student = get(session, Student, ident=student_id) + subject: Subject = get(session, Subject, ident=subject_id) if subject in student.subjects: msg = f"Student with id {student_id} already has subject with id {subject_id}" - raise UniqueConstraintError(msg) + raise ActionAlreadyPerformedError(msg) student.subjects.append(subject) session.commit() def add_teacher_to_subject(session: Session, teacher_id: int, subject_id: int) -> None: - teacher: Teacher | None = get(session, Type[Teacher], ident=teacher_id) - subject: Subject | None = get(session, Type[Subject], ident=subject_id) + teacher: Teacher | None = get(session, Teacher, ident=teacher_id) + subject: Subject | None = get(session, Subject, ident=subject_id) if subject in teacher.subjects: msg = f"Teacher with id {teacher_id} already has subject with id {subject_id}" - raise UniqueConstraintError(msg) + raise ActionAlreadyPerformedError(msg) teacher.subjects.append(subject) session.commit() def get_subjects_of_student(session: Session, student_id: int) -> list[SubjectDataclass]: - student: Student = get(session, Type[Student], ident=student_id) + student: Student = get(session, Student, ident=student_id) subjects: list[Subject] = student.subjects return [vak.to_domain_model() for vak in subjects] diff --git a/backend/domain/logic/submission.py b/backend/domain/logic/submission.py index c4229ebe..e4bb8318 100644 --- a/backend/domain/logic/submission.py +++ b/backend/domain/logic/submission.py @@ -1,10 +1,9 @@ from datetime import datetime -from typing import Type from sqlalchemy.orm import Session -from db.models.models import Student, Group, Submission -from domain.logic.basic_operations import get +from db.models.models import Group, Student, Submission +from domain.logic.basic_operations import get, get_all from domain.models.SubmissionDataclass import SubmissionDataclass, SubmissionState @@ -14,30 +13,38 @@ def create_submission( group_id: int, message: str, state: SubmissionState, - date_time: datetime + date_time: datetime, ) -> SubmissionDataclass: - student: Student = get(session, Type[Student], ident=student_id) - group: Group = get(session, Type[Group], ident=group_id) + student: Student = get(session, Student, ident=student_id) + group: Group = get(session, Group, ident=group_id) new_submission: Submission = Submission( student_id=student.id, group_id=group.id, message=message, state=state, - date_time=date_time + date_time=date_time, ) session.add(new_submission) session.commit() return new_submission.to_domain_model() +def get_submission(session: Session, submission_id: int) -> SubmissionDataclass: + return get(session, Submission, submission_id).to_domain_model() + + +def get_all_submissions(session: Session) -> list[SubmissionDataclass]: + return [submission.to_domain_model() for submission in get_all(session, Submission)] + + def get_submissions_of_student(session: Session, student_id: int) -> list[SubmissionDataclass]: - student: Student = get(session, Type[Student], ident=student_id) + student: Student = get(session, Student, ident=student_id) submissions: list[Submission] = student.submissions return [submission.to_domain_model() for submission in submissions] def get_submissions_of_group(session: Session, group_id: int) -> list[SubmissionDataclass]: - group: Group = get(session, Type[Group], ident=group_id) + group: Group = get(session, Group, ident=group_id) submissions: list[Submission] = group.submissions return [submission.to_domain_model() for submission in submissions] diff --git a/backend/domain/logic/teacher.py b/backend/domain/logic/teacher.py index 229320ee..1bb93b08 100644 --- a/backend/domain/logic/teacher.py +++ b/backend/domain/logic/teacher.py @@ -1,6 +1,7 @@ from sqlalchemy.orm import Session from db.models.models import Teacher +from domain.logic.basic_operations import get, get_all from domain.models.TeacherDataclass import TeacherDataclass @@ -11,6 +12,14 @@ def create_teacher(session: Session, name: str, email: str) -> TeacherDataclass: return new_teacher.to_domain_model() +def get_teacher(session: Session, teacher_id: int) -> TeacherDataclass: + return get(session, Teacher, teacher_id).to_domain_model() + + +def get_all_teachers(session: Session) -> list[TeacherDataclass]: + return [teacher.to_domain_model() for teacher in get_all(session, Teacher)] + + def is_user_teacher(session: Session, user_id: int) -> bool: teacher = session.get(Teacher, user_id) return teacher is not None diff --git a/backend/domain/logic/user.py b/backend/domain/logic/user.py new file mode 100644 index 00000000..4e902e57 --- /dev/null +++ b/backend/domain/logic/user.py @@ -0,0 +1,33 @@ +from sqlalchemy.orm import Session + +from db.models.models import 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 +from domain.logic.student import is_user_student +from domain.logic.teacher import is_user_teacher +from domain.models.APIUser import APIUser +from domain.models.UserDataclass import UserDataclass + + +def convert_user(session: Session, user: UserDataclass) -> APIUser: + api_user = APIUser(id=user.id, name=user.name, email=user.email, roles=[]) + + if is_user_teacher(session, user.id): + api_user.roles.append(Role.TEACHER) + + if is_user_admin(session, user.id): + api_user.roles.append(Role.ADMIN) + + if is_user_student(session, user.id): + api_user.roles.append(Role.STUDENT) + + return api_user + + +def get_user(session: Session, user_id: int) -> UserDataclass: + return get(session, User, user_id).to_domain_model() + + +def get_all_users(session: Session) -> list[UserDataclass]: + return [user.to_domain_model() for user in get_all(session, User)] diff --git a/backend/domain/models/APIUser.py b/backend/domain/models/APIUser.py index f1397bfd..c37fb477 100644 --- a/backend/domain/models/APIUser.py +++ b/backend/domain/models/APIUser.py @@ -1,8 +1,10 @@ from pydantic import BaseModel, EmailStr +from domain.logic.role_enum import Role + class APIUser(BaseModel): id: int name: str email: EmailStr - roles: list[str] + roles: list[Role] diff --git a/backend/fill_database_mock.py b/backend/fill_database_mock.py index da04f028..880048fa 100644 --- a/backend/fill_database_mock.py +++ b/backend/fill_database_mock.py @@ -1,55 +1,98 @@ from datetime import datetime from psycopg2 import tz +from sqlalchemy.orm import sessionmaker from db.extensions import Base, engine -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.SqlTeacherDAO import SqlTeacherDAO +from domain.logic.admin import create_admin +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.teacher import create_teacher if __name__ == "__main__": Base.metadata.create_all(engine) + SessionLocal = sessionmaker(autocommit=False, bind=engine) + session = SessionLocal() - admin_dao = SqlAdminDAO() - student_dao = SqlStudentDAO() - teacher_dao = SqlTeacherDAO() - subject_dao = SqlSubjectDAO() - group_dao = SqlGroupDAO() - project_dao = SqlProjectDAO() + # Create subjects + objeprog = create_subject(session, name="Objectgericht Programmeren") + algoritmen = create_subject(session, name="Algoritmen en Datastructuren") + webtech = create_subject(session, name="Webtechnologie") - # maak een vak - objeprog = subject_dao.create_subject(name="OBJECTGERICHTPROGRAMMEREN") - - # maak een project voor dat vak - objprog_project = project_dao.create_project( + # Create projects for subjects + objprog_project = create_project( + session=session, subject_id=objeprog.id, - name="PROJECT", + name="Flash Cards", archived=False, visible=True, - description="Maak iets in javafx", - requirements="Een zip bestand met java-code", + description="Maak iets in JavaFX", + requirements="Een zip bestand met Java-code", max_students=2, - deadline=datetime(2000, 1, 1, 0, 0, 0, tzinfo=tz.LOCAL), + deadline=datetime(2024, 12, 31, 23, 59, 59, tzinfo=tz.LOCAL), ) - # maak een groepje voor het project van objeprog - groep1 = group_dao.create_group(objprog_project.id) + algo_project = create_project( + session=session, + subject_id=algoritmen.id, + name="Sorteer Algoritmen Implementatie", + archived=False, + visible=True, + description="Implementeer verschillende sorteeralgoritmen", + requirements="Code in Python", + max_students=3, + deadline=datetime(2024, 11, 15, 23, 59, 59, tzinfo=tz.LOCAL), + ) + + web_project = create_project( + session=session, + subject_id=webtech.id, + name="Webshop", + archived=False, + visible=True, + description="Bouw een eenvoudige webshop", + requirements="Gebruik van HTML, CSS, en JavaScript", + max_students=4, + deadline=datetime(2024, 10, 30, 23, 59, 59, tzinfo=tz.LOCAL), + ) + + # Create groups for projects + groep1_objprog = create_group(session, objprog_project.id) + groep2_algo = create_group(session, algo_project.id) + groep3_web = create_group(session, web_project.id) + + # Create students + student1 = create_student(session, "Lukas", "lukas@gmail.com") + student2 = create_student(session, "Alberic", "alberic@gmail.com") + student3 = create_student(session, "Matthias", "matthias@gmail.com") + student4 = create_student(session, "Eva", "eva@gmail.com") + student5 = create_student(session, "Emma", "emma@gmail.com") + + # Create teachers + teacher1 = create_teacher(session, "Kris Coolsaet", "kris.coolsaet@ugent.be") + teacher2 = create_teacher(session, "Sophie Devolder", "sophie.devolder@ugent.be") + teacher3 = create_teacher(session, "Pieter-Jan De Smet", "pj.desmet@ugent.be") + + # Create admin + admin = create_admin(session, "Admin", "admin@gmail.com") + + # Add teachers to subjects + add_teacher_to_subject(session, teacher1.id, objeprog.id) + add_teacher_to_subject(session, teacher2.id, algoritmen.id) + add_teacher_to_subject(session, teacher3.id, webtech.id) - # maak studenten - student1 = student_dao.create_student("Student1", "Student1@gmail.com") - student2 = student_dao.create_student("Student2", "Student2@gmail.com") - student3 = student_dao.create_student("Student3", "Student3@gmail.com") + # Add students to groups + add_student_to_group(session, student1.id, groep1_objprog.id) + add_student_to_group(session, student2.id, groep1_objprog.id) + add_student_to_group(session, student3.id, groep1_objprog.id) - # maak teacher - teacher1 = teacher_dao.create_teacher("Teacher1", "Teacher1@gmail.com") + add_student_to_group(session, student4.id, groep2_algo.id) + add_student_to_group(session, student5.id, groep2_algo.id) - # voeg teacher toe aan objeprog - subject_dao.add_teacher_to_subject(teacher1.id, objeprog.id) + add_student_to_group(session, student1.id, groep3_web.id) + add_student_to_group(session, student3.id, groep3_web.id) + add_student_to_group(session, student5.id, groep3_web.id) - # voeg studenten toe aan de groep - group_dao.add_student_to_group(student1.id, groep1.id) - group_dao.add_student_to_group(student2.id, groep1.id) - group_dao.add_student_to_group(student3.id, groep1.id) + session.commit() diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 1539153d..eaabfa47 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -82,6 +82,7 @@ select = [ ignore = [ "D", # Docstrings "ANN101", # type annotation for self + "ARG001", "S201", # Use of `debug=True` in Flask app detected "INP001", # File `...` is part of an implicit namespace package "S101", # is nodig voor de testen diff --git a/backend/routes/db.py b/backend/routes/db.py deleted file mode 100644 index 584aedad..00000000 --- a/backend/routes/db.py +++ /dev/null @@ -1,6 +0,0 @@ -from db.implementation.SqlDAOProvider import SqlDAOProvider -from db.interface.DAOProvider import DAOProvider - - -def get_dao_provider() -> DAOProvider: - return SqlDAOProvider() diff --git a/backend/routes/dependencies/role_dependencies.py b/backend/routes/dependencies/role_dependencies.py new file mode 100644 index 00000000..a8e6e994 --- /dev/null +++ b/backend/routes/dependencies/role_dependencies.py @@ -0,0 +1,54 @@ +from fastapi import Depends +from sqlalchemy.orm import Session + +from db.sessions import get_session +from domain.logic.admin import get_admin, is_user_admin +from domain.logic.student import get_student, is_user_student +from domain.logic.subject import get_subjects_of_student +from domain.logic.teacher import get_teacher, is_user_teacher +from domain.models.AdminDataclass import AdminDataclass +from domain.models.StudentDataclass import StudentDataclass +from domain.models.TeacherDataclass import TeacherDataclass +from routes.errors.authentication import ( + InvalidAdminCredentialsError, + InvalidStudentCredentialsError, + InvalidTeacherCredentialsError, + StudentNotEnrolledError, +) + + +def get_authenticated_user() -> int: + return 1 # Checken of een user bestaat en/of hij de juiste credentials heeft. + + +def get_authenticated_admin(session: Session = Depends(get_session)) -> AdminDataclass: + user_id = get_authenticated_user() + if not is_user_admin(session, user_id): + raise InvalidAdminCredentialsError + return get_admin(session, user_id) + + +def get_authenticated_teacher(session: Session = Depends(get_session)) -> TeacherDataclass: + user_id = get_authenticated_user() + if not is_user_teacher(session, user_id): + raise InvalidTeacherCredentialsError + return get_teacher(session, user_id) + + +def get_authenticated_student(session: Session = Depends(get_session)) -> StudentDataclass: + user_id = get_authenticated_user() + if not is_user_student(session, user_id): + raise InvalidStudentCredentialsError + return get_student(session, user_id) + + +def get_authenticated_student_for_subject( + subject_id: int, + session: Session = Depends(get_session), + student: StudentDataclass = Depends(get_authenticated_student), +) -> StudentDataclass: + subjects_of_student = get_subjects_of_student(session, student.id) + if subject_id not in [subject.id for subject in subjects_of_student]: + raise StudentNotEnrolledError + return student + diff --git a/backend/routes/errors/authentication.py b/backend/routes/errors/authentication.py new file mode 100644 index 00000000..f1437edf --- /dev/null +++ b/backend/routes/errors/authentication.py @@ -0,0 +1,18 @@ +class InvalidRoleCredentialsError(Exception): + ERROR_MESSAGE = "User does not have the required role" + + +class InvalidAdminCredentialsError(InvalidRoleCredentialsError): + ERROR_MESSAGE = "User does not have the required admin role" + + +class InvalidTeacherCredentialsError(InvalidRoleCredentialsError): + ERROR_MESSAGE = "User does not have the required teacher role" + + +class InvalidStudentCredentialsError(InvalidRoleCredentialsError): + ERROR_MESSAGE = "User does not have the required student role" + + +class StudentNotEnrolledError(Exception): + ERROR_MESSAGE = "Student is not enrolled in the subject" diff --git a/backend/routes/exception_handlers.py b/backend/routes/exception_handlers.py new file mode 100644 index 00000000..a0076c22 --- /dev/null +++ b/backend/routes/exception_handlers.py @@ -0,0 +1,38 @@ +from fastapi import Request, status +from fastapi.responses import JSONResponse + +from app import app +from db.errors.database_errors import ActionAlreadyPerformedError, ItemNotFoundError +from routes.errors.authentication import InvalidRoleCredentialsError, StudentNotEnrolledError + + +@app.exception_handler(InvalidRoleCredentialsError) +def invalid_admin_credentials_error_handler(request: Request, exc: InvalidRoleCredentialsError) -> JSONResponse: + return JSONResponse( + status_code=status.HTTP_403_FORBIDDEN, + content={"message": exc.ERROR_MESSAGE}, + ) + + +@app.exception_handler(ItemNotFoundError) +def item_not_found_error_handler(request: Request, exc: ItemNotFoundError) -> JSONResponse: + return JSONResponse( + status_code=status.HTTP_404_NOT_FOUND, + content={"detail": str(exc)}, + ) + + +@app.exception_handler(StudentNotEnrolledError) +def student_already_enrolled_error_handler(request: Request, exc: StudentNotEnrolledError) -> JSONResponse: + return JSONResponse( + status_code=status.HTTP_400_BAD_REQUEST, + content={"detail": str(exc)}, + ) + + +@app.exception_handler(ActionAlreadyPerformedError) +def action_already_performed_error_handler(request: Request, exc: ActionAlreadyPerformedError) -> JSONResponse: + return JSONResponse( + status_code=status.HTTP_400_BAD_REQUEST, + content={"detail": str(exc)}, + ) diff --git a/backend/routes/group.py b/backend/routes/group.py new file mode 100644 index 00000000..e69de29b diff --git a/backend/routes/login.py b/backend/routes/login.py index d2e32ced..e69de29b 100644 --- a/backend/routes/login.py +++ b/backend/routes/login.py @@ -1,12 +0,0 @@ -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) diff --git a/backend/routes/projects.py b/backend/routes/projects.py deleted file mode 100644 index e73b6574..00000000 --- a/backend/routes/projects.py +++ /dev/null @@ -1,42 +0,0 @@ -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 diff --git a/backend/routes/student.py b/backend/routes/student.py new file mode 100644 index 00000000..006c3d2d --- /dev/null +++ b/backend/routes/student.py @@ -0,0 +1,36 @@ +from fastapi import APIRouter, Depends +from sqlalchemy.orm import Session + +from db.sessions import get_session +from domain.logic.project import get_project, get_projects_of_subject +from domain.logic.subject import get_subject, get_subjects_of_student +from domain.models.ProjectDataclass import ProjectDataclass +from domain.models.StudentDataclass import StudentDataclass +from domain.models.SubjectDataclass import SubjectDataclass +from routes.dependencies.role_dependencies import get_authenticated_student, get_authenticated_student_for_subject + +student_router = APIRouter() + + +@student_router.get("/student/subjects") +def subjects_of_student_get( + session: Session = Depends(get_session), + student: StudentDataclass = Depends(get_authenticated_student), +) -> list[SubjectDataclass]: + return get_subjects_of_student(session, student.id) + + +@student_router.get("/student/subjects/{subject_id}", dependencies=[Depends(get_authenticated_student)]) +def subject_get(subject_id: int, session: Session = Depends(get_session)) -> SubjectDataclass: + return get_subject(session, subject_id) + + +@student_router.get("/student/subjects/{subject_id}/projects", dependencies=[Depends(get_authenticated_student)]) +def get_subject_projects(subject_id: int, session: Session = Depends(get_session)) -> list[ProjectDataclass]: + return get_projects_of_subject(session, subject_id) + + +@student_router.get("/student/subjects/{subject_id}/projects/{project_id}") +def get_subject_project(subject_id: int, project_id: int, session: Session = Depends(get_session)) -> ProjectDataclass: + get_authenticated_student_for_subject(subject_id) + return get_project(session, project_id) diff --git a/backend/routes/subjects.py b/backend/routes/subjects.py deleted file mode 100644 index db602a81..00000000 --- a/backend/routes/subjects.py +++ /dev/null @@ -1,44 +0,0 @@ -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 diff --git a/backend/routes/teacher.py b/backend/routes/teacher.py new file mode 100644 index 00000000..604f5774 --- /dev/null +++ b/backend/routes/teacher.py @@ -0,0 +1,31 @@ +from fastapi import APIRouter, Depends, status +from sqlalchemy.orm import Session +from starlette.responses import Response + +from db.sessions import get_session +from domain.logic.subject import create_subject, get_subjects_of_teacher +from domain.models.SubjectDataclass import SubjectDataclass +from domain.models.TeacherDataclass import TeacherDataclass +from routes.dependencies.role_dependencies import get_authenticated_teacher + +teacher_router = APIRouter() + + +@teacher_router.get("/teacher/subjects") +def subjects_of_teacher_get( + session: Session = Depends(get_session), + teacher: TeacherDataclass = Depends(get_authenticated_teacher), +) -> list[SubjectDataclass]: + return get_subjects_of_teacher(session, teacher.id) + + +@teacher_router.post("teacher/subjects") +def create_subject_post( + subject: SubjectDataclass, + session: Session = Depends(get_session), +) -> Response: + create_subject(session, name=subject.name) + return Response(status_code=status.HTTP_201_CREATED) + + + diff --git a/backend/routes/user.py b/backend/routes/user.py new file mode 100644 index 00000000..c14c52d4 --- /dev/null +++ b/backend/routes/user.py @@ -0,0 +1,33 @@ +from fastapi import APIRouter, Depends +from sqlalchemy.orm import Session + +from db.models.models import User +from db.sessions import get_session +from domain.logic.basic_operations import get, get_all +from domain.logic.user import convert_user +from domain.models.APIUser import APIUser +from domain.models.UserDataclass import UserDataclass +from routes.dependencies.role_dependencies import get_authenticated_admin, get_authenticated_user + +users_router = APIRouter() + + +@users_router.get("/user") +def get_current_user( + session: Session = Depends(get_session), + user_id: int = Depends(get_authenticated_user), +) -> APIUser: + user: UserDataclass = get(session, User, user_id).to_domain_model() + return convert_user(session, user) + + +@users_router.get("/users", dependencies=[Depends(get_authenticated_admin)]) +def get_users(session: Session = Depends(get_session)) -> list[APIUser]: + users: list[UserDataclass] = [user.to_domain_model() for user in get_all(session, User)] + return [convert_user(session, user) for user in users] + + +@users_router.get("/users/{uid}", dependencies=[Depends(get_authenticated_admin)]) +def get_user(uid: int, session: Session = Depends(get_session)) -> APIUser: + user: UserDataclass = get(session, User, uid).to_domain_model() + return convert_user(session, user) diff --git a/backend/routes/users.py b/backend/routes/users.py deleted file mode 100644 index 0c08846e..00000000 --- a/backend/routes/users.py +++ /dev/null @@ -1,36 +0,0 @@ -from fastapi import APIRouter, HTTPException, status - -from db.errors.database_errors import ItemNotFoundError -from domain.logic.UserLogic import convert_user -from domain.models.APIUser import APIUser -from routes.db import get_dao_provider -from routes.login import get_authenticated_user, is_user_admin - -users_router = APIRouter() - - -@users_router.get("/user") -def get_current_user() -> APIUser: - user = get_authenticated_user() - return convert_user(user, get_dao_provider()) - - -@users_router.get("/users") -def get_users() -> list[APIUser]: - if not is_user_admin(): - raise HTTPException(status_code=status.HTTP_403_FORBIDDEN) - user_dao = get_dao_provider().get_user_dao() - users = user_dao.get_all() - return [convert_user(user, get_dao_provider()) for user in users] - - -@users_router.get("/users/{uid}") -def get_user(uid: int) -> APIUser: - if not is_user_admin(): - raise HTTPException(status_code=status.HTTP_403_FORBIDDEN) - user_dao = get_dao_provider().get_user_dao() - try: - user = user_dao.get(uid) - except ItemNotFoundError as err: - raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) from err - return convert_user(user, get_dao_provider()) From 2b0677025c419ef299905d39c7f8169a911e6b4f Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Thu, 7 Mar 2024 15:54:49 +0100 Subject: [PATCH 10/16] teacher moet geauthenticeerd bij het aanmaken van een subject #45 --- backend/routes/teacher.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/routes/teacher.py b/backend/routes/teacher.py index 604f5774..2f7c4f61 100644 --- a/backend/routes/teacher.py +++ b/backend/routes/teacher.py @@ -19,7 +19,7 @@ def subjects_of_teacher_get( return get_subjects_of_teacher(session, teacher.id) -@teacher_router.post("teacher/subjects") +@teacher_router.post("teacher/subjects", dependencies=[Depends(get_authenticated_teacher)]) def create_subject_post( subject: SubjectDataclass, session: Session = Depends(get_session), From 80a4939815f49b4e1163bb5f3f1d1e4761b99ce3 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Thu, 7 Mar 2024 16:05:22 +0100 Subject: [PATCH 11/16] aanmaken subjects en projects enpoints die niet meer verbonden zijn aan student #45 --- backend/app.py | 4 ++++ backend/routes/project.py | 13 +++++++++++++ backend/routes/student.py | 22 ++-------------------- backend/routes/subject.py | 21 +++++++++++++++++++++ 4 files changed, 40 insertions(+), 20 deletions(-) create mode 100644 backend/routes/project.py create mode 100644 backend/routes/subject.py diff --git a/backend/app.py b/backend/app.py index d53e552f..caaff4f2 100644 --- a/backend/app.py +++ b/backend/app.py @@ -6,7 +6,9 @@ from db.errors.database_errors import ActionAlreadyPerformedError, ItemNotFoundError from routes.errors.authentication import InvalidRoleCredentialsError, StudentNotEnrolledError +from routes.project import project_router from routes.student import student_router +from routes.subject import subject_router from routes.teacher import teacher_router from routes.user import users_router @@ -16,6 +18,8 @@ app.include_router(student_router, prefix="/api") app.include_router(teacher_router, prefix="/api") app.include_router(users_router, prefix="/api") +app.include_router(project_router, prefix="/api") +app.include_router(subject_router, prefix="/api") # Koppel de exception handlers diff --git a/backend/routes/project.py b/backend/routes/project.py new file mode 100644 index 00000000..c3392ea1 --- /dev/null +++ b/backend/routes/project.py @@ -0,0 +1,13 @@ +from fastapi import APIRouter, Depends +from sqlalchemy.orm import Session + +from db.sessions import get_session +from domain.logic.project import get_project +from domain.models.ProjectDataclass import ProjectDataclass + +project_router = APIRouter() + + +@project_router.get("/projects/{project_id}") +def get_subject_project(project_id: int, session: Session = Depends(get_session)) -> ProjectDataclass: + return get_project(session, project_id) diff --git a/backend/routes/student.py b/backend/routes/student.py index 006c3d2d..2f3845e1 100644 --- a/backend/routes/student.py +++ b/backend/routes/student.py @@ -2,12 +2,10 @@ from sqlalchemy.orm import Session from db.sessions import get_session -from domain.logic.project import get_project, get_projects_of_subject -from domain.logic.subject import get_subject, get_subjects_of_student -from domain.models.ProjectDataclass import ProjectDataclass +from domain.logic.subject import get_subjects_of_student from domain.models.StudentDataclass import StudentDataclass from domain.models.SubjectDataclass import SubjectDataclass -from routes.dependencies.role_dependencies import get_authenticated_student, get_authenticated_student_for_subject +from routes.dependencies.role_dependencies import get_authenticated_student student_router = APIRouter() @@ -18,19 +16,3 @@ def subjects_of_student_get( student: StudentDataclass = Depends(get_authenticated_student), ) -> list[SubjectDataclass]: return get_subjects_of_student(session, student.id) - - -@student_router.get("/student/subjects/{subject_id}", dependencies=[Depends(get_authenticated_student)]) -def subject_get(subject_id: int, session: Session = Depends(get_session)) -> SubjectDataclass: - return get_subject(session, subject_id) - - -@student_router.get("/student/subjects/{subject_id}/projects", dependencies=[Depends(get_authenticated_student)]) -def get_subject_projects(subject_id: int, session: Session = Depends(get_session)) -> list[ProjectDataclass]: - return get_projects_of_subject(session, subject_id) - - -@student_router.get("/student/subjects/{subject_id}/projects/{project_id}") -def get_subject_project(subject_id: int, project_id: int, session: Session = Depends(get_session)) -> ProjectDataclass: - get_authenticated_student_for_subject(subject_id) - return get_project(session, project_id) diff --git a/backend/routes/subject.py b/backend/routes/subject.py new file mode 100644 index 00000000..14c3e473 --- /dev/null +++ b/backend/routes/subject.py @@ -0,0 +1,21 @@ +from fastapi import APIRouter, Depends +from sqlalchemy.orm import Session + +from db.sessions import get_session +from domain.logic.project import get_projects_of_subject +from domain.logic.subject import get_subject +from domain.models.ProjectDataclass import ProjectDataclass +from domain.models.SubjectDataclass import SubjectDataclass +from routes.dependencies.role_dependencies import get_authenticated_student + +subject_router = APIRouter() + + +@subject_router.get("/subjects/{subject_id}", dependencies=[Depends(get_authenticated_student)]) +def subject_get(subject_id: int, session: Session = Depends(get_session)) -> SubjectDataclass: + return get_subject(session, subject_id) + + +@subject_router.get("/subjects/{subject_id}/projects", dependencies=[Depends(get_authenticated_student)]) +def get_subject_projects(subject_id: int, session: Session = Depends(get_session)) -> list[ProjectDataclass]: + return get_projects_of_subject(session, subject_id) From 5ebf48480a872c28b2a88100e4a394927e0f26e4 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Thu, 7 Mar 2024 16:30:42 +0100 Subject: [PATCH 12/16] aanmaken 'is_user_authenticated_for_subject' #45 --- backend/routes/dependencies/role_dependencies.py | 16 +++++++++++++++- backend/routes/subject.py | 7 +++++-- backend/routes/teacher.py | 2 +- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/backend/routes/dependencies/role_dependencies.py b/backend/routes/dependencies/role_dependencies.py index a8e6e994..9c3eeb23 100644 --- a/backend/routes/dependencies/role_dependencies.py +++ b/backend/routes/dependencies/role_dependencies.py @@ -4,10 +4,11 @@ from db.sessions import get_session from domain.logic.admin import get_admin, is_user_admin from domain.logic.student import get_student, is_user_student -from domain.logic.subject import get_subjects_of_student +from domain.logic.subject import get_subjects_of_student, get_subjects_of_teacher from domain.logic.teacher import get_teacher, is_user_teacher from domain.models.AdminDataclass import AdminDataclass from domain.models.StudentDataclass import StudentDataclass +from domain.models.SubjectDataclass import SubjectDataclass from domain.models.TeacherDataclass import TeacherDataclass from routes.errors.authentication import ( InvalidAdminCredentialsError, @@ -42,6 +43,19 @@ def get_authenticated_student(session: Session = Depends(get_session)) -> Studen return get_student(session, user_id) +def is_user_authorized_for_subject(session: Session, subject_id: int) -> bool: + user_id = get_authenticated_user() + if is_user_teacher(session, user_id): + subjects_of_teacher: list[SubjectDataclass] = get_subjects_of_teacher(session, subject_id) + return subject_id in [subject.id for subject in subjects_of_teacher] + + if is_user_student(session, user_id): + subjects_of_student: list[SubjectDataclass] = get_subjects_of_student(session, subject_id) + return subject_id in [subject.id for subject in subjects_of_student] + + return False + + def get_authenticated_student_for_subject( subject_id: int, session: Session = Depends(get_session), diff --git a/backend/routes/subject.py b/backend/routes/subject.py index 14c3e473..fdec830c 100644 --- a/backend/routes/subject.py +++ b/backend/routes/subject.py @@ -6,7 +6,10 @@ from domain.logic.subject import get_subject from domain.models.ProjectDataclass import ProjectDataclass from domain.models.SubjectDataclass import SubjectDataclass -from routes.dependencies.role_dependencies import get_authenticated_student +from routes.dependencies.role_dependencies import ( + get_authenticated_student, + is_user_authorized_for_subject, +) subject_router = APIRouter() @@ -16,6 +19,6 @@ def subject_get(subject_id: int, session: Session = Depends(get_session)) -> Sub return get_subject(session, subject_id) -@subject_router.get("/subjects/{subject_id}/projects", dependencies=[Depends(get_authenticated_student)]) +@subject_router.get("/subjects/{subject_id}/projects", dependencies=[Depends(is_user_authorized_for_subject)]) def get_subject_projects(subject_id: int, session: Session = Depends(get_session)) -> list[ProjectDataclass]: return get_projects_of_subject(session, subject_id) diff --git a/backend/routes/teacher.py b/backend/routes/teacher.py index 2f7c4f61..6ccdbe4c 100644 --- a/backend/routes/teacher.py +++ b/backend/routes/teacher.py @@ -19,7 +19,7 @@ def subjects_of_teacher_get( return get_subjects_of_teacher(session, teacher.id) -@teacher_router.post("teacher/subjects", dependencies=[Depends(get_authenticated_teacher)]) +@teacher_router.post("/teacher/subjects", dependencies=[Depends(get_authenticated_teacher)]) def create_subject_post( subject: SubjectDataclass, session: Session = Depends(get_session), From c9d1feaa648668e257d784e7baa768b86075e57b Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Thu, 7 Mar 2024 16:34:42 +0100 Subject: [PATCH 13/16] verwijderen code duplicatie #45 --- backend/domain/logic/subject.py | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/backend/domain/logic/subject.py b/backend/domain/logic/subject.py index 38e470e9..7fff59fb 100644 --- a/backend/domain/logic/subject.py +++ b/backend/domain/logic/subject.py @@ -3,22 +3,7 @@ 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 -from domain.models.UserDataclass import UserDataclass - - -def is_user_authorized_for_subject(session: Session, subject: SubjectDataclass, user: UserDataclass) -> bool: - if is_user_teacher(session, user.id): - subjects_of_teacher: list[SubjectDataclass] = get_subjects_of_teacher(session, subject.id) - return subject in subjects_of_teacher - - if is_user_student(session, user.id): - subjects_of_student: list[SubjectDataclass] = get_subjects_of_student(session, subject.id) - return subject in subjects_of_student - - return False def create_subject(session: Session, name: str) -> SubjectDataclass: From cdfeb911fb63afc072614374ccbdcb671602dc66 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Thu, 7 Mar 2024 16:50:14 +0100 Subject: [PATCH 14/16] authenticatie nodig bij bekijken project #45 --- backend/routes/project.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/backend/routes/project.py b/backend/routes/project.py index c3392ea1..fa90c918 100644 --- a/backend/routes/project.py +++ b/backend/routes/project.py @@ -4,10 +4,13 @@ from db.sessions import get_session from domain.logic.project import get_project from domain.models.ProjectDataclass import ProjectDataclass +from routes.dependencies.role_dependencies import is_user_authorized_for_subject project_router = APIRouter() @project_router.get("/projects/{project_id}") def get_subject_project(project_id: int, session: Session = Depends(get_session)) -> ProjectDataclass: - return get_project(session, project_id) + project: ProjectDataclass = get_project(session, project_id) + is_user_authorized_for_subject(session, project.subject_id) + return project From 66b1bcaae24cbc102945da717deffb30ee045591 Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Thu, 7 Mar 2024 16:54:04 +0100 Subject: [PATCH 15/16] everyone can see a subject #45 --- backend/routes/dependencies/role_dependencies.py | 2 +- backend/routes/subject.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/routes/dependencies/role_dependencies.py b/backend/routes/dependencies/role_dependencies.py index 9c3eeb23..abe7619c 100644 --- a/backend/routes/dependencies/role_dependencies.py +++ b/backend/routes/dependencies/role_dependencies.py @@ -43,7 +43,7 @@ def get_authenticated_student(session: Session = Depends(get_session)) -> Studen return get_student(session, user_id) -def is_user_authorized_for_subject(session: Session, subject_id: int) -> bool: +def is_user_authorized_for_subject(subject_id: int, session: Session = Depends(get_session)) -> bool: user_id = get_authenticated_user() if is_user_teacher(session, user_id): subjects_of_teacher: list[SubjectDataclass] = get_subjects_of_teacher(session, subject_id) diff --git a/backend/routes/subject.py b/backend/routes/subject.py index fdec830c..6a4396cc 100644 --- a/backend/routes/subject.py +++ b/backend/routes/subject.py @@ -7,14 +7,14 @@ from domain.models.ProjectDataclass import ProjectDataclass from domain.models.SubjectDataclass import SubjectDataclass from routes.dependencies.role_dependencies import ( - get_authenticated_student, + get_authenticated_user, is_user_authorized_for_subject, ) subject_router = APIRouter() -@subject_router.get("/subjects/{subject_id}", dependencies=[Depends(get_authenticated_student)]) +@subject_router.get("/subjects/{subject_id}", dependencies=[Depends(get_authenticated_user)]) def subject_get(subject_id: int, session: Session = Depends(get_session)) -> SubjectDataclass: return get_subject(session, subject_id) From b6c9782310777c9157cfd36de04ff3d672be8e2d Mon Sep 17 00:00:00 2001 From: Lukas Barragan Torres Date: Thu, 7 Mar 2024 16:57:01 +0100 Subject: [PATCH 16/16] fix depends session #45 --- backend/routes/project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/routes/project.py b/backend/routes/project.py index fa90c918..414b81b0 100644 --- a/backend/routes/project.py +++ b/backend/routes/project.py @@ -12,5 +12,5 @@ @project_router.get("/projects/{project_id}") def get_subject_project(project_id: int, session: Session = Depends(get_session)) -> ProjectDataclass: project: ProjectDataclass = get_project(session, project_id) - is_user_authorized_for_subject(session, project.subject_id) + is_user_authorized_for_subject(project.subject_id) return project