diff --git a/backend/app/config.py b/backend/app/config.py index 55363e4..a4ffad5 100644 --- a/backend/app/config.py +++ b/backend/app/config.py @@ -8,6 +8,7 @@ class Config: BASE_DIR = Path(__file__).resolve().parent.parent SRC_DIR = BASE_DIR / "app" EXCLUDED_DIRS = ["__pycache__", "commands"] + TIMEZONE = 'Europe/Warsaw' ENABLE_CORS = os.environ.get('ENABLE_CORS') or False diff --git a/backend/app/coordinator/routes/enrollments.py b/backend/app/coordinator/routes/enrollments.py index ee407fc..19fcf56 100644 --- a/backend/app/coordinator/routes/enrollments.py +++ b/backend/app/coordinator/routes/enrollments.py @@ -38,7 +38,7 @@ def create_enrollments(examination_schedule_id: int, data: dict) -> dict: for i in range(amount): sd = start_date + datetime.timedelta(minutes=i * prt) ed = start_date + datetime.timedelta(minutes=(i + 1) * prt) - enrollment = Enrollment(start_date=sd, end_date=ed) + enrollment = Enrollment(start_date=sd, end_date=ed, examination_schedule_id=examination_schedule_id) enrollments.append(enrollment) db.session.add_all(enrollments) db.session.commit() diff --git a/backend/app/coordinator/schemas/enrollments.py b/backend/app/coordinator/schemas/enrollments.py index 713cc38..7367e58 100644 --- a/backend/app/coordinator/schemas/enrollments.py +++ b/backend/app/coordinator/schemas/enrollments.py @@ -6,6 +6,3 @@ from ..validators import validate_datetime_greater_than_now class EnrollmentCreateSchema(Schema): start_date = fields.DateTime(validate=validate_datetime_greater_than_now, required=True) end_date = fields.DateTime(validate=validate_datetime_greater_than_now, required=True) - - class Meta: - datetimeformat = '%Y-%m-%d %H:%M:%S' diff --git a/backend/app/coordinator/schemas/examination_schedule.py b/backend/app/coordinator/schemas/examination_schedule.py index 84c666e..5863c21 100644 --- a/backend/app/coordinator/schemas/examination_schedule.py +++ b/backend/app/coordinator/schemas/examination_schedule.py @@ -11,9 +11,6 @@ class ExaminationScheduleUpdateSchema(Schema): start_date = fields.DateTime(validate=validate_datetime_greater_than_now, required=True) end_date = fields.DateTime(validate=validate_datetime_greater_than_now, required=True) - class Meta: - datetimeformat = '%Y-%m-%d %H:%M:%S' - class ExaminationScheduleListItemSchema(Schema): id = fields.Integer(required=True) diff --git a/backend/app/coordinator/validators.py b/backend/app/coordinator/validators.py index e47e317..b74bc1e 100644 --- a/backend/app/coordinator/validators.py +++ b/backend/app/coordinator/validators.py @@ -11,5 +11,5 @@ def validate_index(index: int) -> None: def validate_datetime_greater_than_now(date: datetime) -> None: - if date < datetime.now(): + if date.timestamp() < datetime.utcnow().timestamp(): raise ValidationError("Date must be greater than NOW!") diff --git a/backend/app/examination_schedule/routes/enrollments.py b/backend/app/examination_schedule/routes/enrollments.py index dd34cd2..945f6e1 100644 --- a/backend/app/examination_schedule/routes/enrollments.py +++ b/backend/app/examination_schedule/routes/enrollments.py @@ -1,9 +1,61 @@ +import datetime + from apiflask import APIBlueprint +from flask import abort + +from ..schemas import EnrollmentPaginationSchema, EnrollmentQuerySchema +from ..utils import check_examination_schedule_is_exist, get_list_of_enrollments_response bp = APIBlueprint("enrollments", __name__, url_prefix="/enrollments") -# list enrollments in examination schedule module for students, coordinator and project_supervisor -@bp.get('/') -def list_enrollments() -> dict: - pass +# list of the exam registration for students +@bp.get('//student-view') +@bp.input(EnrollmentQuerySchema, location='query') +@bp.output(EnrollmentPaginationSchema) +def list_enrollments_for_students(examination_schedule_id: int, query: dict) -> dict: + page = query.get('page') + per_page = query.get('per_page') + + examination_schedule = check_examination_schedule_is_exist(examination_schedule_id) + + now = datetime.datetime.utcnow() + if examination_schedule.start_date is None or examination_schedule.end_date is None: + abort(403, "Forbidden! The examination schedule is not available yet") + + if examination_schedule.start_date.timestamp() > now.timestamp(): + abort(403, "Forbidden! Enrollments haven't just started!") + + if examination_schedule.end_date.timestamp() < now.timestamp(): + abort(400, "The exam registration has just finished!") + + return get_list_of_enrollments_response(examination_schedule_id, page, per_page) + + +@bp.get('//coordinator-view/') +@bp.input(EnrollmentQuerySchema, location='query') +@bp.output(EnrollmentPaginationSchema) +def list_enrollments_for_coordinator(examination_schedule_id: int, query: dict) -> dict: + page = query.get('page') + per_page = query.get('per_page') + + check_examination_schedule_is_exist(examination_schedule_id) + + return get_list_of_enrollments_response(examination_schedule_id, page, per_page) + + +@bp.get('//project-supervisor-view/') +@bp.input(EnrollmentQuerySchema, location='query') +@bp.output(EnrollmentPaginationSchema) +def list_enrollments_for_project_supervisor(examination_schedule_id: int, query: dict) -> dict: + page = query.get('page') + per_page = query.get('per_page') + + examination_schedule = check_examination_schedule_is_exist(examination_schedule_id) + + now = datetime.datetime.utcnow() + + if examination_schedule.start_date.timestamp() < now.timestamp(): + abort(403, "Forbidden! Enrollment has just started! You cannot assign to the exam committees!") + + return get_list_of_enrollments_response(examination_schedule_id, page, per_page) diff --git a/backend/app/examination_schedule/schemas.py b/backend/app/examination_schedule/schemas.py index e69de29..e220f56 100644 --- a/backend/app/examination_schedule/schemas.py +++ b/backend/app/examination_schedule/schemas.py @@ -0,0 +1,31 @@ +from marshmallow import fields, validate, Schema + + +class ProjectSupervisorSchema(Schema): + first_name = fields.Str() + last_name = fields.Str() + + +class CommitteeSchema(Schema): + members = fields.List(fields.Nested(ProjectSupervisorSchema)) + + +class GroupSchema(Schema): + name = fields.Str() + + +class EnrollmentSchema(Schema): + start_date = fields.DateTime() + end_date = fields.DateTime() + committee = fields.Nested(CommitteeSchema) + group = fields.Nested(GroupSchema) + + +class EnrollmentPaginationSchema(Schema): + enrollments = fields.List(fields.Nested(EnrollmentSchema)) + max_pages = fields.Integer() + + +class EnrollmentQuerySchema(Schema): + page = fields.Integer() + per_page = fields.Integer() diff --git a/backend/app/examination_schedule/utils.py b/backend/app/examination_schedule/utils.py new file mode 100644 index 0000000..b2cb0fa --- /dev/null +++ b/backend/app/examination_schedule/utils.py @@ -0,0 +1,27 @@ +from flask import abort + +from ..dependencies import db +from .models import Enrollment, Committee, ExaminationSchedule +from ..students.models import Group +from ..base.utils import paginate_models + + +def check_examination_schedule_is_exist(examination_schedule_id: int) -> ExaminationSchedule: + examination_schedule = db.session.query(ExaminationSchedule).filter( + ExaminationSchedule.id == examination_schedule_id).first() + + if examination_schedule is None: + abort(404, "Examination Schedule doesn't exist!") + + return examination_schedule + + +def get_list_of_enrollments_response(examination_schedule_id: int, page: int = None, per_page: int = None) -> dict: + enrollments_query = db.session.query(Enrollment). \ + join(Group, isouter=True).join(Committee, isouter=True). \ + join(ExaminationSchedule, isouter=True). \ + filter(ExaminationSchedule.id == examination_schedule_id) + + data = paginate_models(page, enrollments_query, per_page) + + return {"enrollments": data["items"], "max_pages": data["max_pages"]} diff --git a/backend/app/students/models.py b/backend/app/students/models.py index 66b356a..637b52a 100644 --- a/backend/app/students/models.py +++ b/backend/app/students/models.py @@ -5,6 +5,7 @@ from ..base.models import Person, Base from ..base.utils import order_by_column_name from ..examination_schedule.models import Enrollment + class Group(Base): __tablename__ = "groups"