diff --git a/backend/app/config.py b/backend/app/config.py index 7c3bc65..01c1d8f 100644 --- a/backend/app/config.py +++ b/backend/app/config.py @@ -23,8 +23,6 @@ class Config: DESCRIPTION = 'System PRI' OPENAPI_VERSION = '3.0.2' - PROJECT_PRESENTATION_TIME = 30 # in minutes - class ProductionConfig(Config): DB_SERVER = "0.0.0.0" diff --git a/backend/app/coordinator/routes/__init__.py b/backend/app/coordinator/routes/__init__.py index d796e0d..a99b85a 100644 --- a/backend/app/coordinator/routes/__init__.py +++ b/backend/app/coordinator/routes/__init__.py @@ -1,7 +1,7 @@ from flask import Blueprint from .examination_schedule import bp as examination_schedule_bp -# from .enrollments import bp as enrollments_bp +from .enrollments import bp as enrollments_bp from .groups import bp as groups_bp from .project_supervisor import bp as project_supervisor_bp from .students import bp as students_bp @@ -15,5 +15,5 @@ bp.register_blueprint(project_supervisor_bp) bp.register_blueprint(groups_bp) bp.register_blueprint(year_group_bp) bp.register_blueprint(examination_schedule_bp) -# bp.register_blueprint(enrollments_bp) +bp.register_blueprint(enrollments_bp) # bp.register_blueprint(workloads_bp) diff --git a/backend/app/coordinator/routes/enrollments.py b/backend/app/coordinator/routes/enrollments.py index 10174eb..6a58888 100644 --- a/backend/app/coordinator/routes/enrollments.py +++ b/backend/app/coordinator/routes/enrollments.py @@ -1,69 +1,174 @@ import datetime from apiflask import APIBlueprint -from flask import abort, current_app +from flask import abort +from sqlalchemy import or_, and_ -from ..schemas import MessageSchema, EnrollmentCreateSchema -from ...examination_schedule.models import ExaminationSchedule +from ..schemas import MessageSchema, TermOfDefenceSchema, TermOfDefenceListSchema, \ + TemporaryAvailabilityListSchema +from ...examination_schedule.models import ExaminationSchedule, TermOfDefence, TemporaryAvailability +from ...students.models import YearGroup +from ...project_supervisor.models import ProjectSupervisor from ...dependencies import db bp = APIBlueprint("enrollments", __name__, url_prefix="/enrollments") -@bp.post('//') -@bp.input(EnrollmentCreateSchema, location='json') -@bp.output(MessageSchema) -def create_enrollments(examination_schedule_id: int, data: dict) -> dict: - prt = current_app.config["PROJECT_PRESENTATION_TIME"] +@bp.post('//generate') +def generate_term_of_defence_for_this_examination_schedule(examination_schedule_id: int) -> dict: + pass - 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!") + +@bp.post('//add') +@bp.input(TermOfDefenceSchema) +@bp.output(MessageSchema) +def create_term_of_defence(examination_schedule_id: int, data: dict) -> dict: + if not data: + abort(400, "You have passed empty data!") + + ex = ExaminationSchedule.query.filter(ExaminationSchedule.id == examination_schedule_id).first() + if ex is None: + abort(404, "Not found examination schedule!") + + yg_id = ex.year_group_id + project_supervisors_ids = data.pop('project_supervisors') + project_supervisors = ProjectSupervisor.query.filter( + or_(*[ProjectSupervisor.id == i for i in project_supervisors_ids])).filter(YearGroup.id == yg_id).all() + + if len(project_supervisors) != len(project_supervisors_ids): + abort(404, "Project Supervisors didn't exist!") start_date = data['start_date'] end_date = data['end_date'] - if start_date > end_date: - abort(400, "Invalid dates! End date must be greater than start date!") - if start_date.date() != end_date.date(): - abort(400, "Invalid dates! Only hours can be different!") - enrollment = Enrollment.query.filter(Enrollment.start_date >= start_date, - Enrollment.start_date < end_date).first() - if enrollment is not None: - abort(400, "You have just created enrollments for this range date!") + if not (ex.start_date.timestamp() < start_date.timestamp() and ex.end_date.timestamp() > end_date.timestamp()): + abort(400, "Invalid date range!") - enrollment = Enrollment.query.filter(Enrollment.end_date > start_date, Enrollment.end_date <= end_date).first() - if enrollment is not None: - abort(400, "You have just created enrollments for this range date! `1") + if end_date <= start_date: + abort(400, "End date must be greater than start date!") - delta = end_date - start_date - delta_in_minutes = delta.total_seconds() / 60 - if delta_in_minutes % prt != 0: - abort(400, "Invalid format dates!") + delta_time = end_date - start_date + delta_time_in_minutes = delta_time.total_seconds() / 60 + if delta_time_in_minutes != ex.duration_time: + abort(400, "Invalid duration time!") - amount = int(delta_in_minutes // prt) - enrollments = [] - 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, examination_schedule_id=examination_schedule_id) - enrollments.append(enrollment) - db.session.add_all(enrollments) + td = TermOfDefence.query.filter(TermOfDefence.examination_schedule_id == examination_schedule_id). \ + filter( + or_(and_(TermOfDefence.start_date >= start_date, + TermOfDefence.start_date < end_date, + TermOfDefence.end_date >= end_date), + and_(TermOfDefence.start_date <= start_date, + TermOfDefence.end_date > start_date, + TermOfDefence.end_date <= end_date))).first() + + if td is not None: + abort(400, "This term of defence is taken! You choose other date!") + + td = TermOfDefence(**data, examination_schedule_id=examination_schedule_id) + db.session.add(td) db.session.commit() - - committees = [Committee(enrollment_id=e.id) for e in enrollments] - db.session.add_all(committees) + for p in project_supervisors: + td.members_of_committee.append(p) db.session.commit() - return {"message": "Enrollments was created!"} + return {"message": "Term of defence was created!"} -@bp.delete('//') +@bp.put('//update//') +@bp.input(TermOfDefenceSchema) @bp.output(MessageSchema) -def delete_enrollment(enrollment_id: int) -> dict: - enrollment = db.session.query(Enrollment).filter(Enrollment.id == enrollment_id).first() - if enrollment is None: - abort(404, "Enrollment doesn't exist!") - db.session.delete(enrollment) +def update_term_of_defence(examination_schedule_id: int, term_of_defence_id: int, data: dict) -> dict: + if not data: + abort(400, "You have passed empty data!") + + td_query = TermOfDefence.query.filter(TermOfDefence.id == term_of_defence_id, + TermOfDefence.examination_schedule_id == examination_schedule_id) + td = td_query.first() + if td is None: + abort(404, "Not found term of defence!") + + ex = td.examination_schedule + yg_id = ex.year_group_id + project_supervisors_ids = data.pop('project_supervisors') + project_supervisors = ProjectSupervisor.query.filter( + or_(*[ProjectSupervisor.id == i for i in project_supervisors_ids])).filter(YearGroup.id == yg_id).all() + + if len(project_supervisors) != len(project_supervisors_ids): + abort(404, "Project Supervisors didn't exist!") + + start_date = data['start_date'] + end_date = data['end_date'] + + if not (ex.start_date.timestamp() < start_date.timestamp() and ex.end_date.timestamp() > end_date.timestamp()): + abort(400, "Invalid date range!") + + if end_date <= start_date: + abort(400, "End date must be greater than start date!") + + delta_time = end_date - start_date + delta_time_in_minutes = delta_time.total_seconds() / 60 + if delta_time_in_minutes != ex.duration_time: + abort(400, "Invalid duration time!") + + term_of_defence = TermOfDefence.query.filter(TermOfDefence.id != term_of_defence_id, + TermOfDefence.examination_schedule_id == examination_schedule_id). \ + filter( + or_(and_(TermOfDefence.start_date >= start_date, + TermOfDefence.start_date < end_date, + TermOfDefence.end_date >= end_date), + and_(TermOfDefence.start_date <= start_date, + TermOfDefence.end_date > start_date, + TermOfDefence.end_date <= end_date))).first() + + if term_of_defence is not None: + abort(400, "This term of defence is taken! You choose other date!") + + td_query.update(data) + td.members_of_committee = [] db.session.commit() - return {"message": "Enrollment was deleted!"} + for p in project_supervisors: + td.members_of_committee.append(p) + db.session.commit() + + return {"message": "Term of defence was updated!"} + + +@bp.delete('//delete//') +@bp.output(MessageSchema) +def delete_term_of_defence(examination_schedule_id: int, term_of_defence_id: int) -> dict: + td = TermOfDefence.query.filter(TermOfDefence.id == term_of_defence_id, + ExaminationSchedule.id == examination_schedule_id).first() + if td is None: + abort(404, "Not found term of defence!") + + db.session.delete(td) + db.session.commit() + + return {"message": "Term of defence was deleted!"} + + +@bp.get('//term-of-defences/') +@bp.output(TermOfDefenceListSchema) +def list_of_term_of_defences(examination_schedule_id: int) -> dict: + ex = ExaminationSchedule.query.filter(ExaminationSchedule.id == examination_schedule_id).first() + if ex is None: + abort(400, "Examination Schedule didn't exist") + + td = TermOfDefence.query. \ + filter(TermOfDefence.examination_schedule_id == examination_schedule_id). \ + join(TermOfDefence.members_of_committee, isouter=True). \ + all() + return {"term_of_defences": td} + + +@bp.get('//temporary-availabilities/') +@bp.output(TemporaryAvailabilityListSchema) +def list_of_temporary_availability(examination_schedule_id: int) -> dict: + ex = ExaminationSchedule.query.filter(ExaminationSchedule.id == examination_schedule_id).first() + if ex is None: + abort(400, "Examination Schedule didn't exist") + + td = TemporaryAvailability.query. \ + filter(TemporaryAvailability.examination_schedule_id == examination_schedule_id). \ + join(TemporaryAvailability.project_supervisor). \ + all() + return {"temporary_availabilities": td} diff --git a/backend/app/coordinator/routes/examination_schedule.py b/backend/app/coordinator/routes/examination_schedule.py index 8684444..504cefd 100644 --- a/backend/app/coordinator/routes/examination_schedule.py +++ b/backend/app/coordinator/routes/examination_schedule.py @@ -4,6 +4,7 @@ from apiflask import APIBlueprint from flask import abort, Response, make_response from ...base.utils import paginate_models +from ...base.mode import ModeGroups from ...dependencies import db from ...examination_schedule.models import ExaminationSchedule from ...students.models import Group, YearGroup @@ -37,7 +38,16 @@ def create_examination_schedule(year_group_id: int, data: dict) -> dict: if data['start_date'] > data['end_date']: abort(400, "Invalid data! End date must be greater than start date!") - examination_schedule = ExaminationSchedule(**data, year_group_id=year_group_id) + duration_time = None + if yg.mode == ModeGroups.NON_STATIONARY.value: + duration_time = 20 + elif yg.mode in [ModeGroups.STATIONARY.value, ModeGroups.ENGLISH_SPEAKING_STATIONARY.value]: + duration_time = 30 + + if duration_time is None: + abort(400, "Invalid mode of year group!") + + examination_schedule = ExaminationSchedule(**data, year_group_id=year_group_id, duration_time=duration_time) db.session.add(examination_schedule) db.session.commit() return {"message": "Examination schedule was created!"} diff --git a/backend/app/coordinator/routes/workloads.py b/backend/app/coordinator/routes/workloads.py index 34beea8..2c709f6 100644 --- a/backend/app/coordinator/routes/workloads.py +++ b/backend/app/coordinator/routes/workloads.py @@ -3,7 +3,7 @@ from flask import abort from flask_sqlalchemy import get_debug_queries from ...dependencies import db -from ...examination_schedule.models import ExaminationSchedule, Enrollment, Committee +from ...examination_schedule.models import ExaminationSchedule from ...project_supervisor.models import ProjectSupervisor from ..schemas import WorkloadSchema diff --git a/backend/app/coordinator/routes/year_group.py b/backend/app/coordinator/routes/year_group.py index a64320d..35f2005 100644 --- a/backend/app/coordinator/routes/year_group.py +++ b/backend/app/coordinator/routes/year_group.py @@ -1,6 +1,5 @@ from flask import abort from apiflask import APIBlueprint -from flask_sqlalchemy import get_debug_queries from ...students.models import YearGroup from ..schemas import YearGroupSchema, MessageSchema, YearGroupPaginationSchema, YearGroupQuerySchema diff --git a/backend/app/coordinator/schemas/__init__.py b/backend/app/coordinator/schemas/__init__.py index 12fb332..b5a0cfc 100644 --- a/backend/app/coordinator/schemas/__init__.py +++ b/backend/app/coordinator/schemas/__init__.py @@ -1,4 +1,4 @@ -from .enrollments import EnrollmentCreateSchema +from .enrollments import TermOfDefenceSchema, TermOfDefenceListSchema, TemporaryAvailabilityListSchema from .examination_schedule import ExaminationScheduleSchema, ExaminationScheduleUpdateSchema, \ ExaminationSchedulesPaginationSchema, ExaminationSchedulesQuerySchema, WorkloadSchema from .groups import GroupQuerySchema, GroupsPaginationSchema, GroupCreateSchema, GroupEditSchema diff --git a/backend/app/coordinator/schemas/enrollments.py b/backend/app/coordinator/schemas/enrollments.py index 7367e58..bdfd0a5 100644 --- a/backend/app/coordinator/schemas/enrollments.py +++ b/backend/app/coordinator/schemas/enrollments.py @@ -1,8 +1,41 @@ -from marshmallow import Schema, fields +from marshmallow import Schema, fields, validate from ..validators import validate_datetime_greater_than_now -class EnrollmentCreateSchema(Schema): +class TermOfDefenceSchema(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) + project_supervisors = fields.List(fields.Integer(required=True), validate=validate.Length(3, 3)) + + +class ProjectSupervisorForTermOfDefenceSchema(Schema): + id = fields.Str() + first_name = fields.Str() + last_name = fields.Str() + + +class TermOfDefenceItemSchema(Schema): + start_date = fields.DateTime() + end_date = fields.DateTime() + members_of_committee = fields.List(fields.Nested(ProjectSupervisorForTermOfDefenceSchema)) + + +class TermOfDefenceListSchema(Schema): + term_of_defences = fields.List(fields.Nested(TermOfDefenceItemSchema)) + + +class ProjectSupervisorForTemporaryAvailabilitySchema(Schema): + id = fields.Str() + first_name = fields.Str() + last_name = fields.Str() + + +class TemporaryAvailabilityItemSchema(Schema): + start_date = fields.DateTime() + end_date = fields.DateTime() + project_supervisor = fields.Nested(ProjectSupervisorForTemporaryAvailabilitySchema) + + +class TemporaryAvailabilityListSchema(Schema): + temporary_availabilities = fields.List(fields.Nested(TemporaryAvailabilityItemSchema)) diff --git a/backend/app/examination_schedule/models.py b/backend/app/examination_schedule/models.py index 6b37ff8..b18e6b8 100644 --- a/backend/app/examination_schedule/models.py +++ b/backend/app/examination_schedule/models.py @@ -6,7 +6,7 @@ class ExaminationSchedule(Base): __tablename__ = 'examination_schedules' title = db.Column(db.String(100), unique=True, nullable=False) - duration_time = db.Column(db.Integer) # in minutes + duration_time = db.Column(db.Integer) # in minutes start_date_for_enrollment_students = db.Column(db.DateTime) end_date_for_enrollment_students = db.Column(db.DateTime) start_date = db.Column(db.DateTime, nullable=False) diff --git a/backend/app/examination_schedule/routes/enrollments.py b/backend/app/examination_schedule/routes/enrollments.py deleted file mode 100644 index e01027b..0000000 --- a/backend/app/examination_schedule/routes/enrollments.py +++ /dev/null @@ -1,39 +0,0 @@ -import datetime - -from apiflask import APIBlueprint -from flask import abort - -from ..schemas import EnrollmentPaginationSchema, EnrollmentQuerySchema, ExaminationScheduleProjectSupervisorViewSchema -from ..utils import check_examination_schedule_is_exist, get_list_of_enrollments_response - -bp = APIBlueprint("enrollments", __name__, url_prefix="/enrollments") - - -# 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.output(ExaminationScheduleProjectSupervisorViewSchema) -def list_enrollments_for_coordinator(examination_schedule_id: int) -> dict: - return check_examination_schedule_is_exist(examination_schedule_id) - diff --git a/backend/app/examination_schedule/routes/examination_schedule.py b/backend/app/examination_schedule/routes/examination_schedule.py deleted file mode 100644 index aa1fcfa..0000000 --- a/backend/app/examination_schedule/routes/examination_schedule.py +++ /dev/null @@ -1,18 +0,0 @@ -import datetime - -from apiflask import APIBlueprint - -from ...dependencies import db -from ..models import ExaminationSchedule - -bp = APIBlueprint("list_of_examination_schedule", __name__, url_prefix="/") - - -@bp.get('/students-view/') -def list_examination_schedule_for_students() -> dict: - # in the future filter after the mode of examination schedule if we will have authorization module - now = datetime.datetime.utcnow() - examination_schedules = db.session.query(ExaminationSchedule).\ - filter(ExaminationSchedule.start_date_for_enrollment_students < now).\ - filter(ExaminationSchedule.end_date_for_enrollment_students > now).all() - return {'examination_schedules': examination_schedules} diff --git a/backend/app/examination_schedule/schemas.py b/backend/app/examination_schedule/schemas.py index b7d5114..e69de29 100644 --- a/backend/app/examination_schedule/schemas.py +++ b/backend/app/examination_schedule/schemas.py @@ -1,39 +0,0 @@ -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): - id = fields.Integer() - 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 ExaminationScheduleProjectSupervisorViewSchema(Schema): - id = fields.Integer() - start_date = fields.DateTime() - end_date = fields.DateTime() - - -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 index 2a283c3..e69de29 100644 --- a/backend/app/examination_schedule/utils.py +++ b/backend/app/examination_schedule/utils.py @@ -1,27 +0,0 @@ -from flask import abort - -from ..dependencies import db -from .models import 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/project_supervisor/query.py b/backend/app/project_supervisor/query.py deleted file mode 100644 index f4ee19e..0000000 --- a/backend/app/project_supervisor/query.py +++ /dev/null @@ -1,27 +0,0 @@ -import datetime - -from flask import abort - -from ..dependencies import db -from ..examination_schedule.models import ExaminationSchedule - - -def get_enrollment_by_enrollment_and_examination_schedule_ids(examination_schedule_id: int, - enrollment_id: int) -> None: - enrollment = db.session.query(Enrollment). \ - join(ExaminationSchedule, isouter=True).join(Committee, isouter=True). \ - filter(ExaminationSchedule.id == examination_schedule_id). \ - filter(Enrollment.id == enrollment_id). \ - first() - - if enrollment is None: - abort(404, "Examination schedule doesn't exist!") - - return enrollment - - -def check_the_enrollments_has_just_started(start_date: datetime.datetime, action: str) -> None: - now = datetime.datetime.utcnow() - - if start_date is not None and start_date.timestamp() < now.timestamp(): - abort(403, f"Forbidden! Enrollment has just started! You cannot {action} from the exam committees!") diff --git a/backend/app/project_supervisor/routes/enrollments.py b/backend/app/project_supervisor/routes/enrollments.py index c5fe196..2b3dcc1 100644 --- a/backend/app/project_supervisor/routes/enrollments.py +++ b/backend/app/project_supervisor/routes/enrollments.py @@ -4,7 +4,7 @@ from apiflask import APIBlueprint from flask import abort from ..schemas import MessageSchema, TimeAvailabilityCreateSchema, TemporaryProjectSupervisorSchema, \ - ListOfFreeTimesSchema + ListOfFreeTimesSchema, ListOfTermOfDefenceSchema from ...dependencies import db from ..models import ProjectSupervisor from ...examination_schedule.models import ExaminationSchedule, TemporaryAvailability, TermOfDefence @@ -32,7 +32,9 @@ def set_your_free_time_to_examination_schedule(examination_schedule_id: int, dat if sd > ed: abort(400, "Invalid data! End date must be greater than start date!") - if not (es.start_date >= sd and es.end_date <= ed): + print(es.start_date.timestamp() >= sd.timestamp()) + print(es.end_date.timestamp() <= ed.timestamp()) + if not (es.start_date.timestamp() <= sd.timestamp() and es.end_date.timestamp() >= ed.timestamp()): abort(400, "Invalid date ranges!") now = datetime.utcnow() @@ -47,18 +49,18 @@ def set_your_free_time_to_examination_schedule(examination_schedule_id: int, dat # implement logic pass - ta = TemporaryAvailability(**data, examination_schedule_id=examination_schedule_id, - project_supervisor_id=project_supervisor.id) + ta = TemporaryAvailability(**data, examination_schedule_id=examination_schedule_id) db.session.add(ta) db.session.commit() return {"message": "You have just assigned your free time!"} -@bp.delete('//enrollments//') +@bp.delete('//enrollments//') @bp.input(TemporaryProjectSupervisorSchema) @bp.output(MessageSchema) -def delete_your_free_time_from_examination_schedule(examination_schedule_id: int, id: int, data: dict) -> dict: +def delete_your_free_time_from_examination_schedule(examination_schedule_id: int, temporary_availability_id: int, + data: dict) -> dict: # this code will be removed project_supervisor = db.session.query(ProjectSupervisor).filter(ProjectSupervisor.id == data['id']).first() if project_supervisor is None: @@ -66,7 +68,7 @@ def delete_your_free_time_from_examination_schedule(examination_schedule_id: int ################ ta = TemporaryAvailability.query.filter(TemporaryAvailability.examination_schedule_id == examination_schedule_id, TemporaryAvailability.project_supervisor_id == project_supervisor.id, - TemporaryAvailability.id == id).first() + TemporaryAvailability.id == temporary_availability_id).first() if ta is None: abort(404, "Your free time doesn't exist!") @@ -76,7 +78,7 @@ def delete_your_free_time_from_examination_schedule(examination_schedule_id: int return {"message": "You have just removed your free time!"} -@bp.get('//') +@bp.get('//temporary-availabilities/') @bp.input(TemporaryProjectSupervisorSchema, location='query') @bp.output(ListOfFreeTimesSchema) def list_enrollments_for_project_supervisor(examination_schedule_id: int, data: dict) -> dict: @@ -92,19 +94,21 @@ def list_enrollments_for_project_supervisor(examination_schedule_id: int, data: abort(404, "Examination schedule doesn't exist!") now = datetime.utcnow() - if es.start_date_for_enrollment_students.timestamp() < now.timestamp(): + start_date = es.start_date_for_enrollment_students + if start_date is not None and start_date.timestamp() < now.timestamp(): abort(403, "Forbidden! Enrollment has just started! You cannot assign to the exam committees!") # list of your term of defences first enrollment ta = TemporaryAvailability.query.filter(TemporaryAvailability.examination_schedule_id == examination_schedule_id, TemporaryAvailability.project_supervisor_id == project_supervisor.id).all() - return ta + return {"free_times": ta} @bp.get('//term-of-defences/') @bp.input(TemporaryProjectSupervisorSchema, location='query') -@bp.output(ListOfFreeTimesSchema) -def list_created_enrollment_by_coordinator_for_project_supervisor(examination_schedule_id: int, data: dict) -> dict: +@bp.output(ListOfTermOfDefenceSchema) +def list_created_term_of_defences_by_coordinator_for_project_supervisor(examination_schedule_id: int, + data: dict) -> dict: # this code will be removed project_supervisor = db.session.query(ProjectSupervisor).filter(ProjectSupervisor.id == data['id']).first() if project_supervisor is None: @@ -118,5 +122,5 @@ def list_created_enrollment_by_coordinator_for_project_supervisor(examination_sc # list of your free times first enrollment td = TermOfDefence.query.join(TermOfDefence.members_of_committee). \ filter(TermOfDefence.examination_schedule_id == examination_schedule_id). \ - filter_by(project_supervisor_id=project_supervisor.id).all() - return td + filter_by(id=project_supervisor.id).all() + return {"term_of_defences": td} diff --git a/backend/app/project_supervisor/schemas.py b/backend/app/project_supervisor/schemas.py index e956ab1..e497085 100644 --- a/backend/app/project_supervisor/schemas.py +++ b/backend/app/project_supervisor/schemas.py @@ -15,6 +15,10 @@ class ListOfFreeTimesSchema(Schema): free_times = fields.List(fields.Nested(FreeTimeSchema)) +class ListOfTermOfDefenceSchema(Schema): + term_of_defences = fields.List(fields.Nested(FreeTimeSchema)) + + class TimeAvailabilityCreateSchema(Schema): start_date = fields.DateTime(required=True) end_date = fields.DateTime(required=True) diff --git a/backend/app/students/query.py b/backend/app/students/query.py deleted file mode 100644 index a0f736d..0000000 --- a/backend/app/students/query.py +++ /dev/null @@ -1,10 +0,0 @@ -import datetime - -from flask import abort -from ..examination_schedule.models import ExaminationSchedule - - -def check_the_enrollments_has_just_started(es: ExaminationSchedule) -> None: - now = datetime.datetime.utcnow() - if es.start_date is None or es.end_date is None or not (es.start_date < now < es.end_date): - abort(403, "Forbidden! Enrollment hasn't started yet.") diff --git a/backend/app/students/routes/__init__.py b/backend/app/students/routes/__init__.py index e34d84d..561045e 100644 --- a/backend/app/students/routes/__init__.py +++ b/backend/app/students/routes/__init__.py @@ -2,8 +2,10 @@ from flask import Blueprint from .enrollments import bp as enrollments_bp from .registrations import bp as registrations_bp +from .year_group import bp as year_group_bp bp = Blueprint("students", __name__, url_prefix="/students") -bp.register_blueprint(registrations_bp) bp.register_blueprint(enrollments_bp) +bp.register_blueprint(registrations_bp) +bp.register_blueprint(year_group_bp) diff --git a/backend/app/students/routes/enrollments.py b/backend/app/students/routes/enrollments.py index 2293d2a..b970857 100644 --- a/backend/app/students/routes/enrollments.py +++ b/backend/app/students/routes/enrollments.py @@ -3,7 +3,8 @@ import datetime from apiflask import APIBlueprint from flask import abort -from ..schemas import MessageSchema, TemporaryStudentSchema, ExaminationScheduleListSchema +from ..schemas import MessageSchema, TemporaryStudentSchema, ExaminationScheduleListSchema, \ + TermOfDefenceStudentListSchema from ...dependencies import db from ..models import Student, Group, TermOfDefence from ...examination_schedule.models import ExaminationSchedule @@ -12,33 +13,39 @@ from ...project_supervisor.models import ProjectSupervisor bp = APIBlueprint("enrollments", __name__, url_prefix="/") -@bp.post('//enrollments//') +@bp.post('//enrollments//') @bp.input(TemporaryStudentSchema) @bp.output(MessageSchema) -def assign_your_group_to_term_of_defence(examination_schedule_id: int, data: dict) -> dict: +def assign_your_group_to_term_of_defence(examination_schedule_id: int, term_of_defence_id: int, data: dict) -> dict: # this code will be removed student = Student.query.filter(Student.index == data['student_index']).first() if student is None: abort(404, "Student doesn't exist!") ################ + term_of_defence = TermOfDefence.query.filter(TermOfDefence.id == term_of_defence_id, + TermOfDefence.examination_schedule_id == examination_schedule_id).first() + + if term_of_defence is None: + abort(400, "Term of defence not found!") + st = Student.query.join(Group).join(ProjectSupervisor).filter(Student.index == student.index).first() - if st is None or st.group.project_supervisor is None: + if st is None or st.groups.project_supervisor is None: abort(400, "You don't have a group or your group doesn't have an assigned project supervisor!") - defence = TermOfDefence.query.filter(TermOfDefence.group_id == st.group.id, + defence = TermOfDefence.query.filter(TermOfDefence.group_id == st.groups.id, TermOfDefence.examination_schedule_id == examination_schedule_id).first() if defence is not None: abort(400, "Your group has already assigned to any exam date!") - g = Group.query.filter(id=st.group_id).first() + g = Group.query.filter(Group.id == st.group_id).first() td = TermOfDefence.query.join(TermOfDefence.members_of_committee). \ - filter_by(project_supervisor_id=g.project_supervisor_id).first() + filter_by(id=g.project_supervisor_id).first() if td is None: abort(400, "Your project supervisor is not in committee!") - defence.group_id = st.group.id - db.session.add(defence) + term_of_defence.group_id = st.groups.id + db.session.add(term_of_defence) db.session.commit() return {"message": "You have just assigned the group for this exam date!"} @@ -59,7 +66,7 @@ def delete_your_group_from_term_of_defence(examination_schedule_id: int, term_of if term_of_defence is None: abort(404, "Term of defence doesn't exist!") - if student.group.id != term_of_defence.group_id: + if student.groups.id != term_of_defence.group_id: abort(400, "You are not assigned to this group!") term_of_defence.group_id = None @@ -70,9 +77,9 @@ def delete_your_group_from_term_of_defence(examination_schedule_id: int, term_of @bp.get('/examination-schedule/year-group//') -@bp.input(TemporaryStudentSchema) +@bp.input(TemporaryStudentSchema, location='query') @bp.output(ExaminationScheduleListSchema) -def list_examination_schedule_for_students(year_group_id: int, data: dict) -> dict: +def list_examination_schedule(year_group_id: int, data: dict) -> dict: # this code will be removed student = Student.query.filter(Student.index == data['student_index']).first() if student is None: @@ -88,10 +95,10 @@ def list_examination_schedule_for_students(year_group_id: int, data: dict) -> di return {'examination_schedules': examination_schedules} -@bp.get('/enrollments/') -@bp.input(TemporaryStudentSchema) -@bp.output(ExaminationScheduleListSchema) -def list_term_of_defences_for_students(data: dict) -> dict: +@bp.get('/examination-schedule//enrollments/') +@bp.input(TemporaryStudentSchema, location='query') +@bp.output(TermOfDefenceStudentListSchema) +def list_term_of_defences(examination_schedule_id: int, data: dict) -> dict: # this code will be removed student = Student.query.filter(Student.index == data['student_index']).first() if student is None: @@ -99,9 +106,9 @@ def list_term_of_defences_for_students(data: dict) -> dict: ################ # in the future filter after the mode of examination schedule if we will have authorization module now = datetime.datetime.utcnow() - term_of_defences = TermOfDefence.query.join(ExaminationSchedule, isouter=True). \ + term_of_defences = TermOfDefence.query.filter(TermOfDefence.examination_schedule_id == examination_schedule_id). \ + join(ExaminationSchedule, isouter=True). \ filter(ExaminationSchedule.start_date_for_enrollment_students < now). \ - filter(ExaminationSchedule.end_date_for_enrollment_students > now). \ - filter(TermOfDefence.group_id == student.group_id). \ - all() - return {'examination_schedules': term_of_defences} + filter(ExaminationSchedule.end_date_for_enrollment_students > now).all() + + return {'term_of_defences': term_of_defences} diff --git a/backend/app/students/routes/year_group.py b/backend/app/students/routes/year_group.py new file mode 100644 index 0000000..fc18477 --- /dev/null +++ b/backend/app/students/routes/year_group.py @@ -0,0 +1,32 @@ +from apiflask import APIBlueprint +from flask import abort + +from ...students.models import YearGroup +from ...dependencies import db +from ...base.utils import paginate_models +from ..schemas import YearGroupQueryStudentSchema, YearGroupStudentPaginationSchema +from ..models import Student + +bp = APIBlueprint("year_group", __name__, url_prefix="/year-group") + + +@bp.get('/') +@bp.input(YearGroupQueryStudentSchema, location='query') +@bp.output(YearGroupStudentPaginationSchema) +def list_of_year_groups(query: dict) -> dict: + index = query.get('index') + st = Student.query.filter(Student.index == index).first() + if st is None: + abort(404, "Not found student!") + #################################### + page = query.get('page') + per_page = query.get('per_page') + + year_group_query = YearGroup.query.join(YearGroup.students). \ + filter(Student.index == st.index).order_by(db.desc(YearGroup.created_at)) + data = paginate_models(page, year_group_query, per_page) + + return { + "year_groups": data['items'], + "max_pages": data['max_pages'] + } diff --git a/backend/app/students/schemas.py b/backend/app/students/schemas.py index 6a70137..17bd64e 100644 --- a/backend/app/students/schemas.py +++ b/backend/app/students/schemas.py @@ -35,3 +35,30 @@ class ExaminationScheduleSchema(Schema): class ExaminationScheduleListSchema(Schema): examination_schedules = fields.List(fields.Nested(ExaminationScheduleSchema)) + + +class TermOfDefenceStudentItemSchema(Schema): + id = fields.Integer() + start_date = fields.DateTime() + end_date = fields.DateTime() + + +class TermOfDefenceStudentListSchema(Schema): + term_of_defences = fields.List(fields.Nested(TermOfDefenceStudentItemSchema)) + + +class YearGroupStudentSchema(Schema): + id = fields.Integer() + name = fields.Str() + mode = fields.Str() + + +class YearGroupStudentPaginationSchema(Schema): + year_groups = fields.List(fields.Nested(YearGroupStudentSchema)) + max_pages = fields.Integer() + + +class YearGroupQueryStudentSchema(Schema): + index = fields.Integer(required=True) # it will be removed + page = fields.Integer() + per_page = fields.Integer()