diff --git a/backend/app/coordinator/query/__init__.py b/backend/app/coordinator/query/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/app/coordinator/query/enrollments.py b/backend/app/coordinator/query/enrollments.py new file mode 100644 index 0000000..97ae875 --- /dev/null +++ b/backend/app/coordinator/query/enrollments.py @@ -0,0 +1,43 @@ +from flask import abort + +from ...examination_schedule.models import ExaminationSchedule, TermOfDefence +from ...students.models import Group + + +def get_term_of_defence_by_id_and_examination_schedule_id(examination_schedule_id: int, + term_of_defence_id: int) -> ExaminationSchedule: + td = TermOfDefence.query.filter(TermOfDefence.id == term_of_defence_id, + TermOfDefence.examination_schedule_id == examination_schedule_id).first() + if td is None: + abort(404, "Not found examination schedule or term of defence!") + return td + + +def get_group_by_id(group_id: int) -> Group: + group = Group.query.filter(Group.id == group_id).first() + if group is None: + abort(404, "Not found group!") + return group + + +def check_the_group_has_assigned_to_term_of_defence(group_id: int) -> TermOfDefence: + td = TermOfDefence.query.filter(TermOfDefence.group_id == group_id).first() + if td is not None: + abort(400, "Group has already assigned to term of defence!") + return td + + +def set_new_group_to_term_of_defence(examination_schedule_id: int, term_of_defence_id: int, + group_id: int) -> TermOfDefence: + td = get_term_of_defence_by_id_and_examination_schedule_id(examination_schedule_id, term_of_defence_id) + get_group_by_id(group_id) + check_the_group_has_assigned_to_term_of_defence(group_id) + td.group_id = group_id + return td + + +def get_examination_schedule_by_id(examination_schedule_id: int) -> ExaminationSchedule: + ex = ExaminationSchedule.query.filter(ExaminationSchedule.id == examination_schedule_id).first() + if ex is None: + abort(404, "Not found examination schedule!") + return ex diff --git a/backend/app/coordinator/routes/enrollments.py b/backend/app/coordinator/routes/enrollments.py index e32a9ff..8878e33 100644 --- a/backend/app/coordinator/routes/enrollments.py +++ b/backend/app/coordinator/routes/enrollments.py @@ -4,14 +4,17 @@ from itertools import islice from apiflask import APIBlueprint from flask import abort, current_app from sqlalchemy import or_, and_ +from flask_sqlalchemy import get_debug_queries from ..schemas import MessageSchema, TermOfDefenceSchema, TermOfDefenceListSchema, \ - TemporaryAvailabilityListSchema + TemporaryAvailabilityListSchema, AssignedGroupToTermOfDefenceListSchema, GroupIdSchema from ...examination_schedule.models import ExaminationSchedule, TermOfDefence, TemporaryAvailability from ...students.models import YearGroup from ...project_supervisor.models import ProjectSupervisor from ...dependencies import db from ..utils import generate_range_dates +from ..query.enrollments import get_term_of_defence_by_id_and_examination_schedule_id, set_new_group_to_term_of_defence, \ + get_examination_schedule_by_id bp = APIBlueprint("enrollments", __name__, url_prefix="/enrollments") @@ -19,9 +22,7 @@ bp = APIBlueprint("enrollments", __name__, url_prefix="/enrollments") @bp.post('//generate') @bp.output(MessageSchema) def generate_term_of_defence_for_this_examination_schedule(examination_schedule_id: int) -> dict: - ex = ExaminationSchedule.query.filter(ExaminationSchedule.id == examination_schedule_id).first() - if ex is None: - abort(404, "Not found examination schedule!") + ex = get_examination_schedule_by_id(examination_schedule_id) # print(ex.start_date.date()) # print(ex.end_date.date()) @@ -54,7 +55,8 @@ def generate_term_of_defence_for_this_examination_schedule(examination_schedule_ TermOfDefence.end_date < e) )).first() if td is None: - term_of_defence = TermOfDefence(start_date=d, end_date=e, examination_schedule_id=examination_schedule_id) + term_of_defence = TermOfDefence(start_date=d, end_date=e, + examination_schedule_id=examination_schedule_id) db.session.add(term_of_defence) db.session.commit() project_supervisors_ids = [] @@ -78,9 +80,7 @@ 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!") + ex = get_examination_schedule_by_id(examination_schedule_id) yg_id = ex.year_group_id project_supervisors_ids = data.pop('project_supervisors') @@ -186,23 +186,16 @@ def update_term_of_defence(examination_schedule_id: int, term_of_defence_id: int @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!") - + td = get_term_of_defence_by_id_and_examination_schedule_id(examination_schedule_id, term_of_defence_id) 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") + get_examination_schedule_by_id(examination_schedule_id) td = TermOfDefence.query. \ filter(TermOfDefence.examination_schedule_id == examination_schedule_id). \ @@ -214,12 +207,51 @@ def list_of_term_of_defences(examination_schedule_id: int) -> dict: @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") + get_examination_schedule_by_id(examination_schedule_id) td = TemporaryAvailability.query. \ filter(TemporaryAvailability.examination_schedule_id == examination_schedule_id). \ join(TemporaryAvailability.project_supervisor). \ all() return {"temporary_availabilities": td} + + +@bp.get('//assigned-group-to-term-of-defences/') +@bp.output(AssignedGroupToTermOfDefenceListSchema) +def list_of_assigned_group_to_term_of_defences(examination_schedule_id: int) -> dict: + get_examination_schedule_by_id(examination_schedule_id) + + td = TermOfDefence.query. \ + join(TermOfDefence.members_of_committee, isouter=True). \ + join(TermOfDefence.group). \ + filter(TermOfDefence.examination_schedule_id == examination_schedule_id). \ + filter(TermOfDefence.group_id.isnot(None)). \ + all() + return {"term_of_defences": td} + + +@bp.post('//term-of-defence//group/') +@bp.input(GroupIdSchema) +@bp.output(MessageSchema) +def add_group_to_term_of_defence(examination_schedule_id: int, term_of_defence_id: int, data: dict) -> dict: + set_new_group_to_term_of_defence(examination_schedule_id, term_of_defence_id, data.get("group_id")) + db.session.commit() + return {"message": "Group was added to term of defences!"} + + +@bp.delete('//term-of-defence//group/') +@bp.output(MessageSchema) +def delete_group_to_term_of_defence(examination_schedule_id: int, term_of_defence_id: int) -> dict: + td = get_term_of_defence_by_id_and_examination_schedule_id(examination_schedule_id, term_of_defence_id) + td.group_id = None + db.session.commit() + return {"message": "Group was deleted from term of defences!"} + + +@bp.put('//term-of-defence//group/') +@bp.input(GroupIdSchema) +@bp.output(MessageSchema) +def update_group_for_term_of_defence(examination_schedule_id: int, term_of_defence_id: int, data: dict) -> dict: + set_new_group_to_term_of_defence(examination_schedule_id, term_of_defence_id, data.get("group_id")) + db.session.commit() + return {"message": "Group for term of defence was updated!"} diff --git a/backend/app/coordinator/schemas/__init__.py b/backend/app/coordinator/schemas/__init__.py index caa8e29..2318019 100644 --- a/backend/app/coordinator/schemas/__init__.py +++ b/backend/app/coordinator/schemas/__init__.py @@ -1,7 +1,8 @@ -from .enrollments import TermOfDefenceSchema, TermOfDefenceListSchema, TemporaryAvailabilityListSchema +from .enrollments import TermOfDefenceSchema, TermOfDefenceListSchema, TemporaryAvailabilityListSchema, \ + AssignedGroupToTermOfDefenceListSchema from .examination_schedule import ExaminationScheduleSchema, ExaminationScheduleUpdateSchema, \ ExaminationSchedulesPaginationSchema, ExaminationSchedulesQuerySchema, WorkloadSchema -from .groups import GroupQuerySchema, GroupsPaginationSchema, GroupCreateSchema, GroupEditSchema +from .groups import GroupQuerySchema, GroupsPaginationSchema, GroupCreateSchema, GroupEditSchema, GroupIdSchema from .project_supervisor import ProjectSupervisorQuerySchema, ProjectSupervisorsPaginationSchema, \ ProjectSupervisorCreateSchema, ProjectSupervisorEditSchema, ProjectSupervisorYearGroupSchema from .students import ProjectSupervisorSchema, GroupSchema, StudentSchema, StudentsPaginationSchema, \ diff --git a/backend/app/coordinator/schemas/enrollments.py b/backend/app/coordinator/schemas/enrollments.py index bdfd0a5..09390b3 100644 --- a/backend/app/coordinator/schemas/enrollments.py +++ b/backend/app/coordinator/schemas/enrollments.py @@ -16,6 +16,7 @@ class ProjectSupervisorForTermOfDefenceSchema(Schema): class TermOfDefenceItemSchema(Schema): + id = fields.Integer() start_date = fields.DateTime() end_date = fields.DateTime() members_of_committee = fields.List(fields.Nested(ProjectSupervisorForTermOfDefenceSchema)) @@ -25,6 +26,25 @@ class TermOfDefenceListSchema(Schema): term_of_defences = fields.List(fields.Nested(TermOfDefenceItemSchema)) +class StudentDataItemSchema(Schema): + index = fields.Integer() + first_name = fields.Str() + last_name = fields.Str() + + +class GroupDataItemSchema(Schema): + name = fields.Str() + students = fields.List(fields.Nested(StudentDataItemSchema)) + + +class AssignedGroupToTermOfDefenceItemSchema(TermOfDefenceItemSchema): + group = fields.Nested(GroupDataItemSchema) + + +class AssignedGroupToTermOfDefenceListSchema(Schema): + term_of_defences = fields.List(fields.Nested(AssignedGroupToTermOfDefenceItemSchema)) + + class ProjectSupervisorForTemporaryAvailabilitySchema(Schema): id = fields.Str() first_name = fields.Str() diff --git a/backend/app/coordinator/schemas/groups.py b/backend/app/coordinator/schemas/groups.py index 34ff91a..cabc983 100644 --- a/backend/app/coordinator/schemas/groups.py +++ b/backend/app/coordinator/schemas/groups.py @@ -1,28 +1,31 @@ -from marshmallow import fields, validate +from marshmallow import Schema, fields, validate -from ...dependencies import ma from ..validators import validate_index from .students import GroupSchema, StudentSchema -class GroupQuerySchema(ma.Schema): +class GroupQuerySchema(Schema): name = fields.Str() page = fields.Integer() per_page = fields.Integer() -class GroupsPaginationSchema(ma.Schema): +class GroupsPaginationSchema(Schema): groups = fields.List(fields.Nested(GroupSchema)) max_pages = fields.Integer() -class GroupCreateSchema(ma.Schema): +class GroupCreateSchema(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): +class GroupEditSchema(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 GroupIdSchema(Schema): + group_id = fields.Integer(required=True) diff --git a/backend/app/examination_schedule/models.py b/backend/app/examination_schedule/models.py index 2ea3175..d50ced5 100644 --- a/backend/app/examination_schedule/models.py +++ b/backend/app/examination_schedule/models.py @@ -30,8 +30,8 @@ class TermOfDefence(Base): examination_schedule_id = db.Column(db.Integer, db.ForeignKey('examination_schedules.id')) examination_schedule = db.relationship('ExaminationSchedule', backref='term_of_defences') group_id = db.Column(db.Integer, db.ForeignKey('groups.id')) - group = db.relationship("Group", uselist=False, backref='term_of_defence') - members_of_committee = db.relationship("ProjectSupervisor", secondary=committee) + group = db.relationship("Group", uselist=False, backref='term_of_defence', lazy='joined') + members_of_committee = db.relationship("ProjectSupervisor", secondary=committee, lazy='joined') class TemporaryAvailability(Base): diff --git a/backend/app/students/models.py b/backend/app/students/models.py index bb28e51..29dcbb4 100644 --- a/backend/app/students/models.py +++ b/backend/app/students/models.py @@ -21,7 +21,7 @@ class YearGroup(Base): name = db.Column(db.String(50), nullable=False) mode = db.Column(db.String(1), nullable=False) created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) - students = db.relationship("YearGroupStudents") + students = db.relationship("YearGroupStudents", lazy='joined') __table__args = ( db.UniqueConstraint('name', 'mode', name='uc_name_mode_year_group') @@ -41,12 +41,12 @@ class Group(Base): prz_kod = db.Column(db.String(60), default='06-DPRILI0') tzaj_kod = db.Column(db.String(60), default='LAB') project_supervisor_id = db.Column(db.Integer, db.ForeignKey('project_supervisors.id')) - project_supervisor = db.relationship('ProjectSupervisor', backref='groups', lazy=True) + project_supervisor = db.relationship('ProjectSupervisor', backref='groups', lazy='joined') year_group_id = db.Column(db.Integer, db.ForeignKey('year_groups.id')) - year_group = db.relationship('YearGroup', backref='groups', lazy=True) + year_group = db.relationship('YearGroup', backref='groups', lazy='joined') points_for_first_term = db.Column(db.Integer, default=0, nullable=False) points_for_second_term = db.Column(db.Integer, default=0, nullable=False) - students = db.relationship('Student', secondary=students_groups, back_populates='groups') + students = db.relationship('Student', secondary=students_groups, back_populates='groups', lazy='joined') @classmethod def search_by_name(cls, year_group_id: int, search_name: str = None) -> BaseQuery: @@ -63,8 +63,8 @@ class Student(Person): pesel = db.Column(db.String(11), default='') index = db.Column(db.Integer, primary_key=True) - groups = db.relationship('Group', secondary=students_groups, back_populates='students') - year_groups = db.relationship("YearGroupStudents") + groups = db.relationship('Group', secondary=students_groups, back_populates='students', lazy='joined') + year_groups = db.relationship("YearGroupStudents", lazy='joined') @classmethod def search_by_fullname_and_mode_and_order_by_first_name_or_last_name(cls, year_group_id: int, fullname: str = None,