add year group and change logic of endpoints v2

This commit is contained in:
dominik24c 2022-11-12 16:18:07 +01:00
parent 82807b7c2e
commit eea98dd225
32 changed files with 762 additions and 291 deletions

View File

@ -13,4 +13,3 @@ COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .

7
backend/app/base/mode.py Normal file
View File

@ -0,0 +1,7 @@
from enum import Enum
class ModeGroups(str, Enum):
STATIONARY = 's'
NON_STATIONARY = 'n'
ENGLISH_SPEAKING_STATIONARY = 'e'

View File

@ -2,7 +2,7 @@ from flask.cli import with_appcontext
from click import command
from ..dependencies import db
from ..factory import ProjectSupervisorFactory, GroupFactory, StudentFactory
# from ..factory import ProjectSupervisorFactory, GroupFactory, StudentFactory
@command('init_db')
@ -11,27 +11,27 @@ def init_db() -> None:
"""Fill database with some data"""
db.drop_all()
db.create_all()
num_of_supervisors = 5
projects_supervisors = [ProjectSupervisorFactory() for _ in range(num_of_supervisors)]
db.session.add_all(projects_supervisors)
db.session.commit()
groups = [GroupFactory(project_supervisor=projects_supervisors[i]) for i in range(num_of_supervisors)]
db.session.add_all(groups)
db.session.commit()
num_of_students = num_of_supervisors * 3
students = [StudentFactory(group=groups[i % num_of_supervisors]) for i in range(num_of_students)]
max_count = 10
max_length = len(students)
start_count = 0
while True:
if start_count > max_length:
break
db.session.add_all(students[start_count:max_count])
db.session.commit()
start_count += max_count
#
# num_of_supervisors = 5
#
# projects_supervisors = [ProjectSupervisorFactory() for _ in range(num_of_supervisors)]
# db.session.add_all(projects_supervisors)
# db.session.commit()
#
# groups = [GroupFactory(project_supervisor=projects_supervisors[i]) for i in range(num_of_supervisors)]
# db.session.add_all(groups)
# db.session.commit()
#
# num_of_students = num_of_supervisors * 3
# students = [StudentFactory(group=groups[i % num_of_supervisors]) for i in range(num_of_students)]
#
# max_count = 10
# max_length = len(students)
# start_count = 0
#
# while True:
# if start_count > max_length:
# break
# db.session.add_all(students[start_count:max_count])
# db.session.commit()
# start_count += max_count

View File

@ -1,17 +1,19 @@
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
from .workloads import bp as workloads_bp
from .year_group import bp as year_group_bp
# from .workloads import bp as workloads_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(year_group_bp)
bp.register_blueprint(examination_schedule_bp)
bp.register_blueprint(enrollments_bp)
bp.register_blueprint(workloads_bp)
# bp.register_blueprint(enrollments_bp)
# bp.register_blueprint(workloads_bp)

View File

@ -4,7 +4,7 @@ from apiflask import APIBlueprint
from flask import abort, current_app
from ..schemas import MessageSchema, EnrollmentCreateSchema
from ...examination_schedule.models import Enrollment, Committee, ExaminationSchedule
from ...examination_schedule.models import ExaminationSchedule
from ...dependencies import db
bp = APIBlueprint("enrollments", __name__, url_prefix="/enrollments")

View File

@ -5,8 +5,8 @@ from flask import abort, Response, make_response
from ...base.utils import paginate_models
from ...dependencies import db
from ...examination_schedule.models import ExaminationSchedule, Enrollment
from ...students.models import Group
from ...examination_schedule.models import ExaminationSchedule
from ...students.models import Group, YearGroup
from ...project_supervisor.models import ProjectSupervisor
from ..schemas import ExaminationScheduleSchema, ExaminationScheduleUpdateSchema, MessageSchema, \
ExaminationSchedulesQuerySchema, ExaminationSchedulesPaginationSchema
@ -15,21 +15,29 @@ from ..utils import generate_examination_schedule_pdf_file
bp = APIBlueprint("examination_schedule", __name__, url_prefix="/examination_schedule")
@bp.get('/')
@bp.get('/<int:year_group_id>/')
@bp.input(ExaminationSchedulesQuerySchema, location='query')
@bp.output(ExaminationSchedulesPaginationSchema)
def list_examination_schedule(query: dict) -> dict:
def list_examination_schedule(year_group_id: int, query: dict) -> dict:
page = query.get('page')
per_page = query.get('per_page')
data = paginate_models(page, ExaminationSchedule.query, per_page)
es_query = ExaminationSchedule.query.filter(ExaminationSchedule.year_group_id == year_group_id)
data = paginate_models(page, es_query, per_page)
return {'examination_schedules': data['items'], 'max_pages': data['max_pages']}
@bp.post('/')
@bp.post('/<int:year_group_id>/')
@bp.input(ExaminationScheduleSchema)
@bp.output(MessageSchema)
def create_examination_schedule(data: dict) -> dict:
examination_schedule = ExaminationSchedule(**data)
def create_examination_schedule(year_group_id: int, data: dict) -> dict:
yg = YearGroup.query.filter(YearGroup.id == year_group_id).first()
if yg is None:
abort(404, "Year group doesn't exist!")
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)
db.session.add(examination_schedule)
db.session.commit()
return {"message": "Examination schedule was created!"}
@ -70,7 +78,7 @@ def set_date_of_examination_schedule(id: int, data: dict) -> dict:
if examination_schedule is None:
abort(404, "Examination schedule doesn't exist!")
if data['start_date'] > data['end_date']:
if data['start_date_for_enrollment_students'] > data['end_date_for_enrollment_students']:
abort(400, "Invalid data! End date must be greater than start date!")
examination_schedule_query.update(data)
@ -91,7 +99,7 @@ def download_examination_schedule(examination_schedule_id: int) -> Response:
nested_enrollments = []
for d in distinct_dates:
date_tmp = datetime.datetime.strptime(d[0], "%Y-%m-%d").date()
enrollment = db.session.query(Enrollment).join(ExaminationSchedule, isouter=True).\
enrollment = db.session.query(Enrollment).join(ExaminationSchedule, isouter=True). \
join(Group, isouter=True).join(ProjectSupervisor, isouter=True). \
filter(ExaminationSchedule.id == examination_schedule_id).filter(
db.func.Date(Enrollment.start_date) == date_tmp).all()

View File

@ -2,8 +2,8 @@ from flask import abort, current_app
from apiflask import APIBlueprint
from flask_sqlalchemy import get_debug_queries
from ...students.models import Group, Student
from ...project_supervisor.models import ProjectSupervisor
from ...students.models import Group, Student, YearGroup
from ...project_supervisor.models import ProjectSupervisor, YearGroupProjectSupervisors
from ..schemas import GroupSchema, GroupEditSchema, GroupsPaginationSchema, \
GroupCreateSchema, MessageSchema, GroupQuerySchema
from ...dependencies import db
@ -12,15 +12,15 @@ from ...base.utils import paginate_models
bp = APIBlueprint("groups", __name__, url_prefix="/groups")
@bp.route("/", methods=["GET"])
@bp.get("/<int:year_group_id>/")
@bp.input(GroupQuerySchema, location='query')
@bp.output(GroupsPaginationSchema)
def list_groups(query: dict) -> dict:
def list_groups(year_group_id: int, query: dict) -> dict:
search_name = query.get('name')
page = query.get('page')
per_page = query.get('per_page')
groups_query = Group.search_by_name(search_name)
groups_query = Group.search_by_name(year_group_id, search_name)
data = paginate_models(page, groups_query, per_page)
@ -30,14 +30,18 @@ def list_groups(query: dict) -> dict:
}
@bp.route("/", methods=["POST"])
@bp.post("/<int:year_group_id>/")
@bp.input(GroupCreateSchema)
@bp.output(MessageSchema)
def create_group(data: dict) -> dict:
def create_group(year_group_id: int, data: dict) -> dict:
name = data['name']
students_indexes = data['students']
project_supervisor_id = data['project_supervisor_id']
yg = YearGroup.query.filter(YearGroup.id == year_group_id).first()
if yg is None:
abort(404, "YearGroup doesn't exist!")
project_supervisor = ProjectSupervisor.query.filter_by(id=project_supervisor_id).first()
limit_student_per_group = current_app.config.get('LIMIT_STUDENTS_PER_GROUP')
@ -47,13 +51,16 @@ def create_group(data: dict) -> dict:
abort(400, f"Too much students you want add to group, "
f"The group can have only {limit_student_per_group} students")
limit = db.session.query(ProjectSupervisor.limit_group - db.func.count(ProjectSupervisor.id)).join(Group).filter(
ProjectSupervisor.id == project_supervisor_id).group_by(ProjectSupervisor.id).scalar()
limit = db.session.query(db.func.count(ProjectSupervisor.id)). \
join(Group).filter(ProjectSupervisor.id == project_supervisor_id).group_by(ProjectSupervisor.id).scalar()
if limit is not None and limit <= 0:
ygps = YearGroupProjectSupervisors.query. \
filter(YearGroupProjectSupervisors.year_group_id == year_group_id,
YearGroupProjectSupervisors.project_supervisor_id == project_supervisor_id).first()
if limit is not None and ygps is not None and limit >= ygps.limit_group:
abort(400, "Can't create new group, project supervisor achieved a limit of groups")
group = Group(name=name, project_supervisor_id=project_supervisor_id)
group = Group(name=name, project_supervisor_id=project_supervisor_id, year_group_id=year_group_id)
students_without_groups = db.session.query(Student).join(Group, isouter=True) \
.filter(Group.id.is_(None)).filter(Student.index.in_(students_indexes)).count()
@ -65,7 +72,6 @@ def create_group(data: dict) -> dict:
db.session.commit()
students = db.session.query(Student).filter(Student.index.in_(students_indexes)).all()
project_supervisor.count_groups += 1
for student in students:
student.group_id = group.id
@ -74,7 +80,7 @@ def create_group(data: dict) -> dict:
return {"message": "Group was created!"}
@bp.route("/<int:id>/", methods=["GET"])
@bp.get("/<int:id>/")
@bp.output(GroupSchema)
def detail_group(id: int) -> Group:
group = Group.query.filter_by(id=id).first()
@ -83,16 +89,13 @@ def detail_group(id: int) -> Group:
return group
@bp.route("/<int:id>/", methods=["DELETE"])
@bp.delete("/<int:id>/")
@bp.output(MessageSchema, status_code=202)
def delete_group(id: int) -> dict:
group = Group.query.filter_by(id=id).first()
if group is None:
abort(400, f"Group with id {id} doesn't exist!")
project_supervisor = ProjectSupervisor.query.filter_by(id=group.project_supervisor_id).first()
project_supervisor.count_groups -= 1
students = db.session.query(Student).filter_by(group_id=id).all()
for student in students:
student.group_id = None
@ -102,7 +105,7 @@ def delete_group(id: int) -> dict:
return {"message": "Group was deleted!"}
@bp.route("/<int:id>", methods=["PUT"])
@bp.put("/<int:id>/")
@bp.input(GroupEditSchema)
@bp.output(MessageSchema)
def edit_group(id: int, data: dict) -> dict:

View File

@ -2,29 +2,28 @@ from flask import abort
from apiflask import APIBlueprint
from flask_sqlalchemy import get_debug_queries
from ...project_supervisor.models import ProjectSupervisor
from ...students.models import Group
from ...project_supervisor.models import ProjectSupervisor, YearGroupProjectSupervisors
from ...students.models import Group, YearGroup
from ..schemas import ProjectSupervisorSchema, ProjectSupervisorEditSchema, ProjectSupervisorsPaginationSchema, \
ProjectSupervisorCreateSchema, MessageSchema, ProjectSupervisorQuerySchema
ProjectSupervisorCreateSchema, MessageSchema, ProjectSupervisorQuerySchema, ProjectSupervisorYearGroupSchema
from ...dependencies import db
from ...base.utils import paginate_models
bp = APIBlueprint("project_supervisor", __name__, url_prefix="/project_supervisor")
@bp.route("/", methods=["GET"])
@bp.get("/")
@bp.input(ProjectSupervisorQuerySchema, location='query')
@bp.output(ProjectSupervisorsPaginationSchema)
def list_project_supervisors(query: dict) -> dict:
fullname = query.get('fullname')
order_by_first_name = query.get('order_by_first_name')
order_by_last_name = query.get('order_by_last_name')
mode = query.get('mode')
page = query.get('page')
per_page = query.get('per_page')
project_supervisor_query = ProjectSupervisor.search_by_fullname_and_mode_and_order_by_first_name_or_last_name(
fullname, mode, order_by_first_name, order_by_last_name)
None, fullname, order_by_first_name, order_by_last_name)
data = paginate_models(page, project_supervisor_query, per_page)
# print(get_debug_queries()[0])
@ -34,7 +33,28 @@ def list_project_supervisors(query: dict) -> dict:
}
@bp.route("/", methods=["POST"])
@bp.get("/<int:year_group_id>/")
@bp.input(ProjectSupervisorQuerySchema, location='query')
@bp.output(ProjectSupervisorsPaginationSchema)
def list_project_supervisors_by_year_group(year_group_id: int, query: dict) -> dict:
fullname = query.get('fullname')
order_by_first_name = query.get('order_by_first_name')
order_by_last_name = query.get('order_by_last_name')
page = query.get('page')
per_page = query.get('per_page')
project_supervisor_query = ProjectSupervisor.search_by_fullname_and_mode_and_order_by_first_name_or_last_name(
year_group_id, fullname, order_by_first_name, order_by_last_name)
data = paginate_models(page, project_supervisor_query, per_page)
# print(get_debug_queries()[0])
return {
"project_supervisors": data['items'],
"max_pages": data['max_pages']
}
@bp.post("/")
@bp.input(ProjectSupervisorCreateSchema)
@bp.output(MessageSchema)
def create_project_supervisor(data: dict) -> dict:
@ -43,7 +63,7 @@ def create_project_supervisor(data: dict) -> dict:
project_supervisor = ProjectSupervisor.query.filter_by(first_name=first_name).filter_by(last_name=last_name).first()
if project_supervisor is not None:
abort(400, "Project Supervisor has already exists!")
project_supervisor = ProjectSupervisor(**data)
db.session.add(project_supervisor)
@ -52,7 +72,7 @@ def create_project_supervisor(data: dict) -> dict:
return {"message": "Project Supervisor was created!"}
@bp.route("/<int:id>/", methods=["GET"])
@bp.get("/<int:id>/")
@bp.output(ProjectSupervisorSchema)
def detail_project_supervisor(id: int) -> ProjectSupervisor:
project_supervisor = ProjectSupervisor.query.filter_by(id=id).first()
@ -61,37 +81,99 @@ def detail_project_supervisor(id: int) -> ProjectSupervisor:
return project_supervisor
@bp.route("/<int:id>/", methods=["DELETE"])
@bp.delete("/<int:id>/")
@bp.output(MessageSchema, status_code=202)
def delete_project_supervisor(id: int) -> dict:
project_supervisor = ProjectSupervisor.query.filter_by(id=id).first()
if project_supervisor is None:
abort(400, f"Project Supervisor with id {id} doesn't exist!")
count_groups = db.session.query(db.func.count(ProjectSupervisor.id)).join(Group).\
count_groups = db.session.query(db.func.count(ProjectSupervisor.id)).join(Group). \
filter(ProjectSupervisor.id == id).group_by(ProjectSupervisor.id)
if count_groups is not None:
abort(400, f"Project Supervisor with id {id} has gropus!")
abort(400, f"Project Supervisor with id {id} has groups!")
db.session.delete(project_supervisor)
db.session.commit()
return {"message": "Project Supervisor was deleted!"}
@bp.route("/<int:id>", methods=["PUT"])
@bp.put("/<int:id>/")
@bp.input(ProjectSupervisorEditSchema)
@bp.output(MessageSchema)
def edit_project_supervisor(id: int, data: dict) -> dict:
if not data:
abort(400, 'You have passed empty data!')
project_supervisor_query = ProjectSupervisor.query.filter_by(id=id)
project_supervisor = project_supervisor_query.first()
if project_supervisor is None:
abort(400, f"Project Supervisor with id {id} doesn't exist!")
project_supervisor_query.update(data)
db.session.commit()
return {"message": "Project Supervisor was updated!"}
@bp.post("/<int:id>/year-group/<int:year_group_id>")
@bp.input(ProjectSupervisorYearGroupSchema)
@bp.output(MessageSchema)
def add_project_supervisor_to_year_group(id: int, year_group_id: int, data: dict) -> dict:
if not data:
abort(400, 'You have passed empty data!')
limit_group = data.get('limit_group')
project_supervisor = ProjectSupervisor.query.filter(ProjectSupervisor.id == id).first()
if project_supervisor is None:
abort(400, f"Project Supervisor with id {id} doesn't exist!")
year_group = YearGroup.query.filter(YearGroup.id == year_group_id).first()
if year_group is None:
abort(400, "Year group doesn't exist!")
ygps = YearGroupProjectSupervisors.query.filter(YearGroupProjectSupervisors.project_supervisor_id == id). \
filter(YearGroupProjectSupervisors.year_group_id == year_group_id).first()
if ygps is not None:
abort(400, "Project supervisor is assigned to this year group!")
ygps = YearGroupProjectSupervisors(year_group_id=year_group_id, project_supervisor_id=id,
limit_group=limit_group)
db.session.add(ygps)
db.session.commit()
return {"message": "Project Supervisor was added to year group!"}
@bp.delete("/<int:id>/year-group/<int:year_group_id>")
@bp.input(ProjectSupervisorYearGroupSchema)
@bp.output(MessageSchema)
def delete_project_supervisor_to_year_group(id: int, year_group_id: int) -> dict:
project_supervisor = ProjectSupervisor.query.filter(ProjectSupervisor.id == id). \
filter(YearGroup.id == year_group_id).first()
if project_supervisor is None:
abort(400, "Project Supervisor doesn't exist!")
ygps = YearGroupProjectSupervisors.query.filter(YearGroupProjectSupervisors.project_supervisor_id == id). \
filter(YearGroupProjectSupervisors.year_group_id == year_group_id).first()
db.session.delete(project_supervisor)
db.session.delete(ygps)
db.session.commit()
return {"message": "Project Supervisor was removed from this year group!"}
@bp.put("/<int:id>/year-group/<int:year_group_id>")
@bp.input(ProjectSupervisorYearGroupSchema)
@bp.output(MessageSchema)
def update_limit_of_group_for_project_supervisor(id: int, year_group_id: int, data: dict) -> dict:
project_supervisor = ProjectSupervisor.query.filter(ProjectSupervisor.id == id). \
filter(YearGroup.id == year_group_id).first()
if project_supervisor is None:
abort(400, "Project Supervisor doesn't exist!")
ygps = YearGroupProjectSupervisors.query.filter(YearGroupProjectSupervisors.project_supervisor_id == id). \
filter(YearGroupProjectSupervisors.year_group_id == year_group_id)
ygps.update(data)
db.session.commit()
return {"message": "Limit of group was changed!"}

View File

@ -1,13 +1,12 @@
from random import randint
from itertools import islice
from typing import List
from flask import Response, abort
from apiflask import APIBlueprint
from sqlalchemy.exc import IntegrityError
from flask_sqlalchemy import get_debug_queries
from ...students.models import Student, Group
from ...students.models import Student, Group, YearGroup, YearGroupStudents
from ...project_supervisor.models import ProjectSupervisor
from ..schemas import StudentSchema, StudentEditSchema, StudentsPaginationSchema, \
StudentCreateSchema, MessageSchema, FileSchema, StudentQuerySchema, StudentListFileDownloaderSchema
@ -19,19 +18,19 @@ from ...base.utils import paginate_models, is_allowed_extensions
bp = APIBlueprint("students", __name__, url_prefix="/students")
@bp.route("/", methods=["GET"])
@bp.get("/<int:year_group_id>/")
@bp.input(StudentQuerySchema, location='query')
@bp.output(StudentsPaginationSchema)
def list_students(query: dict) -> dict:
def list_students(year_group_id: int, query: dict) -> dict:
# add filter by year group
fullname = query.get('fullname')
order_by_first_name = query.get('order_by_first_name')
order_by_last_name = query.get('order_by_last_name')
mode = query.get('mode')
page = query.get('page')
per_page = query.get('per_page')
student_query = Student.search_by_fullname_and_mode_and_order_by_first_name_or_last_name(
fullname, mode, order_by_first_name, order_by_last_name)
year_group_id, fullname, order_by_first_name, order_by_last_name)
data = paginate_models(page, student_query, per_page)
# print(get_debug_queries()[0])
@ -41,7 +40,7 @@ def list_students(query: dict) -> dict:
}
@bp.route("/<int:index>/", methods=["GET"])
@bp.get("/<int:index>/")
@bp.output(StudentSchema)
def detail_student(index: int) -> Student:
student = Student.query.filter_by(index=index).first()
@ -50,7 +49,7 @@ def detail_student(index: int) -> Student:
return student
@bp.route("/<int:index>/", methods=["DELETE"])
@bp.delete("/<int:index>/")
@bp.output(MessageSchema, status_code=202)
def delete_student(index: int) -> dict:
student = Student.query.filter_by(index=index).first()
@ -61,7 +60,7 @@ def delete_student(index: int) -> dict:
return {"message": "Student was deleted!"}
@bp.route("/<int:index>/", methods=["PUT"])
@bp.put("/<int:index>/")
@bp.input(StudentEditSchema)
@bp.output(MessageSchema)
def edit_student(index: int, data: dict) -> dict:
@ -80,11 +79,13 @@ def edit_student(index: int, data: dict) -> dict:
return {"message": "Student was updated!"}
@bp.route("/", methods=["POST"])
@bp.post("/")
@bp.input(StudentCreateSchema)
@bp.output(MessageSchema)
def create_student(data: dict) -> dict:
index = data['index']
yg_id = data['year_group_id']
del data['year_group_id']
student = Student.query.filter_by(index=index).first()
if student is not None:
abort(400, "Student has already exists!")
@ -92,12 +93,20 @@ def create_student(data: dict) -> dict:
dummy_email = f'student{randint(1, 300_000)}@gmail.com'
student = Student(**data, email=dummy_email)
db.session.add(student)
# add student to the chosen year group
year_group = YearGroup.query.filter(YearGroup.id == yg_id).first()
if year_group is None:
abort(400, "Year group doesn't exist!")
ygs = YearGroupStudents(student_index=student.index, year_group_id=year_group.id)
db.session.add(ygs)
db.session.commit()
return {"message": "Student was created!"}
@bp.route("/upload/", methods=["POST"])
@bp.post("/upload/")
@bp.input(FileSchema, location='form_and_files')
@bp.output(MessageSchema)
def upload_students(file: dict) -> dict:
@ -125,7 +134,7 @@ def upload_students(file: dict) -> dict:
return {"message": "Students was created by uploading csv file!"}
@bp.route("/download/", methods=["POST"])
@bp.post("/download/")
@bp.input(StudentListFileDownloaderSchema, location='query')
def download_students(query: dict) -> Response:
mode = query.get('mode')

View File

@ -0,0 +1,72 @@
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
from ...dependencies import db
from ...base.utils import paginate_models
bp = APIBlueprint("year_group", __name__, url_prefix="/year-group")
@bp.post('/')
@bp.input(YearGroupSchema)
@bp.output(MessageSchema, status_code=200)
def create_year_group(data: dict) -> dict:
name = data['name']
year_group = YearGroup.query.filter(YearGroup.name == name).first()
if year_group is not None:
abort(400, "Year group has already exists!")
yg = YearGroup(**data)
db.session.add(yg)
db.session.commit()
return {"message": "Year group was created!"}
@bp.get('/')
@bp.input(YearGroupQuerySchema, location='query')
@bp.output(YearGroupPaginationSchema)
def list_of_year_groups(query: dict) -> dict:
page = query.get('page')
per_page = query.get('per_page')
year_group_query = YearGroup.query.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']
}
@bp.put('/<int:id>/')
@bp.input(YearGroupSchema)
@bp.output(MessageSchema)
def update_year_of_group(id: int, data: dict) -> dict:
if not data:
abort(400, 'You have passed empty data!')
year_group_query = YearGroup.query.filter(YearGroup.id == id)
year_group = year_group_query.first()
if year_group is None:
abort(404, 'Not found year group!')
year_group_query.update(data)
db.session.commit()
return {"message": "Year group was updated!"}
@bp.delete('/<int:id>/')
@bp.output(MessageSchema, status_code=202)
def delete_year_of_group(id: int) -> dict:
year_group = YearGroup.query.filter_by(id=id).first()
if year_group is None:
abort(404, f"Year group doesn't exist!")
db.session.delete(year_group)
db.session.commit()
return {"message": "Year group was deleted!"}

View File

@ -3,7 +3,8 @@ from .examination_schedule import ExaminationScheduleSchema, ExaminationSchedule
ExaminationSchedulesPaginationSchema, ExaminationSchedulesQuerySchema, WorkloadSchema
from .groups import GroupQuerySchema, GroupsPaginationSchema, GroupCreateSchema, GroupEditSchema
from .project_supervisor import ProjectSupervisorQuerySchema, ProjectSupervisorsPaginationSchema, \
ProjectSupervisorCreateSchema, ProjectSupervisorEditSchema
ProjectSupervisorCreateSchema, ProjectSupervisorEditSchema, ProjectSupervisorYearGroupSchema
from .students import ProjectSupervisorSchema, GroupSchema, StudentSchema, StudentsPaginationSchema, \
StudentListFileDownloaderSchema, StudentCreateSchema, StudentEditSchema, MessageSchema, FileSchema, \
StudentQuerySchema
from .year_group import YearGroupSchema, YearGroupPaginationSchema, YearGroupQuerySchema

View File

@ -5,20 +5,22 @@ from ..validators import validate_datetime_greater_than_now
class ExaminationScheduleSchema(Schema):
title = fields.Str(validate=validate.Length(min=1, max=100), required=True)
mode = fields.Boolean(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 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)
start_date_for_enrollment_students = fields.DateTime(validate=validate_datetime_greater_than_now, required=True)
end_date_for_enrollment_students = fields.DateTime(validate=validate_datetime_greater_than_now, required=True)
class ExaminationScheduleListItemSchema(Schema):
id = fields.Integer(required=True)
title = fields.Str(validate=validate.Length(min=1, max=100), required=True)
mode = fields.Boolean(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)
id = fields.Integer()
title = fields.Str()
start_date = fields.DateTime()
end_date = fields.DateTime()
start_date_for_enrollment_students = fields.DateTime()
end_date_for_enrollment_students = fields.DateTime()
class ExaminationSchedulesPaginationSchema(Schema):
@ -32,7 +34,7 @@ class ExaminationSchedulesQuerySchema(Schema):
class ProjectSupervisorStatisticsSchema(Schema):
full_name = fields.Str()
fullname = fields.Str()
assigned_to_committee = fields.Integer()
groups_assigned_to_his_committee = fields.Integer()

View File

@ -1,36 +1,32 @@
from marshmallow import fields, validate
from marshmallow import fields, validate, Schema
from ...dependencies import ma
from ..validators import validate_index
from .students import ProjectSupervisorSchema
class ProjectSupervisorQuerySchema(ma.Schema):
class ProjectSupervisorQuerySchema(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):
class ProjectSupervisorsPaginationSchema(Schema):
project_supervisors = fields.List(fields.Nested(ProjectSupervisorSchema))
max_pages = fields.Integer()
class ProjectSupervisorCreateSchema(ma.Schema):
class ProjectSupervisorCreateSchema(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):
class ProjectSupervisorEditSchema(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)
class ProjectSupervisorYearGroupSchema(Schema):
limit_group = fields.Integer(required=True)

View File

@ -40,7 +40,7 @@ class StudentCreateSchema(ma.Schema):
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)
year_group_id = fields.Integer()
class StudentEditSchema(ma.Schema):
@ -48,7 +48,6 @@ class StudentEditSchema(ma.Schema):
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):
@ -65,4 +64,3 @@ class StudentQuerySchema(ma.Schema):
order_by_last_name = fields.Str()
page = fields.Integer()
per_page = fields.Integer()
mode = fields.Boolean()

View File

@ -0,0 +1,30 @@
from marshmallow import Schema, fields, validate, ValidationError
from ...base.mode import ModeGroups
def validate_mode(value: str) -> str:
if value not in [m.value for m in ModeGroups]:
raise ValidationError("Invalid mode!")
return value
class YearGroupSchema(Schema):
name = fields.Str(validate=validate.Regexp('^\d{4}/\d{4}$'), required=True)
mode = fields.Str(validate=validate_mode, required=True)
class YearGroupItemSchema(Schema):
id = fields.Integer()
name = fields.Str()
mode = fields.Str()
class YearGroupPaginationSchema(Schema):
year_groups = fields.List(fields.Nested(YearGroupItemSchema))
max_pages = fields.Integer()
class YearGroupQuerySchema(Schema):
page = fields.Integer()
per_page = fields.Integer()

View File

@ -13,7 +13,7 @@ from reportlab.platypus import SimpleDocTemplate, Paragraph, PageBreak, Table
from .exceptions import InvalidNameOrTypeHeaderException
from ..students.models import Student
from ..examination_schedule.models import Enrollment
# from ..examination_schedule.models import Enrollment
def check_columns(df: pd.DataFrame) -> bool:
@ -66,7 +66,7 @@ def generate_csv(students: List[Student]) -> str:
return df.to_csv(index=False)
def generate_examination_schedule_pdf_file(title: str, nested_enrollments: List[List[Enrollment]]) -> bytes:
def generate_examination_schedule_pdf_file(title: str, nested_enrollments: List[List[object]]) -> bytes:
pagesize = (297 * mm, 210 * mm)
headers = ["lp.", "Godzina", "Nazwa projektu", "Opiekun", "Zespol", "Komisja"]
pdf_buffer = BytesIO()

View File

@ -6,33 +6,40 @@ class ExaminationSchedule(Base):
__tablename__ = 'examination_schedules'
title = db.Column(db.String(100), unique=True, nullable=False)
mode = db.Column(db.Boolean, default=True, nullable=False) # True - stationary, False - non-stationary
start_date = db.Column(db.DateTime)
end_date = db.Column(db.DateTime)
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)
end_date = db.Column(db.DateTime, nullable=False)
year_group_id = db.Column(db.Integer, db.ForeignKey('year_groups.id'), nullable=False)
year_group = db.relationship('YearGroup', backref='examination_schedules')
class Enrollment(Base):
__tablename__ = 'enrollments'
committee = db.Table(
"committees",
db.Column("term_of_defence_id", db.ForeignKey("term_of_defences.id")),
db.Column("project_supervisor_id", db.ForeignKey("project_supervisors.id")),
)
class TermOfDefence(Base):
__tablename__ = 'term_of_defences'
start_date = db.Column(db.DateTime, nullable=False)
end_date = db.Column(db.DateTime, nullable=False)
examination_schedule_id = db.Column(db.Integer, db.ForeignKey('examination_schedules.id'))
examination_schedule = db.relationship('ExaminationSchedule', backref='enrollments')
committee = db.relationship("Committee", uselist=False, backref=db.backref('enrollment', passive_deletes=True))
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='enrollment')
group = db.relationship("Group", uselist=False, backref='term_of_defence')
members_of_committee = db.relationship("ProjectSupervisor", secondary=committee)
class Committee(Base):
__tablename__ = 'committees'
class TemporaryAvailability(Base):
__tablename__ = 'temporary_availabilities'
enrollment_id = db.Column(db.Integer, db.ForeignKey('enrollments.id', ondelete='CASCADE'))
members = db.relationship('ProjectSupervisor', secondary='committees_projects_supervisors', backref='committees')
class CommitteeProjectSupervisor(Base):
__tablename__ = 'committees_projects_supervisors'
chairman = db.Column(db.Boolean, default=False, nullable=False)
committee_id = db.Column(db.Integer, db.ForeignKey('committees.id'))
member_id = db.Column(db.Integer, db.ForeignKey('project_supervisors.id'))
start_date = db.Column(db.DateTime, nullable=False)
end_date = db.Column(db.DateTime, nullable=False)
examination_schedule_id = db.Column(db.Integer, db.ForeignKey('examination_schedules.id'), nullable=False)
examination_schedule = db.relationship("ExaminationSchedule", backref='temporary_availabilities')
project_supervisor_id = db.Column(db.Integer, db.ForeignKey('project_supervisors.id'), nullable=False)
project_supervisor = db.relationship("ProjectSupervisor", backref='temporary_availabilities')

View File

@ -1,9 +1,3 @@
from flask import Blueprint
from .enrollments import bp as enrollments_bp
from .examination_schedule import bp as examination_schedule_bp
bp = Blueprint("examination_schedule", __name__, url_prefix="/examination_schedule")
bp.register_blueprint(enrollments_bp)
bp.register_blueprint(examination_schedule_bp)

View File

@ -3,7 +3,7 @@ import datetime
from apiflask import APIBlueprint
from flask import abort
from ..schemas import EnrollmentPaginationSchema, EnrollmentQuerySchema
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")
@ -33,29 +33,7 @@ def list_enrollments_for_students(examination_schedule_id: int, query: dict) ->
@bp.get('/<int:examination_schedule_id>/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')
@bp.output(ExaminationScheduleProjectSupervisorViewSchema)
def list_enrollments_for_coordinator(examination_schedule_id: int) -> dict:
return check_examination_schedule_is_exist(examination_schedule_id)
check_examination_schedule_is_exist(examination_schedule_id)
return get_list_of_enrollments_response(examination_schedule_id, page, per_page)
@bp.get('/<int:examination_schedule_id>/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)

View File

@ -1,32 +1,18 @@
import datetime
from apiflask import APIBlueprint
from flask import abort
from ...dependencies import db
from ..models import ExaminationSchedule
from ..schemas import ExaminationScheduleListSchema
bp = APIBlueprint("list_of_examination_schedule", __name__, url_prefix="/")
@bp.get('/project-supervisor-view/')
@bp.output(ExaminationScheduleListSchema)
def list_examination_schedule_for_project_supervisors() -> dict:
now = datetime.datetime.utcnow()
examination_schedules = db.session.query(ExaminationSchedule). \
filter(ExaminationSchedule.start_date > now). \
all()
return {'examination_schedules': examination_schedules}
@bp.get('/students-view/')
@bp.output(ExaminationScheduleListSchema)
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 < now).\
filter(ExaminationSchedule.end_date > now).\
all()
filter(ExaminationSchedule.start_date_for_enrollment_students < now).\
filter(ExaminationSchedule.end_date_for_enrollment_students > now).all()
return {'examination_schedules': examination_schedules}

View File

@ -27,15 +27,13 @@ class EnrollmentPaginationSchema(Schema):
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()
class ExaminationScheduleSchema(Schema):
id = fields.Integer()
title = fields.Str()
class ExaminationScheduleListSchema(Schema):
examination_schedules = fields.List(fields.Nested(ExaminationScheduleSchema))

View File

@ -1,7 +1,7 @@
from flask import abort
from ..dependencies import db
from .models import Enrollment, Committee, ExaminationSchedule
from .models import ExaminationSchedule
from ..students.models import Group
from ..base.utils import paginate_models

View File

@ -3,24 +3,31 @@ from flask_sqlalchemy import BaseQuery
from ..dependencies import db
from ..base.models import Person, Base
from ..base.utils import order_by_column_name
from ..students.models import YearGroup
class YearGroupProjectSupervisors(Base):
__tablename__ = 'year_group_project_supervisors'
project_supervisor_id = db.Column(db.Integer, db.ForeignKey('project_supervisors.id'), nullable=False)
year_group_id = db.Column(db.Integer, db.ForeignKey('year_groups.id'), nullable=False)
limit_group = db.Column(db.Integer, default=3, nullable=False)
class ProjectSupervisor(Base, Person):
__tablename__ = "project_supervisors"
limit_group = db.Column(db.Integer, default=3, nullable=False)
count_groups = db.Column(db.Integer, default=0, nullable=False)
mode = db.Column(db.Integer, default=0, nullable=False) # 0 - stationary, 1 - non-stationary, 2 - both
year_groups = db.relationship('YearGroupProjectSupervisors', lazy=True)
@classmethod
def search_by_fullname_and_mode_and_order_by_first_name_or_last_name(cls, fullname: str = None,
mode: int = None,
def search_by_fullname_and_mode_and_order_by_first_name_or_last_name(cls, year_group_id: int = None,
fullname: str = None,
order_by_first_name: str = None,
order_by_last_name: str = None) -> BaseQuery:
project_supervisors_query = cls.query
if mode is not None:
project_supervisors_query = project_supervisors_query.filter(mode != 1 - mode)
if year_group_id is not None:
project_supervisors_query = project_supervisors_query.filter(YearGroup.id == year_group_id)
if fullname is not None:
project_supervisors_query = project_supervisors_query.filter(

View File

@ -3,11 +3,11 @@ import datetime
from flask import abort
from ..dependencies import db
from ..examination_schedule.models import Enrollment, ExaminationSchedule, Committee
from ..examination_schedule.models import ExaminationSchedule
def get_enrollment_by_enrollment_and_examination_schedule_ids(examination_schedule_id: int,
enrollment_id: int) -> Enrollment:
enrollment_id: int) -> None:
enrollment = db.session.query(Enrollment). \
join(ExaminationSchedule, isouter=True).join(Committee, isouter=True). \
filter(ExaminationSchedule.id == examination_schedule_id). \
@ -20,12 +20,6 @@ def get_enrollment_by_enrollment_and_examination_schedule_ids(examination_schedu
return enrollment
def check_the_project_supervisor_is_in_committee(enrollment_id: int, project_supervisor_id) -> bool:
return db.session.query(
Committee.query.join(Committee.members).filter(Committee.enrollment_id == enrollment_id).filter(
Committee.members.any(id=project_supervisor_id)).exists()).scalar()
def check_the_enrollments_has_just_started(start_date: datetime.datetime, action: str) -> None:
now = datetime.datetime.utcnow()

View File

@ -1,20 +1,21 @@
from apiflask import APIBlueprint
from flask import abort, current_app
from datetime import datetime
from ..schemas import MessageSchema, CommitteeCreateSchema, TemporaryProjectSupervisorSchema
from ...examination_schedule.models import Committee
from apiflask import APIBlueprint
from flask import abort
from ..schemas import MessageSchema, TimeAvailabilityCreateSchema, TemporaryProjectSupervisorSchema, \
ListOfFreeTimesSchema
from ...dependencies import db
from ..models import ProjectSupervisor
from ..query import get_enrollment_by_enrollment_and_examination_schedule_ids, \
check_the_project_supervisor_is_in_committee, check_the_enrollments_has_just_started
from ...examination_schedule.models import ExaminationSchedule, TemporaryAvailability, TermOfDefence
bp = APIBlueprint("enrollments", __name__, url_prefix="/")
@bp.post('/<int:examination_schedule_id>/enrollments/<int:enrollment_id>/')
@bp.input(CommitteeCreateSchema)
@bp.post('/<int:examination_schedule_id>/enrollments/')
@bp.input(TimeAvailabilityCreateSchema)
@bp.output(MessageSchema)
def assign_yourself_to_committee(examination_schedule_id: int, enrollment_id: int, data: dict) -> dict:
def set_your_free_time_to_examination_schedule(examination_schedule_id: int, data: dict) -> dict:
# this code will be removed
project_supervisor = db.session.query(ProjectSupervisor).filter(
ProjectSupervisor.id == data['project_supervisor_id']).first()
@ -22,43 +23,100 @@ def assign_yourself_to_committee(examination_schedule_id: int, enrollment_id: in
abort(404, "ProjectSupervisor doesn't exist!")
################
limit_of_committtee = current_app.config['LIMIT_PERSONS_PER_COMMITTEE']
es = ExaminationSchedule.query.filter(ExaminationSchedule.id == examination_schedule_id).first()
if es is None:
abort(404, "Examination schedule doesn't exist!")
enrollment = get_enrollment_by_enrollment_and_examination_schedule_ids(examination_schedule_id, enrollment_id)
check_the_enrollments_has_just_started(enrollment.examination_schedule.start_date, "assign")
sd = data['start_date']
ed = data['end_date']
if sd > ed:
abort(400, "Invalid data! End date must be greater than start date!")
size_of_committee = db.session.query(Committee).join(Committee.members). \
filter(Committee.enrollment_id == enrollment.id).count()
if not (es.start_date >= sd and es.end_date <= ed):
abort(400, "Invalid date ranges!")
if size_of_committee >= limit_of_committtee:
abort(400, "The committee is full!")
now = datetime.utcnow()
if es.start_date_for_enrollment_students is not None and \
es.start_date_for_enrollment_students.timestamp() < now.timestamp():
abort(403, "Enrollment has started! You cannot set your free time!")
if check_the_project_supervisor_is_in_committee(enrollment.id, project_supervisor.id):
abort(400, "You have already in this committee!")
ta_query = TemporaryAvailability.query.filter(
TemporaryAvailability.examination_schedule_id == examination_schedule_id). \
filter(TemporaryAvailability.project_supervisor_id == project_supervisor.id)
if ta_query.first() is not None:
# implement logic
pass
enrollment.committee.members.append(project_supervisor)
db.session.add(enrollment)
ta = TemporaryAvailability(**data, examination_schedule_id=examination_schedule_id,
project_supervisor_id=project_supervisor.id)
db.session.add(ta)
db.session.commit()
return {"message": "You have just assigned yourself to committee!"}
return {"message": "You have just assigned your free time!"}
@bp.delete('/<int:examination_schedule_id>/enrollments/<int:enrollment_id>/')
@bp.delete('/<int:examination_schedule_id>/enrollments/<int:id>/')
@bp.input(TemporaryProjectSupervisorSchema)
@bp.output(MessageSchema)
def delete_yourself_from_committee(examination_schedule_id: int, enrollment_id: int, data: dict) -> dict:
def delete_your_free_time_from_examination_schedule(examination_schedule_id: int, 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:
abort(404, "ProjectSupervisor doesn't exist!")
################
ta = TemporaryAvailability.query.filter(TemporaryAvailability.examination_schedule_id == examination_schedule_id,
TemporaryAvailability.project_supervisor_id == project_supervisor.id,
TemporaryAvailability.id == id).first()
enrollment = get_enrollment_by_enrollment_and_examination_schedule_ids(examination_schedule_id, enrollment_id)
check_the_enrollments_has_just_started(enrollment.examination_schedule.start_date, "delete")
if ta is None:
abort(404, "Your free time doesn't exist!")
if not check_the_project_supervisor_is_in_committee(enrollment.id, project_supervisor.id):
abort(400, "You are not assigned to this committee!")
enrollment.committee.members.remove(project_supervisor)
db.session.delete(ta)
db.session.commit()
return {"message": "You have just removed from committee!"}
return {"message": "You have just removed your free time!"}
@bp.get('/<int:examination_schedule_id>/')
@bp.input(TemporaryProjectSupervisorSchema, location='query')
@bp.output(ListOfFreeTimesSchema)
def list_enrollments_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:
abort(404, "ProjectSupervisor doesn't exist!")
################
es = ExaminationSchedule.query.filter(ExaminationSchedule.id == examination_schedule_id,
ExaminationSchedule.year_group_id).first()
if es is None:
abort(404, "Examination schedule doesn't exist!")
now = datetime.utcnow()
if es.start_date_for_enrollment_students.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
@bp.get('/<int:examination_schedule_id>/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:
# this code will be removed
project_supervisor = db.session.query(ProjectSupervisor).filter(ProjectSupervisor.id == data['id']).first()
if project_supervisor is None:
abort(404, "ProjectSupervisor doesn't exist!")
################
es = ExaminationSchedule.query.filter(ExaminationSchedule.id == examination_schedule_id,
ExaminationSchedule.year_group_id).first()
if es is None:
abort(404, "Examination schedule doesn't exist!")
# 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

View File

@ -1,13 +1,23 @@
from marshmallow import fields, validate, Schema
# MessageSchema, CommitteeCreateSchema
class MessageSchema(Schema):
message = fields.Str()
class CommitteeCreateSchema(Schema):
class FreeTimeSchema(Schema):
id = fields.Integer()
start_date = fields.DateTime(required=True)
end_date = fields.DateTime(required=True)
class ListOfFreeTimesSchema(Schema):
free_times = fields.List(fields.Nested(FreeTimeSchema))
class TimeAvailabilityCreateSchema(Schema):
start_date = fields.DateTime(required=True)
end_date = fields.DateTime(required=True)
project_supervisor_id = fields.Integer(required=True) # temporary field it will be removed in the future

View File

@ -1,9 +1,27 @@
from datetime import datetime
from flask_sqlalchemy import BaseQuery
from ..dependencies import db
from ..base.models import Person, Base
from ..base.utils import order_by_column_name
from ..examination_schedule.models import Enrollment
from ..examination_schedule.models import TermOfDefence
class YearGroupStudents(Base):
__tablename__ = 'year_group_students'
year_group_id = db.Column(db.Integer, db.ForeignKey('year_groups.id', ondelete='CASCADE'))
student_index = db.Column(db.Integer, db.ForeignKey('students.index', ondelete='CASCADE'))
class YearGroup(Base):
__tablename__ = 'year_groups'
name = db.Column(db.String(50), unique=True, nullable=False)
mode = db.Column(db.String(1), unique=True, nullable=False)
created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
students = db.relationship("YearGroupStudents")
class Group(Base):
@ -15,13 +33,14 @@ class Group(Base):
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)
year_group_id = db.Column(db.Integer, db.ForeignKey('year_groups.id'))
year_group = db.relationship('YearGroup', backref='groups', lazy=True)
points_for_first_term = db.Column(db.Integer, default=0, nullable=False)
points_for_second_term = db.Column(db.Integer, default=0, nullable=False)
# enrollment = db.relationship('Enrollment', uselist=False, backref='group')
@classmethod
def search_by_name(cls, search_name: str = None) -> BaseQuery:
group_query = cls.query
def search_by_name(cls, year_group_id: int, search_name: str = None) -> BaseQuery:
group_query = cls.query.filter(Group.year_group_id == year_group_id)
if search_name is not None:
group_query = group_query.filter(Group.name.like(f'{search_name}%'))
@ -35,18 +54,14 @@ class Student(Person):
pesel = db.Column(db.String(11), default='')
index = db.Column(db.Integer, primary_key=True)
group_id = db.Column(db.Integer, db.ForeignKey('groups.id'))
group = db.relationship('Group', backref='students', lazy=True)
mode = db.Column(db.Boolean, default=True, nullable=False) # True - stationary, False - non-stationary
groups = db.relationship('Group', backref='students', lazy=True)
year_groups = db.relationship("YearGroupStudents")
@classmethod
def search_by_fullname_and_mode_and_order_by_first_name_or_last_name(cls, fullname: str = None,
mode: bool = None,
def search_by_fullname_and_mode_and_order_by_first_name_or_last_name(cls, year_group_id: int, fullname: str = None,
order_by_first_name: str = None,
order_by_last_name: str = None) -> BaseQuery:
student_query = cls.query
if mode is not None:
student_query = student_query.filter_by(mode=mode)
student_query = cls.query.filter(YearGroup.id == year_group_id)
if fullname is not None:
student_query = student_query.filter((Student.first_name + ' ' + Student.last_name).like(f'{fullname}%'))

View File

@ -3,14 +3,11 @@ import datetime
from apiflask import APIBlueprint
from flask import abort
from ..schemas import MessageSchema, TemporaryStudentSchema
from ..schemas import MessageSchema, TemporaryStudentSchema, ExaminationScheduleListSchema
from ...dependencies import db
from ...examination_schedule.models import Enrollment
from ..models import Student, Group
from ..models import Student, Group, TermOfDefence
from ...examination_schedule.models import ExaminationSchedule
from ...project_supervisor.models import ProjectSupervisor
from ...project_supervisor.query import get_enrollment_by_enrollment_and_examination_schedule_ids, \
check_the_project_supervisor_is_in_committee
from ..query import check_the_enrollments_has_just_started
bp = APIBlueprint("enrollments", __name__, url_prefix="/")
@ -18,52 +15,93 @@ bp = APIBlueprint("enrollments", __name__, url_prefix="/")
@bp.post('/<int:examination_schedule_id>/enrollments/<int:enrollment_id>/')
@bp.input(TemporaryStudentSchema)
@bp.output(MessageSchema)
def assign_group_for_this_exam_date(examination_schedule_id: int, enrollment_id: int, data: dict) -> dict:
def assign_your_group_to_term_of_defence(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:
abort(404, "Student doesn't exist!")
################
st = Student.query.join(Group).join(ProjectSupervisor).filter(Student.index == student.index).first()
enrollment = db.session.query(Enrollment.id).filter(Enrollment.group_id == st.group.id).first()
if enrollment is not None:
abort(400, "Your group has already assigned to any exam date!")
enrollment = get_enrollment_by_enrollment_and_examination_schedule_ids(examination_schedule_id, enrollment_id)
check_the_enrollments_has_just_started(enrollment.examination_schedule)
if st is None or st.group.project_supervisor is None:
abort(400, "You don't have a group or your group doesn't have an assigned project supervisor!")
if not check_the_project_supervisor_is_in_committee(enrollment_id, st.group.project_supervisor.id):
defence = TermOfDefence.query.filter(TermOfDefence.group_id == st.group.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()
td = TermOfDefence.query.join(TermOfDefence.members_of_committee). \
filter_by(project_supervisor_id=g.project_supervisor_id).first()
if td is None:
abort(400, "Your project supervisor is not in committee!")
enrollment.group_id = st.group.id
db.session.add(enrollment)
defence.group_id = st.group.id
db.session.add(defence)
db.session.commit()
return {"message": "You have just assigned the group for this exam date!"}
@bp.delete('/<int:examination_schedule_id>/enrollments/<int:enrollment_id>/')
@bp.delete('/<int:examination_schedule_id>/enrollments/<int:term_of_defence_id>/')
@bp.input(TemporaryStudentSchema)
@bp.output(MessageSchema)
def delete_group_for_this_exam_date(examination_schedule_id: int, enrollment_id: int, data: dict) -> dict:
def delete_your_group_from_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!")
################
enrollment = get_enrollment_by_enrollment_and_examination_schedule_ids(examination_schedule_id, enrollment_id)
term_of_defence = TermOfDefence.query.filter(TermOfDefence.id == term_of_defence_id). \
filter(TermOfDefence.examination_schedule_id == examination_schedule_id).first()
if student.group.id != enrollment.group_id:
abort(400, "You are not assigned to this committee!")
if term_of_defence is None:
abort(404, "Term of defence doesn't exist!")
check_the_enrollments_has_just_started(enrollment.examination_schedule)
if student.group.id != term_of_defence.group_id:
abort(400, "You are not assigned to this group!")
enrollment.group = None
db.session.add(enrollment)
term_of_defence.group_id = None
db.session.add(term_of_defence)
db.session.commit()
return {"message": "You have just removed the group for this exam date!"}
@bp.get('/examination-schedule/year-group/<int:year_group_id>/')
@bp.input(TemporaryStudentSchema)
@bp.output(ExaminationScheduleListSchema)
def list_examination_schedule_for_students(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:
abort(404, "Student doesn't exist!")
################
# in the future filter after the mode of examination schedule if we will have authorization module
now = datetime.datetime.utcnow()
examination_schedules = ExaminationSchedule.query. \
filter(ExaminationSchedule.year_group_id == year_group_id). \
filter(ExaminationSchedule.start_date_for_enrollment_students < now). \
filter(ExaminationSchedule.end_date_for_enrollment_students > now).all()
return {'examination_schedules': examination_schedules}
@bp.get('/enrollments/')
@bp.input(TemporaryStudentSchema)
@bp.output(ExaminationScheduleListSchema)
def list_term_of_defences_for_students(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!")
################
# 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). \
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}

View File

@ -1,6 +1,6 @@
from apiflask import APIBlueprint
from ...project_supervisor.models import ProjectSupervisor
from ...project_supervisor.models import ProjectSupervisor, YearGroupProjectSupervisors
from ..models import Group
from ...dependencies import db
from ..schemas import ProjectSupervisorQuerySchema, ProjectSupervisorPaginationSchema
@ -9,21 +9,20 @@ from ...base.utils import paginate_models
bp = APIBlueprint("registrations", __name__, url_prefix="/registrations")
@bp.get('/')
@bp.get('/<int:year_group_id>/')
@bp.input(ProjectSupervisorQuerySchema, location='query')
@bp.output(ProjectSupervisorPaginationSchema)
def list_available_groups(query: dict) -> dict:
mode = 0 if query.get('mode') else 1
def list_available_groups(year_group_id: int, query: dict) -> dict:
page = query.get('page')
per_page = query.get('per_page')
available_groups = (ProjectSupervisor.limit_group - ProjectSupervisor.count_groups)
ps_query = db.session.query(ProjectSupervisor, available_groups).join(Group, isouter=True)
if mode is not None:
ps_query = ps_query.filter(ProjectSupervisor.mode != 1-mode)
ps_query = ps_query.group_by(ProjectSupervisor.id)
available_groups = (YearGroupProjectSupervisors.limit_group - db.func.count(Group.id))
ps_query = db.session. \
query(ProjectSupervisor, available_groups). \
join(Group, isouter=True). \
join(YearGroupProjectSupervisors, isouter=True). \
filter(YearGroupProjectSupervisors.year_group_id == year_group_id).\
group_by(ProjectSupervisor.id)
data = paginate_models(page, ps_query, per_page)

View File

@ -5,7 +5,6 @@ class ProjectSupervisorSchema(Schema):
first_name = fields.Str()
last_name = fields.Str()
email = fields.Str()
mode = fields.Integer()
available_groups = fields.Integer()
@ -17,7 +16,6 @@ class ProjectSupervisorPaginationSchema(Schema):
class ProjectSupervisorQuerySchema(Schema):
page = fields.Integer()
per_page = fields.Integer()
mode = fields.Boolean()
class TemporaryStudentSchema(Schema):
@ -26,3 +24,14 @@ class TemporaryStudentSchema(Schema):
class MessageSchema(Schema):
message = fields.Str()
class ExaminationScheduleSchema(Schema):
id = fields.Integer()
title = fields.Str()
start_date = fields.DateTime()
end_date = fields.DateTime()
class ExaminationScheduleListSchema(Schema):
examination_schedules = fields.List(fields.Nested(ExaminationScheduleSchema))

View File

@ -0,0 +1,28 @@
"""empty message
Revision ID: 5c3c4c4e1a72
Revises: a96f91e5b556
Create Date: 2022-11-12 11:48:38.377516
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '5c3c4c4e1a72'
down_revision = 'a96f91e5b556'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('examination_schedules', sa.Column('duration_time', sa.Integer(), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('examination_schedules', 'duration_time')
# ### end Alembic commands ###

View File

@ -0,0 +1,141 @@
"""empty message
Revision ID: a96f91e5b556
Revises:
Create Date: 2022-11-12 11:34:01.223667
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'a96f91e5b556'
down_revision = None
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('project_supervisors',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('first_name', sa.String(length=255), nullable=False),
sa.Column('last_name', sa.String(length=255), nullable=False),
sa.Column('email', sa.String(length=120), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('email')
)
op.create_index(op.f('ix_project_supervisors_first_name'), 'project_supervisors', ['first_name'], unique=False)
op.create_index(op.f('ix_project_supervisors_last_name'), 'project_supervisors', ['last_name'], unique=False)
op.create_table('year_groups',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('name', sa.String(length=50), nullable=False),
sa.Column('mode', sa.String(length=1), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('mode'),
sa.UniqueConstraint('name')
)
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('year_group_id', sa.Integer(), nullable=False),
sa.Column('start_date_for_enrollment_students', sa.DateTime(), nullable=True),
sa.Column('end_date_for_enrollment_students', sa.DateTime(), nullable=True),
sa.Column('start_date', sa.DateTime(), nullable=False),
sa.Column('end_date', sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(['year_group_id'], ['year_groups.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('title')
)
op.create_table('groups',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('name', sa.String(length=60), nullable=False),
sa.Column('cdyd_kod', sa.String(length=60), nullable=True),
sa.Column('prz_kod', sa.String(length=60), nullable=True),
sa.Column('tzaj_kod', sa.String(length=60), nullable=True),
sa.Column('project_supervisor_id', sa.Integer(), nullable=True),
sa.Column('year_group_id', sa.Integer(), nullable=True),
sa.Column('points_for_first_term', sa.Integer(), nullable=False),
sa.Column('points_for_second_term', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(['project_supervisor_id'], ['project_supervisors.id'], ),
sa.ForeignKeyConstraint(['year_group_id'], ['year_groups.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('year_group_project_supervisors',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('project_supervisor_id', sa.Integer(), nullable=False),
sa.Column('year_group_id', sa.Integer(), nullable=False),
sa.Column('limit_group', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(['project_supervisor_id'], ['project_supervisors.id'], ),
sa.ForeignKeyConstraint(['year_group_id'], ['year_groups.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('students',
sa.Column('first_name', sa.String(length=255), nullable=False),
sa.Column('last_name', sa.String(length=255), nullable=False),
sa.Column('email', sa.String(length=120), nullable=True),
sa.Column('pesel', sa.String(length=11), nullable=True),
sa.Column('index', sa.Integer(), nullable=False),
sa.Column('group_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['group_id'], ['groups.id'], ),
sa.PrimaryKeyConstraint('index'),
sa.UniqueConstraint('email')
)
op.create_index(op.f('ix_students_first_name'), 'students', ['first_name'], unique=False)
op.create_index(op.f('ix_students_last_name'), 'students', ['last_name'], unique=False)
op.create_table('temporary_availabilities',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('start_date', sa.DateTime(), nullable=False),
sa.Column('end_date', sa.DateTime(), nullable=False),
sa.Column('examination_schedule_id', sa.Integer(), nullable=False),
sa.Column('project_supervisor_id', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(['examination_schedule_id'], ['examination_schedules.id'], ),
sa.ForeignKeyConstraint(['project_supervisor_id'], ['project_supervisors.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('term_of_defences',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('start_date', sa.DateTime(), nullable=False),
sa.Column('end_date', sa.DateTime(), nullable=False),
sa.Column('examination_schedule_id', sa.Integer(), nullable=True),
sa.Column('group_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['examination_schedule_id'], ['examination_schedules.id'], ),
sa.ForeignKeyConstraint(['group_id'], ['groups.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('committees',
sa.Column('term_of_defence_id', sa.Integer(), nullable=True),
sa.Column('project_supervisor_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['project_supervisor_id'], ['project_supervisors.id'], ),
sa.ForeignKeyConstraint(['term_of_defence_id'], ['term_of_defences.id'], )
)
op.create_table('year_group_students',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('year_group_id', sa.Integer(), nullable=True),
sa.Column('student_index', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['student_index'], ['students.index'], ondelete='CASCADE'),
sa.ForeignKeyConstraint(['year_group_id'], ['year_groups.id'], ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id')
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('year_group_students')
op.drop_table('committees')
op.drop_table('term_of_defences')
op.drop_table('temporary_availabilities')
op.drop_index(op.f('ix_students_last_name'), table_name='students')
op.drop_index(op.f('ix_students_first_name'), table_name='students')
op.drop_table('students')
op.drop_table('year_group_project_supervisors')
op.drop_table('groups')
op.drop_table('examination_schedules')
op.drop_table('year_groups')
op.drop_index(op.f('ix_project_supervisors_last_name'), table_name='project_supervisors')
op.drop_index(op.f('ix_project_supervisors_first_name'), table_name='project_supervisors')
op.drop_table('project_supervisors')
# ### end Alembic commands ###