From 55a08d34068ce4302832aa8cd0bdf39a6e29be72 Mon Sep 17 00:00:00 2001 From: dominik24c Date: Wed, 26 Oct 2022 12:41:22 +0200 Subject: [PATCH] add examination schedule module and endpoint to set date of enrollment for students --- backend/app/coordinator/routes/__init__.py | 2 + .../routes/examination_schedule.py | 63 +++++++++ backend/app/coordinator/schemas.py | 127 ------------------ backend/app/coordinator/schemas/__init__.py | 8 ++ .../schemas/examination_schedule.py | 32 +++++ backend/app/coordinator/schemas/groups.py | 28 ++++ .../coordinator/schemas/project_supervisor.py | 36 +++++ backend/app/coordinator/schemas/students.py | 68 ++++++++++ backend/app/coordinator/validators.py | 15 +++ backend/app/examination_schedule/models.py | 4 +- .../{c7adfbd3c67f_.py => 9874e26f2b87_.py} | 10 +- 11 files changed, 259 insertions(+), 134 deletions(-) create mode 100644 backend/app/coordinator/routes/examination_schedule.py delete mode 100644 backend/app/coordinator/schemas.py create mode 100644 backend/app/coordinator/schemas/__init__.py create mode 100644 backend/app/coordinator/schemas/examination_schedule.py create mode 100644 backend/app/coordinator/schemas/groups.py create mode 100644 backend/app/coordinator/schemas/project_supervisor.py create mode 100644 backend/app/coordinator/schemas/students.py create mode 100644 backend/app/coordinator/validators.py rename backend/migrations/versions/{c7adfbd3c67f_.py => 9874e26f2b87_.py} (91%) diff --git a/backend/app/coordinator/routes/__init__.py b/backend/app/coordinator/routes/__init__.py index 6da1701..1fc05b4 100644 --- a/backend/app/coordinator/routes/__init__.py +++ b/backend/app/coordinator/routes/__init__.py @@ -3,9 +3,11 @@ from flask import Blueprint from .students import bp as students_bp from .project_supervisor import bp as project_supervisor_bp from .groups import bp as groups_bp +from .examination_schedule import bp as examination_schedule_bp bp = Blueprint("coordinator", __name__, url_prefix="/coordinator") bp.register_blueprint(students_bp) bp.register_blueprint(project_supervisor_bp) bp.register_blueprint(groups_bp) +bp.register_blueprint(examination_schedule_bp) diff --git a/backend/app/coordinator/routes/examination_schedule.py b/backend/app/coordinator/routes/examination_schedule.py new file mode 100644 index 0000000..e4899f5 --- /dev/null +++ b/backend/app/coordinator/routes/examination_schedule.py @@ -0,0 +1,63 @@ +from apiflask import APIBlueprint +from flask import abort + +from ..schemas import ExaminationScheduleSchema, ExaminationScheduleUpdateSchema, MessageSchema, \ + ExaminationSchedulesQuerySchema, ExaminationSchedulesPaginationSchema +from ...examination_schedule.models import ExaminationSchedule +from ...dependencies import db +from ...base.utils import paginate_models + +bp = APIBlueprint("examination_schedule", __name__, url_prefix="/examination_schedule") + + +@bp.get('/') +@bp.input(ExaminationSchedulesQuerySchema, location='query') +@bp.output(ExaminationSchedulesPaginationSchema) +def list_examination_schedule(query: dict) -> dict: + page = query.get('page') + per_page = query.get('per_page') + data = paginate_models(page, ExaminationSchedule.query, per_page) + return {'examination_schedules': data['items'], 'max_pages': data['max_pages']} + + +@bp.post('/') +@bp.input(ExaminationScheduleSchema) +@bp.output(MessageSchema) +def create_examination_schedule(data: dict) -> dict: + examination_schedule = ExaminationSchedule(**data) + db.session.add(examination_schedule) + db.session.commit() + + return {"message": "Examination schedule was created!"} + + +@bp.put('//') +@bp.input(ExaminationScheduleSchema) +@bp.output(MessageSchema) +def update_title_examination_schedule(id: int, data: dict) -> dict: + examination_schedule_query = db.session.query(ExaminationSchedule).filter(ExaminationSchedule.id == id) + examination_schedule = examination_schedule_query.first() + + if examination_schedule is None: + abort(404, "Examination schedule doesn't exist!") + examination_schedule_query.update(data) + db.session.commit() + return {"message": "Examination schedule was updated!"} + + +@bp.put('//date') +@bp.input(ExaminationScheduleUpdateSchema) +@bp.output(MessageSchema) +def set_date_of_examination_schedule(id: int, data: dict) -> dict: + examination_schedule_query = db.session.query(ExaminationSchedule).filter(ExaminationSchedule.id == id) + examination_schedule = examination_schedule_query.first() + + if examination_schedule is None: + abort(404, "Examination schedule doesn't exist!") + + if data['start_date'] > data['end_date']: + abort(400, "Invalid data! End date must be greater than start date!") + + examination_schedule_query.update(data) + db.session.commit() + return {"message": "You set date of examination schedule!"} diff --git a/backend/app/coordinator/schemas.py b/backend/app/coordinator/schemas.py deleted file mode 100644 index 94235df..0000000 --- a/backend/app/coordinator/schemas.py +++ /dev/null @@ -1,127 +0,0 @@ -from ..dependencies import ma -from ..students.models import Student, Group -from ..project_supervisor.models import ProjectSupervisor -from marshmallow import fields, validate, ValidationError - - -def validate_index(index): - if len(str(index)) > 6: - raise ValidationError("Length of index is too long!") - elif len(str(index)) < 6: - raise ValidationError("Length of index is too short!") - - -class ProjectSupervisorSchema(ma.SQLAlchemyAutoSchema): - class Meta: - model = ProjectSupervisor - include_relationships = False - - -class GroupSchema(ma.SQLAlchemyAutoSchema): - project_supervisor = fields.Nested(ProjectSupervisorSchema) - - class Meta: - model = Group - - -class StudentSchema(ma.SQLAlchemyAutoSchema): - group = fields.Nested(GroupSchema) - - class Meta: - model = Student - - -class StudentsPaginationSchema(ma.Schema): - students = fields.List(fields.Nested(StudentSchema)) - max_pages = fields.Integer() - - -class StudentListFileDownloaderSchema(ma.Schema): - mode = fields.Integer() - - -class StudentCreateSchema(ma.Schema): - first_name = fields.Str(validate=validate.Length(min=1, max=255), required=True) - last_name = fields.Str(validate=validate.Length(min=1, max=255), required=True) - pesel = fields.Str(validate=validate.Length(min=0, max=11), required=True) - index = fields.Integer(validate=validate_index, required=True) - mode = fields.Boolean(required=True) - - -class StudentEditSchema(ma.Schema): - first_name = fields.Str(validate=validate.Length(min=1, max=255)) - last_name = fields.Str(validate=validate.Length(min=1, max=255)) - pesel = fields.Str(validate=validate.Length(min=0, max=11)) - index = fields.Integer(validate=validate_index) - mode = fields.Boolean() - - -class MessageSchema(ma.Schema): - message = fields.Str(required=True) - - -class FileSchema(ma.Schema): - file = fields.Raw(type='file', required=True) - - -class StudentQuerySchema(ma.Schema): - fullname = fields.Str() - order_by_first_name = fields.Str() - order_by_last_name = fields.Str() - page = fields.Integer() - per_page = fields.Integer() - mode = fields.Boolean() - - -class GroupQuerySchema(ma.Schema): - name = fields.Str() - page = fields.Integer() - per_page = fields.Integer() - - -class GroupsPaginationSchema(ma.Schema): - groups = fields.List(fields.Nested(GroupSchema)) - max_pages = fields.Integer() - - -class GroupCreateSchema(ma.Schema): - name = fields.Str(validate=validate.Length(min=1, max=255), required=True) - project_supervisor_id = fields.Integer(required=True) - students = fields.List(fields.Integer(validate=validate_index, required=True)) - - -class GroupEditSchema(ma.Schema): - name = fields.Str(validate=validate.Length(min=1, max=255)) - project_supervisor_id = fields.Integer(validate=validate_index) - students = fields.List(fields.Nested(StudentSchema), validate=validate.Length(min=1, max=255)) - - -class ProjectSupervisorQuerySchema(ma.Schema): - fullname = fields.Str() - order_by_first_name = fields.Str() - order_by_last_name = fields.Str() - page = fields.Integer() - per_page = fields.Integer() - mode = fields.Integer() - - -class ProjectSupervisorsPaginationSchema(ma.Schema): - project_supervisors = fields.List(fields.Nested(ProjectSupervisorSchema)) - max_pages = fields.Integer() - - -class ProjectSupervisorCreateSchema(ma.Schema): - first_name = fields.Str(validate=validate.Length(min=1, max=255), required=True) - last_name = fields.Str(validate=validate.Length(min=1, max=255), required=True) - email = fields.Str(validate=validate.Length(min=1, max=255), required=True) - limit_group = fields.Integer() - mode = fields.Integer(required=True) - - -class ProjectSupervisorEditSchema(ma.Schema): - first_name = fields.Str(validate=validate.Length(min=1, max=255), required=True) - last_name = fields.Str(validate=validate.Length(min=1, max=255), required=True) - email = fields.Str(validate=validate.Length(min=0, max=11), required=True) - limit_group = fields.Integer(validate=validate_index) - count_groups = fields.Integer(validate=validate_index) - mode = fields.Integer(required=True) diff --git a/backend/app/coordinator/schemas/__init__.py b/backend/app/coordinator/schemas/__init__.py new file mode 100644 index 0000000..ca8aeb3 --- /dev/null +++ b/backend/app/coordinator/schemas/__init__.py @@ -0,0 +1,8 @@ +from .groups import GroupQuerySchema, GroupsPaginationSchema, GroupCreateSchema, GroupEditSchema +from .project_supervisor import ProjectSupervisorQuerySchema, ProjectSupervisorsPaginationSchema, \ + ProjectSupervisorCreateSchema, ProjectSupervisorEditSchema +from .students import ProjectSupervisorSchema, GroupSchema, StudentSchema, StudentsPaginationSchema, \ + StudentListFileDownloaderSchema, StudentCreateSchema, StudentEditSchema, MessageSchema, FileSchema, \ + StudentQuerySchema +from .examination_schedule import ExaminationScheduleSchema, ExaminationScheduleUpdateSchema, \ + ExaminationSchedulesPaginationSchema, ExaminationSchedulesQuerySchema diff --git a/backend/app/coordinator/schemas/examination_schedule.py b/backend/app/coordinator/schemas/examination_schedule.py new file mode 100644 index 0000000..84c666e --- /dev/null +++ b/backend/app/coordinator/schemas/examination_schedule.py @@ -0,0 +1,32 @@ +from marshmallow import fields, validate, Schema + +from ..validators import validate_datetime_greater_than_now + + +class ExaminationScheduleSchema(Schema): + title = fields.Str(validate=validate.Length(min=1, max=100), required=True) + + +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) + title = fields.Str(validate=validate.Length(min=1, max=100), required=True) + 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 ExaminationSchedulesPaginationSchema(Schema): + examination_schedules = fields.List(fields.Nested(ExaminationScheduleListItemSchema)) + max_pages = fields.Integer() + + +class ExaminationSchedulesQuerySchema(Schema): + page = fields.Integer() + per_page = fields.Integer() diff --git a/backend/app/coordinator/schemas/groups.py b/backend/app/coordinator/schemas/groups.py new file mode 100644 index 0000000..34ff91a --- /dev/null +++ b/backend/app/coordinator/schemas/groups.py @@ -0,0 +1,28 @@ +from marshmallow import fields, validate + +from ...dependencies import ma +from ..validators import validate_index +from .students import GroupSchema, StudentSchema + + +class GroupQuerySchema(ma.Schema): + name = fields.Str() + page = fields.Integer() + per_page = fields.Integer() + + +class GroupsPaginationSchema(ma.Schema): + groups = fields.List(fields.Nested(GroupSchema)) + max_pages = fields.Integer() + + +class GroupCreateSchema(ma.Schema): + name = fields.Str(validate=validate.Length(min=1, max=255), required=True) + project_supervisor_id = fields.Integer(required=True) + students = fields.List(fields.Integer(validate=validate_index, required=True)) + + +class GroupEditSchema(ma.Schema): + name = fields.Str(validate=validate.Length(min=1, max=255)) + project_supervisor_id = fields.Integer(validate=validate_index) + students = fields.List(fields.Nested(StudentSchema), validate=validate.Length(min=1, max=255)) diff --git a/backend/app/coordinator/schemas/project_supervisor.py b/backend/app/coordinator/schemas/project_supervisor.py new file mode 100644 index 0000000..389959f --- /dev/null +++ b/backend/app/coordinator/schemas/project_supervisor.py @@ -0,0 +1,36 @@ +from marshmallow import fields, validate + +from ...dependencies import ma +from ..validators import validate_index +from .students import ProjectSupervisorSchema + + +class ProjectSupervisorQuerySchema(ma.Schema): + fullname = fields.Str() + order_by_first_name = fields.Str() + order_by_last_name = fields.Str() + page = fields.Integer() + per_page = fields.Integer() + mode = fields.Integer() + + +class ProjectSupervisorsPaginationSchema(ma.Schema): + project_supervisors = fields.List(fields.Nested(ProjectSupervisorSchema)) + max_pages = fields.Integer() + + +class ProjectSupervisorCreateSchema(ma.Schema): + first_name = fields.Str(validate=validate.Length(min=1, max=255), required=True) + last_name = fields.Str(validate=validate.Length(min=1, max=255), required=True) + email = fields.Str(validate=validate.Length(min=1, max=255), required=True) + limit_group = fields.Integer() + mode = fields.Integer(required=True) + + +class ProjectSupervisorEditSchema(ma.Schema): + first_name = fields.Str(validate=validate.Length(min=1, max=255), required=True) + last_name = fields.Str(validate=validate.Length(min=1, max=255), required=True) + email = fields.Str(validate=validate.Length(min=0, max=11), required=True) + limit_group = fields.Integer(validate=validate_index) + count_groups = fields.Integer(validate=validate_index) + mode = fields.Integer(required=True) diff --git a/backend/app/coordinator/schemas/students.py b/backend/app/coordinator/schemas/students.py new file mode 100644 index 0000000..747cb0e --- /dev/null +++ b/backend/app/coordinator/schemas/students.py @@ -0,0 +1,68 @@ +from marshmallow import fields, validate + +from ...dependencies import ma +from ...students.models import Student, Group +from ...project_supervisor.models import ProjectSupervisor +from ..validators import validate_index + + +class ProjectSupervisorSchema(ma.SQLAlchemyAutoSchema): + class Meta: + model = ProjectSupervisor + include_relationships = False + + +class GroupSchema(ma.SQLAlchemyAutoSchema): + project_supervisor = fields.Nested(ProjectSupervisorSchema) + + class Meta: + model = Group + + +class StudentSchema(ma.SQLAlchemyAutoSchema): + group = fields.Nested(GroupSchema) + + class Meta: + model = Student + + +class StudentsPaginationSchema(ma.Schema): + students = fields.List(fields.Nested(StudentSchema)) + max_pages = fields.Integer() + + +class StudentListFileDownloaderSchema(ma.Schema): + mode = fields.Integer() + + +class StudentCreateSchema(ma.Schema): + first_name = fields.Str(validate=validate.Length(min=1, max=255), required=True) + last_name = fields.Str(validate=validate.Length(min=1, max=255), required=True) + pesel = fields.Str(validate=validate.Length(min=0, max=11), required=True) + index = fields.Integer(validate=validate_index, required=True) + mode = fields.Boolean(required=True) + + +class StudentEditSchema(ma.Schema): + first_name = fields.Str(validate=validate.Length(min=1, max=255)) + last_name = fields.Str(validate=validate.Length(min=1, max=255)) + pesel = fields.Str(validate=validate.Length(min=0, max=11)) + index = fields.Integer(validate=validate_index) + mode = fields.Boolean() + + +class MessageSchema(ma.Schema): + message = fields.Str(required=True) + + +class FileSchema(ma.Schema): + file = fields.Raw(type='file', required=True) + + +class StudentQuerySchema(ma.Schema): + fullname = fields.Str() + order_by_first_name = fields.Str() + order_by_last_name = fields.Str() + page = fields.Integer() + per_page = fields.Integer() + mode = fields.Boolean() diff --git a/backend/app/coordinator/validators.py b/backend/app/coordinator/validators.py new file mode 100644 index 0000000..e47e317 --- /dev/null +++ b/backend/app/coordinator/validators.py @@ -0,0 +1,15 @@ +from datetime import datetime + +from marshmallow import ValidationError + + +def validate_index(index: int) -> None: + if len(str(index)) > 6: + raise ValidationError("Length of index is too long!") + elif len(str(index)) < 6: + raise ValidationError("Length of index is too short!") + + +def validate_datetime_greater_than_now(date: datetime) -> None: + if date < datetime.now(): + raise ValidationError("Date must be greater than NOW!") diff --git a/backend/app/examination_schedule/models.py b/backend/app/examination_schedule/models.py index 547463e..96df07a 100644 --- a/backend/app/examination_schedule/models.py +++ b/backend/app/examination_schedule/models.py @@ -6,8 +6,8 @@ class ExaminationSchedule(Base): __tablename__ = 'examination_schedules' title = db.Column(db.String(100), unique=True, nullable=False) - start_date = db.Column(db.DateTime, nullable=False) - end_date = db.Column(db.DateTime, nullable=False) + start_date = db.Column(db.DateTime) + end_date = db.Column(db.DateTime) class Enrollment(Base): diff --git a/backend/migrations/versions/c7adfbd3c67f_.py b/backend/migrations/versions/9874e26f2b87_.py similarity index 91% rename from backend/migrations/versions/c7adfbd3c67f_.py rename to backend/migrations/versions/9874e26f2b87_.py index 755cc14..83bb654 100644 --- a/backend/migrations/versions/c7adfbd3c67f_.py +++ b/backend/migrations/versions/9874e26f2b87_.py @@ -1,8 +1,8 @@ """empty message -Revision ID: c7adfbd3c67f +Revision ID: 9874e26f2b87 Revises: ceaefb33117e -Create Date: 2022-10-26 08:11:00.965906 +Create Date: 2022-10-26 09:46:09.397814 """ from alembic import op @@ -10,7 +10,7 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = 'c7adfbd3c67f' +revision = '9874e26f2b87' down_revision = 'ceaefb33117e' branch_labels = None depends_on = None @@ -21,8 +21,8 @@ def upgrade(): op.create_table('examination_schedules', sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), sa.Column('title', sa.String(length=100), nullable=False), - sa.Column('start_date', sa.DateTime(), nullable=False), - sa.Column('end_date', sa.DateTime(), nullable=False), + sa.Column('start_date', sa.DateTime(), nullable=True), + sa.Column('end_date', sa.DateTime(), nullable=True), sa.PrimaryKeyConstraint('id'), sa.UniqueConstraint('title') )