fix group, students, project_supervisor endpoints for coordinator, fix importing csv students endpoint and add copy project supervisors from last year group, refactor code
This commit is contained in:
parent
48be4c6345
commit
038f9da93d
@ -7,11 +7,10 @@ from flask_cors import CORS
|
||||
from .config import config
|
||||
from .dependencies import db, ma
|
||||
from .commands.startapp import startapp
|
||||
from .commands.init_db import init_db
|
||||
from .commands.clear_db import clear_db
|
||||
from .utils import import_models
|
||||
from .api import api_bp
|
||||
from .errors import request_entity_too_large, register_error_handlers
|
||||
from .errors import register_error_handlers
|
||||
|
||||
|
||||
def create_app(config_name: str = '') -> APIFlask:
|
||||
@ -37,11 +36,9 @@ def create_app(config_name: str = '') -> APIFlask:
|
||||
|
||||
# register commands
|
||||
app.cli.add_command(startapp)
|
||||
app.cli.add_command(init_db)
|
||||
app.cli.add_command(clear_db)
|
||||
|
||||
# register errors
|
||||
register_error_handlers(app)
|
||||
# app.register_error_handler(413, request_entity_too_large)
|
||||
|
||||
return app
|
||||
|
@ -12,4 +12,4 @@ class Person(db.Model):
|
||||
|
||||
first_name = db.Column(db.String(255), index=True, nullable=False)
|
||||
last_name = db.Column(db.String(255), index=True, nullable=False)
|
||||
email = db.Column(db.String(120), unique=True)
|
||||
email = db.Column(db.String(120), index=True, nullable=False)
|
||||
|
5
backend/app/base/schemas.py
Normal file
5
backend/app/base/schemas.py
Normal file
@ -0,0 +1,5 @@
|
||||
from marshmallow import fields, Schema
|
||||
|
||||
|
||||
class MessageSchema(Schema):
|
||||
message = fields.Str()
|
@ -1,37 +0,0 @@
|
||||
from flask.cli import with_appcontext
|
||||
from click import command
|
||||
|
||||
from ..dependencies import db
|
||||
# from ..factory import ProjectSupervisorFactory, GroupFactory, StudentFactory
|
||||
|
||||
|
||||
@command('init_db')
|
||||
@with_appcontext
|
||||
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
|
@ -1,5 +0,0 @@
|
||||
# from ..base.models import Base
|
||||
#
|
||||
#
|
||||
# class Coordinator(Base):
|
||||
# __tablename__ = 'coordinators'
|
@ -5,12 +5,13 @@ 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, AssignedGroupToTermOfDefenceListSchema, GroupIdSchema
|
||||
from ...examination_schedule.models import TermOfDefence, TemporaryAvailability, committee
|
||||
from ..schemas import TermOfDefenceSchema, TemporaryAvailabilityListSchema, AssignedGroupToTermOfDefenceListSchema, \
|
||||
GroupIdSchema
|
||||
from ...examination_schedule.models import TermOfDefence, TemporaryAvailability
|
||||
from ...students.models import YearGroup
|
||||
from ...project_supervisor.models import ProjectSupervisor
|
||||
from ...dependencies import db
|
||||
from ...base.schemas import MessageSchema
|
||||
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
|
||||
@ -18,68 +19,6 @@ from ..query.enrollments import get_term_of_defence_by_id_and_examination_schedu
|
||||
bp = APIBlueprint("enrollments", __name__, url_prefix="/enrollments")
|
||||
|
||||
|
||||
@bp.post('/<int:examination_schedule_id>/generate')
|
||||
@bp.output(MessageSchema)
|
||||
def generate_term_of_defence_for_this_examination_schedule(examination_schedule_id: int) -> dict:
|
||||
ex = get_examination_schedule_by_id(examination_schedule_id)
|
||||
limit = current_app.config.get('LIMIT_MEMBERS_PER_COMMITTEE', 3)
|
||||
|
||||
term_of_defences = TermOfDefence.query. \
|
||||
filter(TermOfDefence.examination_schedule_id == examination_schedule_id).first()
|
||||
if term_of_defences is not None:
|
||||
abort(400,
|
||||
"First you have to delete all term of defences for this examination schedule to generate them again!")
|
||||
|
||||
temporary_availabilities = TemporaryAvailability.query. \
|
||||
filter(TemporaryAvailability.examination_schedule_id == examination_schedule_id). \
|
||||
join(TemporaryAvailability.project_supervisor). \
|
||||
all()
|
||||
if len(temporary_availabilities) == 0:
|
||||
abort(404, "Not found temporary availabilities for project supervisors")
|
||||
|
||||
dates = generate_range_dates(ex.start_date, ex.end_date, ex.duration_time)
|
||||
|
||||
term_of_defences = []
|
||||
for d in dates:
|
||||
e = d + datetime.timedelta(minutes=ex.duration_time)
|
||||
t = list(filter(lambda ta: ta.start_date <= d and ta.end_date >= e, temporary_availabilities))
|
||||
if len(t) >= limit:
|
||||
projects_supervisors = [t[i].project_supervisor for i in range(limit)]
|
||||
term_of_defence = TermOfDefence(start_date=d, end_date=e, examination_schedule_id=examination_schedule_id)
|
||||
term_of_defence.members_of_committee = projects_supervisors
|
||||
term_of_defences.append(term_of_defence)
|
||||
db.session.add_all(term_of_defences)
|
||||
db.session.commit()
|
||||
return {"message": "Term of defences was generated!"}
|
||||
|
||||
|
||||
@bp.post('/<int:examination_schedule_id>/clear-term-of-defences/')
|
||||
@bp.output(MessageSchema)
|
||||
def clear_generated_term_of_defences(examination_schedule_id: int) -> dict:
|
||||
get_examination_schedule_by_id(examination_schedule_id)
|
||||
|
||||
# count_defences = db.func.count(ProjectSupervisor.id)
|
||||
# td = db.session.query(TermOfDefence, count_defences). \
|
||||
# join(ProjectSupervisor, isouter=True). \
|
||||
# filter(TermOfDefence.examination_schedule_id == examination_schedule_id). \
|
||||
# group_by(TermOfDefence.id).having(count_defences > 0).first()
|
||||
# print(td)
|
||||
# if td is not None:
|
||||
# abort(400, "First you remove group assigned to term of defence to clear term of defences!")
|
||||
|
||||
td = TermOfDefence.query. \
|
||||
filter(TermOfDefence.examination_schedule_id == examination_schedule_id).all()
|
||||
while True:
|
||||
for t in td[0:10]:
|
||||
db.session.delete(t)
|
||||
db.session.commit()
|
||||
if len(td) == 0:
|
||||
break
|
||||
td[0:10] = []
|
||||
# print(len(get_debug_queries()))
|
||||
return {"message": "Term of defences was deleted!"}
|
||||
|
||||
|
||||
@bp.post('/<int:examination_schedule_id>/add')
|
||||
@bp.input(TermOfDefenceSchema)
|
||||
@bp.output(MessageSchema)
|
||||
|
@ -9,9 +9,10 @@ from ...dependencies import db
|
||||
from ...examination_schedule.models import ExaminationSchedule, TermOfDefence
|
||||
from ...students.models import Group, YearGroup
|
||||
from ...project_supervisor.models import ProjectSupervisor
|
||||
from ..schemas import ExaminationScheduleSchema, ExaminationScheduleUpdateSchema, MessageSchema, \
|
||||
from ..schemas import ExaminationScheduleSchema, ExaminationScheduleUpdateSchema, \
|
||||
ExaminationSchedulesQuerySchema, ExaminationSchedulesPaginationSchema
|
||||
from ..utils import generate_examination_schedule_pdf_file
|
||||
from ...base.schemas import MessageSchema
|
||||
|
||||
bp = APIBlueprint("examination_schedule", __name__, url_prefix="/examination_schedule")
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
from flask import abort, current_app
|
||||
from flask import abort
|
||||
from apiflask import APIBlueprint
|
||||
from flask_sqlalchemy import get_debug_queries
|
||||
|
||||
from ...students.models import Group, Student, YearGroup, ProjectGradeSheet
|
||||
from ...project_supervisor.models import ProjectSupervisor, YearGroupProjectSupervisors
|
||||
from ..schemas import GroupEditSchema, GroupsPaginationSchema, GroupCreateSchema, MessageSchema, GroupQuerySchema, \
|
||||
DetailGroupSchema
|
||||
from ...project_supervisor.models import ProjectSupervisor
|
||||
from ..schemas import GroupEditSchema, GroupsPaginationSchema, GroupCreateSchema, GroupQuerySchema, DetailGroupSchema
|
||||
from ...dependencies import db
|
||||
from ...base.utils import paginate_models
|
||||
from ...base.schemas import MessageSchema
|
||||
from ..utils import attach_points_for_first_and_second_term_to_group_models
|
||||
|
||||
bp = APIBlueprint("groups", __name__, url_prefix="/groups")
|
||||
@ -38,7 +38,7 @@ def list_groups(year_group_id: int, query: dict) -> dict:
|
||||
@bp.output(MessageSchema, status_code=201)
|
||||
def create_group(year_group_id: int, data: dict) -> dict:
|
||||
name = data['name']
|
||||
students_indexes = data['students']
|
||||
students_ids = data['students']
|
||||
project_supervisor_id = data['project_supervisor_id']
|
||||
|
||||
yg = YearGroup.query.filter(YearGroup.id == year_group_id).first()
|
||||
@ -47,33 +47,21 @@ def create_group(year_group_id: int, data: dict) -> dict:
|
||||
|
||||
project_supervisor = ProjectSupervisor.query.filter_by(id=project_supervisor_id).first()
|
||||
|
||||
limit_student_per_group = current_app.config.get('LIMIT_STUDENTS_PER_GROUP')
|
||||
if project_supervisor is None:
|
||||
abort(404, f"Not found project supervisor!")
|
||||
elif limit_student_per_group is not None and limit_student_per_group < len(students_indexes):
|
||||
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(db.func.count(ProjectSupervisor.id)). \
|
||||
join(Group).filter(ProjectSupervisor.id == project_supervisor_id).group_by(ProjectSupervisor.id).scalar()
|
||||
|
||||
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, year_group_id=year_group_id)
|
||||
|
||||
students_without_groups = db.session.query(Student, Group). \
|
||||
join(Group, Student.groups). \
|
||||
filter(Group.year_group_id == year_group_id). \
|
||||
filter(db.or_(*[Student.index == idx for idx in students_indexes])).all()
|
||||
filter(db.or_(*[Student.id == st_id for st_id in students_ids])).all()
|
||||
|
||||
if len(students_without_groups) > 0:
|
||||
abort(400, "One or more students have already belonged to group!")
|
||||
|
||||
students = db.session.query(Student).filter(Student.index.in_(students_indexes)).all()
|
||||
if len(students) != len(students_indexes):
|
||||
students = db.session.query(Student).filter(Student.id.in_(students_ids)).all()
|
||||
if len(students) != len(students_ids):
|
||||
abort(404, "Not found students!")
|
||||
|
||||
db.session.add(group)
|
||||
@ -81,9 +69,6 @@ def create_group(year_group_id: int, data: dict) -> dict:
|
||||
|
||||
for student in students:
|
||||
group.students.append(student)
|
||||
|
||||
db.session.commit()
|
||||
|
||||
pgs = ProjectGradeSheet(group_id=group.id)
|
||||
db.session.add(pgs)
|
||||
db.session.commit()
|
||||
@ -91,19 +76,19 @@ def create_group(year_group_id: int, data: dict) -> dict:
|
||||
return {"message": "Group was created!"}
|
||||
|
||||
|
||||
@bp.get("/<int:id>/detail/")
|
||||
@bp.get("/<int:group_id>/detail/")
|
||||
@bp.output(DetailGroupSchema)
|
||||
def detail_group(id: int) -> Group:
|
||||
group = Group.query.filter_by(id=id).first()
|
||||
def detail_group(group_id: int) -> Group:
|
||||
group = Group.query.filter_by(id=group_id).first()
|
||||
if group is None:
|
||||
abort(404, f"Not found group!")
|
||||
return group
|
||||
|
||||
|
||||
@bp.delete("/<int:id>/")
|
||||
@bp.delete("/<int:group_id>/")
|
||||
@bp.output(MessageSchema, status_code=202)
|
||||
def delete_group(id: int) -> dict:
|
||||
group = Group.query.filter_by(id=id).first()
|
||||
def delete_group(group_id: int) -> dict:
|
||||
group = Group.query.filter_by(id=group_id).first()
|
||||
if group is None:
|
||||
abort(404, f"Not found group!")
|
||||
|
||||
@ -113,26 +98,26 @@ def delete_group(id: int) -> dict:
|
||||
return {"message": "Group was deleted!"}
|
||||
|
||||
|
||||
@bp.put("/<int:id>/")
|
||||
@bp.put("/<int:group_id>/")
|
||||
@bp.input(GroupEditSchema)
|
||||
@bp.output(MessageSchema)
|
||||
def edit_group(id: int, data: dict) -> dict:
|
||||
def edit_group(group_id: int, data: dict) -> dict:
|
||||
if not data:
|
||||
abort(400, 'You have passed empty data!')
|
||||
|
||||
group_query = Group.query.filter_by(id=id)
|
||||
group_query = Group.query.filter_by(id=group_id)
|
||||
group = group_query.first()
|
||||
|
||||
if group is None:
|
||||
abort(404, f"Not found group!")
|
||||
|
||||
students_indexes = data.get('students')
|
||||
students_ids = data.get('students')
|
||||
name = data.get('name')
|
||||
project_supervisor_id = data.get('project_supervisor_id')
|
||||
|
||||
if students_indexes is not None:
|
||||
students = db.session.query(Student).filter(Student.index.in_(students_indexes)).all()
|
||||
if len(students_indexes) != len(students):
|
||||
if students_ids is not None:
|
||||
students = db.session.query(Student).filter(Student.id.in_(students_ids)).all()
|
||||
if len(students_ids) != len(students):
|
||||
abort(404, 'Not found students!')
|
||||
group.students = students
|
||||
|
||||
|
@ -2,41 +2,21 @@ from flask import abort
|
||||
from apiflask import APIBlueprint
|
||||
from flask_sqlalchemy import get_debug_queries
|
||||
|
||||
from ...project_supervisor.models import ProjectSupervisor, YearGroupProjectSupervisors
|
||||
from ...project_supervisor.models import ProjectSupervisor
|
||||
from ...students.models import Group, YearGroup
|
||||
from ..schemas import ProjectSupervisorSchema, ProjectSupervisorEditSchema, ProjectSupervisorsPaginationSchema, \
|
||||
ProjectSupervisorCreateSchema, MessageSchema, ProjectSupervisorQuerySchema, ProjectSupervisorYearGroupSchema
|
||||
ProjectSupervisorCreateSchema, MessageWithIdSchema, ProjectSupervisorQuerySchema
|
||||
from ...base.schemas import MessageSchema
|
||||
from ...dependencies import db
|
||||
from ...base.utils import paginate_models
|
||||
|
||||
bp = APIBlueprint("project_supervisor", __name__, url_prefix="/project_supervisor")
|
||||
|
||||
|
||||
@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')
|
||||
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(
|
||||
None, 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.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:
|
||||
def list_project_supervisors(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')
|
||||
@ -54,44 +34,44 @@ def list_project_supervisors_by_year_group(year_group_id: int, query: dict) -> d
|
||||
}
|
||||
|
||||
|
||||
@bp.post("/")
|
||||
@bp.post("/<int:year_group_id>/")
|
||||
@bp.input(ProjectSupervisorCreateSchema)
|
||||
@bp.output(MessageSchema, status_code=201)
|
||||
def create_project_supervisor(data: dict) -> dict:
|
||||
first_name = data['first_name']
|
||||
last_name = data['last_name']
|
||||
project_supervisor = ProjectSupervisor.query.filter_by(first_name=first_name).filter_by(last_name=last_name).first()
|
||||
@bp.output(MessageWithIdSchema, status_code=201)
|
||||
def create_project_supervisor(year_group_id: int, data: dict) -> dict:
|
||||
year_group = YearGroup.query.filter(YearGroup.id == year_group_id).first()
|
||||
if year_group is None:
|
||||
abort(404, "Not found year group!")
|
||||
|
||||
email = data['email']
|
||||
project_supervisor = ProjectSupervisor.query.filter(ProjectSupervisor.email == email).first()
|
||||
if project_supervisor is not None:
|
||||
abort(400, "Project Supervisor has already exists!")
|
||||
|
||||
project_supervisor = ProjectSupervisor(**data)
|
||||
|
||||
project_supervisor = ProjectSupervisor(**data, year_group_id=year_group_id)
|
||||
db.session.add(project_supervisor)
|
||||
db.session.commit()
|
||||
|
||||
return {"message": "Project Supervisor was created!", "id": project_supervisor.id}
|
||||
|
||||
|
||||
@bp.get("/<int:id>/detail/")
|
||||
@bp.get("/<int:project_supervisor_id>/detail/")
|
||||
@bp.output(ProjectSupervisorSchema)
|
||||
def detail_project_supervisor(id: int) -> ProjectSupervisor:
|
||||
project_supervisor = ProjectSupervisor.query.filter_by(id=id).first()
|
||||
def detail_project_supervisor(project_supervisor_id: int) -> ProjectSupervisor:
|
||||
project_supervisor = ProjectSupervisor.query.filter_by(id=project_supervisor_id).first()
|
||||
if project_supervisor is None:
|
||||
abort(404, 'Not found project supervisor!')
|
||||
return project_supervisor
|
||||
|
||||
|
||||
@bp.delete("/<int:id>/")
|
||||
@bp.delete("/<int:project_supervisor_id>/")
|
||||
@bp.output(MessageSchema)
|
||||
def delete_project_supervisor(id: int) -> dict:
|
||||
project_supervisor = ProjectSupervisor.query.filter_by(id=id).first()
|
||||
def delete_project_supervisor(project_supervisor_id: int) -> dict:
|
||||
project_supervisor = ProjectSupervisor.query.filter_by(id=project_supervisor_id).first()
|
||||
if project_supervisor is None:
|
||||
abort(404, "Not found project supervisor!")
|
||||
|
||||
count_groups = db.session.query(db.func.count(ProjectSupervisor.id)).join(Group). \
|
||||
filter(ProjectSupervisor.id == id).group_by(ProjectSupervisor.id).scalar()
|
||||
|
||||
if count_groups is not None and count_groups > 0:
|
||||
count_groups = len(Group.query.filter(Group.project_supervisor_id == project_supervisor.id).all())
|
||||
if count_groups > 0:
|
||||
abort(400, "Project Supervisor has at least one group!")
|
||||
|
||||
db.session.delete(project_supervisor)
|
||||
@ -99,14 +79,14 @@ def delete_project_supervisor(id: int) -> dict:
|
||||
return {"message": "Project Supervisor was deleted!"}
|
||||
|
||||
|
||||
@bp.put("/<int:id>/")
|
||||
@bp.put("/<int:project_supervisor_id>/")
|
||||
@bp.input(ProjectSupervisorEditSchema)
|
||||
@bp.output(MessageSchema)
|
||||
def edit_project_supervisor(id: int, data: dict) -> dict:
|
||||
def edit_project_supervisor(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_query = ProjectSupervisor.query.filter_by(id=project_supervisor_id)
|
||||
project_supervisor = project_supervisor_query.first()
|
||||
|
||||
if project_supervisor is None:
|
||||
@ -117,65 +97,27 @@ def edit_project_supervisor(id: int, data: dict) -> dict:
|
||||
return {"message": "Project Supervisor was updated!"}
|
||||
|
||||
|
||||
@bp.post("/<int:id>/year-group/<int:year_group_id>/")
|
||||
@bp.input(ProjectSupervisorYearGroupSchema)
|
||||
@bp.post("/copy-project-supervisors-from-last-year-group/<int:year_group_id>/")
|
||||
@bp.output(MessageSchema, status_code=201)
|
||||
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(404, f"Not found project supervisor!")
|
||||
|
||||
def copy_project_supervisors_from_last_year_group(year_group_id: int) -> dict:
|
||||
year_group = YearGroup.query.filter(YearGroup.id == year_group_id).first()
|
||||
if year_group is None:
|
||||
abort(404, "Not found year group!")
|
||||
|
||||
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!")
|
||||
last_year_group = YearGroup.query.filter(YearGroup.mode == year_group.mode). \
|
||||
filter(YearGroup.id != year_group_id).filter(YearGroup.created_at < year_group.created_at). \
|
||||
order_by(db.desc(YearGroup.created_at)).first()
|
||||
if last_year_group is None:
|
||||
abort(400, "The latest year group doesn't exist!")
|
||||
|
||||
ygps = YearGroupProjectSupervisors(year_group_id=year_group_id, project_supervisor_id=id,
|
||||
limit_group=limit_group)
|
||||
db.session.add(ygps)
|
||||
project_supervisors = ProjectSupervisor.query.filter(ProjectSupervisor.year_group_id == last_year_group.id).all()
|
||||
current_project_supervisors_email_in_new_year_group = [ps.email for ps in ProjectSupervisor.query.filter(
|
||||
ProjectSupervisor.year_group_id == year_group_id).all()]
|
||||
for ps in project_supervisors:
|
||||
if ps.email not in current_project_supervisors_email_in_new_year_group:
|
||||
new_ps = ProjectSupervisor(first_name=ps.first_name, last_name=ps.last_name, email=ps.email,
|
||||
limit_group=ps.limit_group, year_group_id=year_group_id)
|
||||
db.session.add(new_ps)
|
||||
db.session.commit()
|
||||
return {"message": "Project Supervisor was added to year group!"}
|
||||
|
||||
|
||||
@bp.delete("/<int:id>/year-group/<int:year_group_id>/")
|
||||
@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).first()
|
||||
if project_supervisor is None:
|
||||
abort(404, f"Not found project supervisor!")
|
||||
|
||||
year_group = YearGroup.query.filter(YearGroup.id == year_group_id).first()
|
||||
if year_group is None:
|
||||
abort(404, "Not found year group!")
|
||||
|
||||
db.session.delete(project_supervisor)
|
||||
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).first()
|
||||
if project_supervisor is None:
|
||||
abort(404, f"Not found project supervisor!")
|
||||
|
||||
year_group = YearGroup.query.filter(YearGroup.id == year_group_id).first()
|
||||
if year_group is None:
|
||||
abort(404, "Not found year group!")
|
||||
|
||||
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!"}
|
||||
return {"message": "Project Supervisors was added!"}
|
||||
|
@ -6,14 +6,15 @@ from apiflask import APIBlueprint
|
||||
from sqlalchemy import or_
|
||||
from flask_sqlalchemy import get_debug_queries
|
||||
|
||||
from ...students.models import Student, Group, YearGroup, YearGroupStudents
|
||||
from ...students.models import Student, Group, YearGroup
|
||||
from ...project_supervisor.models import ProjectSupervisor
|
||||
from ..schemas import StudentSchema, StudentEditSchema, StudentsPaginationSchema, YearGroupInfoQuery, \
|
||||
StudentCreateSchema, MessageSchema, FileSchema, StudentQuerySchema, StudentListFileDownloaderSchema
|
||||
StudentCreateSchema, FileSchema, StudentQuerySchema, StudentListFileDownloaderSchema
|
||||
from ...dependencies import db
|
||||
from ..utils import parse_csv, generate_csv
|
||||
from ..exceptions import InvalidNameOrTypeHeaderException
|
||||
from ...base.utils import paginate_models, is_allowed_extensions
|
||||
from ...base.schemas import MessageSchema
|
||||
|
||||
bp = APIBlueprint("students", __name__, url_prefix="/students")
|
||||
|
||||
@ -40,19 +41,19 @@ def list_students(year_group_id: int, query: dict) -> dict:
|
||||
}
|
||||
|
||||
|
||||
@bp.get("/<int:index>/detail/")
|
||||
@bp.get("/<int:student_id>/detail/")
|
||||
@bp.output(StudentSchema)
|
||||
def detail_student(index: int) -> Student:
|
||||
student = Student.query.filter_by(index=index).first()
|
||||
def detail_student(student_id: int) -> Student:
|
||||
student = Student.query.filter_by(id=student_id).first()
|
||||
if student is None:
|
||||
abort(404, "Not found student!")
|
||||
return student
|
||||
|
||||
|
||||
@bp.delete("/<int:index>/")
|
||||
@bp.delete("/<int:student_id>/")
|
||||
@bp.output(MessageSchema, status_code=202)
|
||||
def delete_student(index: int) -> dict:
|
||||
student = Student.query.filter_by(index=index).first()
|
||||
def delete_student(student_id: int) -> dict:
|
||||
student = Student.query.filter_by(id=student_id).first()
|
||||
if student is None:
|
||||
abort(404, "Not found student!")
|
||||
db.session.delete(student)
|
||||
@ -60,14 +61,14 @@ def delete_student(index: int) -> dict:
|
||||
return {"message": "Student was deleted!"}
|
||||
|
||||
|
||||
@bp.put("/<int:index>/")
|
||||
@bp.put("/<int:student_id>/")
|
||||
@bp.input(StudentEditSchema)
|
||||
@bp.output(MessageSchema)
|
||||
def edit_student(index: int, data: dict) -> dict:
|
||||
def edit_student(student_id: int, data: dict) -> dict:
|
||||
if not data:
|
||||
abort(400, 'You have passed empty data!')
|
||||
|
||||
student_query = Student.query.filter_by(index=index)
|
||||
student_query = Student.query.filter(Student.id==student_id)
|
||||
student = student_query.first()
|
||||
|
||||
if student is None:
|
||||
@ -87,24 +88,18 @@ def create_student(data: dict) -> dict:
|
||||
yg_id = data['year_group_id']
|
||||
del data['year_group_id']
|
||||
|
||||
student = Student.query.filter(Student.index == index).first()
|
||||
# if student is not None:
|
||||
# abort(400, "Student has already exists!")
|
||||
if student is None:
|
||||
dummy_email = f'student{randint(1, 300_000)}@gmail.com'
|
||||
student = Student(**data, email=dummy_email)
|
||||
db.session.add(student)
|
||||
student = Student.query.filter(Student.index == index, Student.year_group_id == yg_id).first()
|
||||
if student is not None:
|
||||
abort(400, "Student has already assigned to this year group!")
|
||||
|
||||
# add student to the chosen year group
|
||||
year_group = YearGroup.query.filter(YearGroup.id == yg_id).first()
|
||||
if year_group is None:
|
||||
abort(404, "Not found year group!")
|
||||
if any((year_group.id == yg.id for yg in student.year_groups)):
|
||||
abort(400, "You are assigned to this year group!")
|
||||
|
||||
ygs = YearGroupStudents(student_index=student.index, year_group_id=year_group.id)
|
||||
db.session.add(ygs)
|
||||
|
||||
dummy_email = f'student{randint(1, 300_000)}@gmail.com'
|
||||
student = Student(**data, email=dummy_email, year_group_id=yg_id)
|
||||
db.session.add(student)
|
||||
db.session.commit()
|
||||
|
||||
return {"message": "Student was created!"}
|
||||
@ -117,7 +112,7 @@ def create_student(data: dict) -> dict:
|
||||
def upload_students(query: dict, file: dict) -> dict:
|
||||
"""Add only Students to chosen year group if students exist in db and assigned to correct year group,
|
||||
they will be omitted"""
|
||||
year_group_id = query.get('id')
|
||||
year_group_id = query.get('year_group_id')
|
||||
yg = YearGroup.query.filter(YearGroup.id == year_group_id).first()
|
||||
if yg is None:
|
||||
abort(404, "Not found year group!")
|
||||
@ -125,7 +120,7 @@ def upload_students(query: dict, file: dict) -> dict:
|
||||
uploaded_file = file.get('file')
|
||||
if uploaded_file and is_allowed_extensions(uploaded_file.filename):
|
||||
try:
|
||||
students = parse_csv(uploaded_file)
|
||||
students = parse_csv(uploaded_file, year_group_id)
|
||||
while True:
|
||||
sliced_students = islice(students, 5)
|
||||
list_of_students = list(sliced_students)
|
||||
@ -133,25 +128,13 @@ def upload_students(query: dict, file: dict) -> dict:
|
||||
if len(list_of_students) == 0:
|
||||
break
|
||||
|
||||
students_in_db = Student.query.filter(or_(Student.index == s.index for s in list_of_students)).all()
|
||||
students_in_db_and_assigned_to_year_group = Student.query.join(YearGroupStudents, isouter=True). \
|
||||
filter(YearGroupStudents.year_group_id == year_group_id). \
|
||||
filter(or_(Student.index == s.index for s in list_of_students)).all()
|
||||
|
||||
students_in_db = Student.query.filter(or_(Student.index == s.index for s in list_of_students)).\
|
||||
filter(Student.year_group_id==year_group_id).all()
|
||||
student_index_in_db = [s.index for s in students_in_db]
|
||||
student_index_in_year_group = [s.index for s in students_in_db_and_assigned_to_year_group]
|
||||
students_in_db_and_not_assigned_to_year_group = list(
|
||||
filter(lambda s: s.index not in student_index_in_year_group, students_in_db))
|
||||
|
||||
students_not_exists_in_db = list(filter(lambda s: s.index not in student_index_in_db, list_of_students))
|
||||
db.session.add_all(students_not_exists_in_db)
|
||||
db.session.commit()
|
||||
|
||||
ygs = [YearGroupStudents(year_group_id=year_group_id, student_index=student.index) for student in
|
||||
students_in_db_and_not_assigned_to_year_group + students_not_exists_in_db]
|
||||
db.session.add_all(ygs)
|
||||
db.session.commit()
|
||||
|
||||
except InvalidNameOrTypeHeaderException:
|
||||
abort(400, "Invalid format of csv file!")
|
||||
else:
|
||||
@ -169,7 +152,7 @@ def download_students(query: dict) -> Response:
|
||||
join(ProjectSupervisor).all()
|
||||
|
||||
if len(students_and_groups) == 0:
|
||||
abort(404, "Not found students, which are assigned to group!")
|
||||
abort(404, "Not found students!")
|
||||
|
||||
csv_file = generate_csv(students_and_groups)
|
||||
response = Response(csv_file, mimetype='text/csv')
|
||||
|
@ -2,9 +2,10 @@ from flask import abort
|
||||
from apiflask import APIBlueprint
|
||||
|
||||
from ...students.models import YearGroup
|
||||
from ..schemas import YearGroupSchema, MessageSchema, YearGroupPaginationSchema, YearGroupQuerySchema
|
||||
from ..schemas import YearGroupSchema, YearGroupPaginationSchema, YearGroupQuerySchema
|
||||
from ...dependencies import db
|
||||
from ...base.utils import paginate_models
|
||||
from ...base.schemas import MessageSchema
|
||||
|
||||
bp = APIBlueprint("year_group", __name__, url_prefix="/year-group")
|
||||
|
||||
|
@ -4,8 +4,8 @@ from .examination_schedule import ExaminationScheduleSchema, ExaminationSchedule
|
||||
ExaminationSchedulesPaginationSchema, ExaminationSchedulesQuerySchema, WorkloadSchema
|
||||
from .groups import GroupQuerySchema, GroupsPaginationSchema, GroupCreateSchema, GroupEditSchema, GroupIdSchema
|
||||
from .project_supervisor import ProjectSupervisorQuerySchema, ProjectSupervisorsPaginationSchema, \
|
||||
ProjectSupervisorCreateSchema, ProjectSupervisorEditSchema, ProjectSupervisorYearGroupSchema
|
||||
ProjectSupervisorCreateSchema, ProjectSupervisorEditSchema
|
||||
from .students import ProjectSupervisorSchema, GroupSchema, StudentSchema, StudentsPaginationSchema, \
|
||||
StudentListFileDownloaderSchema, StudentCreateSchema, StudentEditSchema, MessageSchema, FileSchema, \
|
||||
StudentListFileDownloaderSchema, StudentCreateSchema, StudentEditSchema, MessageWithIdSchema, FileSchema, \
|
||||
StudentQuerySchema, YearGroupInfoQuery, DetailGroupSchema
|
||||
from .year_group import YearGroupSchema, YearGroupPaginationSchema, YearGroupQuerySchema
|
||||
|
@ -1,7 +1,7 @@
|
||||
from marshmallow import Schema, fields, validate
|
||||
|
||||
from ..validators import validate_index
|
||||
from .students import GroupSchema, StudentSchema
|
||||
from .students import GroupSchema
|
||||
|
||||
|
||||
class GroupQuerySchema(Schema):
|
||||
@ -18,13 +18,13 @@ class GroupsPaginationSchema(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))
|
||||
students = fields.List(fields.Integer(required=True))
|
||||
|
||||
|
||||
class GroupEditSchema(Schema):
|
||||
name = fields.Str(validate=validate.Length(min=1, max=255))
|
||||
project_supervisor_id = fields.Integer()
|
||||
students = fields.List(fields.Integer(validate=validate_index))
|
||||
students = fields.List(fields.Integer())
|
||||
|
||||
|
||||
class GroupIdSchema(Schema):
|
||||
|
@ -20,13 +20,11 @@ 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), validate.Email()], required=True)
|
||||
limit_group = fields.Integer(required=True)
|
||||
|
||||
|
||||
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=255), validate.Email()], required=True)
|
||||
|
||||
|
||||
class ProjectSupervisorYearGroupSchema(Schema):
|
||||
limit_group = fields.Integer(required=True)
|
||||
first_name = fields.Str(validate=validate.Length(min=1, max=255))
|
||||
last_name = fields.Str(validate=validate.Length(min=1, max=255))
|
||||
email = fields.Str(validate=[validate.Length(min=0, max=255), validate.Email()])
|
||||
limit_group = fields.Integer()
|
@ -20,7 +20,7 @@ class GroupSchema(ma.SQLAlchemyAutoSchema):
|
||||
|
||||
|
||||
class StudentSchema(ma.SQLAlchemyAutoSchema):
|
||||
groups = fields.List(fields.Nested(GroupSchema))
|
||||
group = fields.Nested(GroupSchema)
|
||||
|
||||
class Meta:
|
||||
model = Student
|
||||
@ -38,7 +38,6 @@ class StudentListFileDownloaderSchema(ma.Schema):
|
||||
class StudentCreateSchema(ma.Schema):
|
||||
first_name = fields.Str(validate=validate.Length(min=1, max=255), required=True)
|
||||
last_name = fields.Str(validate=validate.Length(min=1, max=255), required=True)
|
||||
pesel = fields.Str(validate=validate.Length(min=0, max=11), required=True)
|
||||
index = fields.Integer(validate=validate_index, required=True)
|
||||
year_group_id = fields.Integer()
|
||||
|
||||
@ -50,7 +49,7 @@ class StudentEditSchema(ma.Schema):
|
||||
index = fields.Integer(validate=validate_index)
|
||||
|
||||
|
||||
class MessageSchema(ma.Schema):
|
||||
class MessageWithIdSchema(ma.Schema):
|
||||
message = fields.Str(required=True)
|
||||
id = fields.Str(required=False)
|
||||
|
||||
@ -68,7 +67,7 @@ class StudentQuerySchema(ma.Schema):
|
||||
|
||||
|
||||
class YearGroupInfoQuery(Schema):
|
||||
id = fields.Integer(required=True)
|
||||
year_group_id = fields.Integer(required=True)
|
||||
|
||||
class DetailGroupSchema(ma.SQLAlchemyAutoSchema):
|
||||
project_supervisor = fields.Nested(ProjectSupervisorSchema)
|
||||
|
@ -24,42 +24,31 @@ from ..examination_schedule.models import TermOfDefence
|
||||
|
||||
def check_columns(df: pd.DataFrame) -> bool:
|
||||
headers = set(df.keys().values)
|
||||
columns = ['NAZWISKO', 'IMIE', 'INDEKS', 'PESEL', 'EMAIL']
|
||||
|
||||
if len(headers - set(columns)) != 0:
|
||||
return False
|
||||
|
||||
flag = True
|
||||
col_types = ['object', 'object', 'int', 'int64', 'object']
|
||||
|
||||
for name, col_type in zip(columns, col_types):
|
||||
if not str(df.dtypes[name]).startswith(col_type):
|
||||
flag = False
|
||||
break
|
||||
|
||||
return flag
|
||||
column_names = ['NAZWISKO', 'IMIE', 'INDEKS', 'EMAIL']
|
||||
column_types = ['object', 'object', 'int', 'object']
|
||||
return all((column_name in headers for column_name in column_names)) and \
|
||||
all((str(df.dtypes[column_name]).startswith(column_type) for column_name, column_type in
|
||||
zip(column_names, column_types)))
|
||||
|
||||
|
||||
def parse_csv(file: Union[FileStorage, TextIO]) -> Generator[Student, Any, None]:
|
||||
def parse_csv(file: Union[FileStorage, TextIO], year_group_id: int) -> Generator[Student, Any, None]:
|
||||
df = pd.read_csv(file)
|
||||
# raise Exception(df.to_string())
|
||||
|
||||
if not check_columns(df):
|
||||
raise InvalidNameOrTypeHeaderException
|
||||
|
||||
students = (Student(last_name=dict(item.items())['NAZWISKO'],
|
||||
first_name=dict(item.items())['IMIE'],
|
||||
index=dict(item.items())['INDEKS'],
|
||||
pesel=str(int(dict(item.items())['PESEL'])) if not pd.isna(
|
||||
dict(item.items())['PESEL']) else None,
|
||||
email=dict(item.items())['EMAIL'])
|
||||
email=dict(item.items())['EMAIL'],
|
||||
year_group_id=year_group_id)
|
||||
for _, item in df.iterrows())
|
||||
|
||||
return students
|
||||
|
||||
|
||||
def generate_csv(students_and_groups: List[Tuple[Student, Group]]) -> str:
|
||||
headers = ['PESEL', 'INDEKS', 'IMIE', 'NAZWISKO', 'EMAIL', 'CDYD_KOD', 'PRZ_KOD', 'TZAJ_KOD', 'GR_NR', 'PRG_KOD']
|
||||
data = [(student.pesel, student.index, student.first_name, student.last_name, student.email,
|
||||
headers = ['INDEKS', 'IMIE', 'NAZWISKO', 'EMAIL', 'CDYD_KOD', 'PRZ_KOD', 'TZAJ_KOD', 'GR_NR', 'PRG_KOD']
|
||||
data = [(student.index, student.first_name, student.last_name, student.email,
|
||||
group.cdyd_kod, group.prz_kod, group.tzaj_kod, group.project_supervisor_id,
|
||||
None) for student, group in students_and_groups]
|
||||
dataframe = defaultdict(list)
|
||||
|
@ -1,12 +1,7 @@
|
||||
import json
|
||||
from typing import Tuple
|
||||
|
||||
from apiflask import APIFlask
|
||||
from werkzeug.exceptions import RequestEntityTooLarge, HTTPException
|
||||
|
||||
|
||||
def request_entity_too_large(error: RequestEntityTooLarge) -> Tuple[dict, int]:
|
||||
return {'error': 'File too large!'}, 413
|
||||
from werkzeug.exceptions import HTTPException
|
||||
|
||||
|
||||
def register_error_handlers(app: APIFlask):
|
||||
|
@ -1,44 +0,0 @@
|
||||
from factory import alchemy, Sequence
|
||||
from factory.faker import Faker
|
||||
from factory.fuzzy import FuzzyInteger, FuzzyChoice
|
||||
|
||||
from .dependencies import db
|
||||
from .students.models import Student, Group
|
||||
from .project_supervisor.models import ProjectSupervisor
|
||||
|
||||
|
||||
class ProjectSupervisorFactory(alchemy.SQLAlchemyModelFactory):
|
||||
class Meta:
|
||||
model = ProjectSupervisor
|
||||
sqlalchemy_session = db.session
|
||||
|
||||
first_name = Faker('first_name')
|
||||
last_name = Faker('last_name')
|
||||
email = Faker('email')
|
||||
limit_group = 4 # FuzzyInteger(3, 5)
|
||||
count_groups = 4
|
||||
mode = 0
|
||||
|
||||
|
||||
class GroupFactory(alchemy.SQLAlchemyModelFactory):
|
||||
class Meta:
|
||||
model = Group
|
||||
sqlalchemy_session = db.session
|
||||
|
||||
name = Sequence(lambda n: f'Group-{n}')
|
||||
points_for_first_term = FuzzyInteger(1, 5)
|
||||
points_for_second_term = FuzzyInteger(1, 5)
|
||||
# project_supervisor = RelatedFactory(ProjectSupervisorFactory, 'project_supervisor')
|
||||
|
||||
|
||||
class StudentFactory(alchemy.SQLAlchemyModelFactory):
|
||||
class Meta:
|
||||
model = Student
|
||||
sqlalchemy_session = db.session
|
||||
|
||||
first_name = Faker('first_name')
|
||||
last_name = Faker('last_name')
|
||||
email = Faker('email')
|
||||
index = Sequence(lambda n: 400_000 + n)
|
||||
# group = RelatedFactory(GroupFactory)
|
||||
mode = FuzzyChoice([True, False])
|
@ -6,18 +6,12 @@ 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"
|
||||
|
||||
year_groups = db.relationship('YearGroupProjectSupervisors', lazy=True)
|
||||
limit_group = db.Column(db.Integer, default=3, nullable=False)
|
||||
year_group_id = db.Column(db.Integer, db.ForeignKey('year_groups.id'))
|
||||
year_group = db.relationship('YearGroup', backref='project_supervisors')
|
||||
|
||||
@classmethod
|
||||
def search_by_fullname_and_mode_and_order_by_first_name_or_last_name(cls, year_group_id: int = None,
|
||||
@ -27,8 +21,8 @@ class ProjectSupervisor(Base, Person):
|
||||
project_supervisors_query = cls.query
|
||||
|
||||
if year_group_id is not None:
|
||||
project_supervisors_query = project_supervisors_query.join(YearGroupProjectSupervisors). \
|
||||
filter(YearGroupProjectSupervisors.year_group_id == year_group_id)
|
||||
project_supervisors_query = project_supervisors_query. \
|
||||
filter(ProjectSupervisor.year_group_id == year_group_id)
|
||||
|
||||
if fullname is not None:
|
||||
project_supervisors_query = project_supervisors_query.filter(
|
||||
|
@ -3,11 +3,12 @@ from datetime import datetime
|
||||
from apiflask import APIBlueprint
|
||||
from flask import abort
|
||||
from sqlalchemy import and_, or_
|
||||
from ..schemas import MessageSchema, TimeAvailabilityCreateSchema, TemporaryProjectSupervisorSchema, \
|
||||
from ..schemas import TimeAvailabilityCreateSchema, TemporaryProjectSupervisorSchema, \
|
||||
ListOfFreeTimesSchema, ListOfTermOfDefenceSchema
|
||||
from ...dependencies import db
|
||||
from ..models import ProjectSupervisor
|
||||
from ...examination_schedule.models import ExaminationSchedule, TemporaryAvailability, TermOfDefence
|
||||
from ...base.schemas import MessageSchema
|
||||
|
||||
bp = APIBlueprint("enrollments", __name__, url_prefix="/")
|
||||
|
||||
|
@ -3,10 +3,11 @@ from flask import abort
|
||||
|
||||
from ...dependencies import db
|
||||
from ..models import ProjectSupervisor
|
||||
from ..schemas import ProjectSupervisorTermQuerySchema, MessageSchema, TemporaryProjectSupervisorSchema
|
||||
from ..schemas import ProjectSupervisorTermQuerySchema, TemporaryProjectSupervisorSchema
|
||||
from ...students.schemas import ProjectGradeSheetDetailFirstTermSchema, ProjectGradeSheetDetailSecondTermSchema, \
|
||||
ProjectGradeSheetEditFirstTermSchema, ProjectGradeSheetEditSecondTermSchema
|
||||
from ...students.models import Group, ProjectGradeSheet
|
||||
from ...base.schemas import MessageSchema
|
||||
|
||||
bp = APIBlueprint("project_grade_sheet_for_project_supervisor", __name__, url_prefix="/project-grade-sheet")
|
||||
|
||||
|
@ -1,10 +1,6 @@
|
||||
from marshmallow import fields, validate, Schema
|
||||
|
||||
|
||||
class MessageSchema(Schema):
|
||||
message = fields.Str()
|
||||
|
||||
|
||||
class FreeTimeSchema(Schema):
|
||||
id = fields.Integer()
|
||||
start_date = fields.DateTime(required=True)
|
||||
@ -29,6 +25,7 @@ class TimeAvailabilityCreateSchema(Schema):
|
||||
class TemporaryProjectSupervisorSchema(Schema):
|
||||
id = fields.Integer(required=True)
|
||||
|
||||
|
||||
class ProjectSupervisorTermQuerySchema(Schema):
|
||||
id = fields.Integer(required=True)
|
||||
term = fields.Integer(required=True, validate=validate.OneOf([1, 2]))
|
||||
|
@ -8,20 +8,12 @@ from ..base.utils import order_by_column_name
|
||||
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), 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", lazy='joined')
|
||||
|
||||
__table__args = (
|
||||
db.UniqueConstraint('name', 'mode', name='uc_name_mode_year_group')
|
||||
@ -30,7 +22,7 @@ class YearGroup(Base):
|
||||
|
||||
students_groups = db.Table('students_groups',
|
||||
db.Column('group_id', db.ForeignKey('groups.id'), nullable=False),
|
||||
db.Column('student_index', db.ForeignKey('students.index'), nullable=False))
|
||||
db.Column('student_id', db.ForeignKey('students.id'), nullable=False))
|
||||
|
||||
|
||||
class Group(Base):
|
||||
@ -41,12 +33,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='joined')
|
||||
project_supervisor = db.relationship('ProjectSupervisor', backref='groups')
|
||||
year_group_id = db.Column(db.Integer, db.ForeignKey('year_groups.id'))
|
||||
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', lazy='joined')
|
||||
students = db.relationship('Student', secondary=students_groups, back_populates='groups')
|
||||
|
||||
@classmethod
|
||||
def search_by_name(cls, year_group_id: int, search_name: str = None) -> BaseQuery:
|
||||
@ -62,7 +54,7 @@ class ProjectGradeSheet(Base):
|
||||
__tablename__ = 'project_grade_sheets'
|
||||
|
||||
group_id = db.Column(db.Integer, db.ForeignKey('groups.id'))
|
||||
group = db.relationship('Group', backref='project_grade_sheet', uselist=False, lazy='joined')
|
||||
group = db.relationship('Group', backref='project_grade_sheet', uselist=False)
|
||||
|
||||
presentation_required_content_1 = db.Column(db.Integer, default=0)
|
||||
presentation_required_content_2 = db.Column(db.Integer, default=0)
|
||||
@ -125,20 +117,19 @@ class ProjectGradeSheet(Base):
|
||||
products_project_technology_2 = db.Column(db.Integer, default=0)
|
||||
|
||||
|
||||
class Student(Person):
|
||||
class Student(Base, Person):
|
||||
__tablename__ = "students"
|
||||
|
||||
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', lazy='joined')
|
||||
year_groups = db.relationship("YearGroupStudents", lazy='joined')
|
||||
index = db.Column(db.Integer, nullable=False)
|
||||
groups = db.relationship('Group', secondary=students_groups, back_populates='students')
|
||||
year_group_id = db.Column(db.Integer, db.ForeignKey('year_groups.id'))
|
||||
year_group = db.relationship('YearGroup', backref='students')
|
||||
|
||||
@classmethod
|
||||
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.join(YearGroupStudents, isouter=True). \
|
||||
filter(YearGroupStudents.year_group_id == year_group_id)
|
||||
student_query = cls.query.filter(Student.year_group_id == year_group_id)
|
||||
|
||||
if fullname is not None:
|
||||
student_query = student_query.filter((Student.first_name + ' ' + Student.last_name).like(f'{fullname}%'))
|
||||
|
@ -3,11 +3,9 @@ from flask import Blueprint
|
||||
from .enrollments import bp as enrollments_bp
|
||||
from .project_grade_sheet import bp as project_grade_sheet_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(enrollments_bp)
|
||||
bp.register_blueprint(project_grade_sheet_bp)
|
||||
bp.register_blueprint(registrations_bp)
|
||||
bp.register_blueprint(year_group_bp)
|
||||
|
@ -3,12 +3,13 @@ import datetime
|
||||
from apiflask import APIBlueprint
|
||||
from flask import abort
|
||||
|
||||
from ..schemas import MessageSchema, TemporaryStudentSchema, ExaminationScheduleListSchema, \
|
||||
from ..schemas import TemporaryStudentSchema, ExaminationScheduleListSchema, \
|
||||
TermOfDefenceStudentListSchema
|
||||
from ...dependencies import db
|
||||
from ..models import Student, Group, TermOfDefence
|
||||
from ...examination_schedule.models import ExaminationSchedule
|
||||
from ...project_supervisor.models import ProjectSupervisor
|
||||
from ...base.schemas import MessageSchema
|
||||
|
||||
bp = APIBlueprint("enrollments", __name__, url_prefix="/")
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
from apiflask import APIBlueprint
|
||||
|
||||
from ...project_supervisor.models import ProjectSupervisor, YearGroupProjectSupervisors
|
||||
from ...project_supervisor.models import ProjectSupervisor
|
||||
from ..models import Group
|
||||
from ...dependencies import db
|
||||
from ..schemas import ProjectSupervisorQuerySchema, ProjectSupervisorPaginationSchema
|
||||
@ -16,12 +16,11 @@ def list_available_groups(year_group_id: int, query: dict) -> dict:
|
||||
page = query.get('page')
|
||||
per_page = query.get('per_page')
|
||||
|
||||
available_groups = (YearGroupProjectSupervisors.limit_group - db.func.count(Group.id))
|
||||
available_groups = (ProjectSupervisor.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).\
|
||||
filter(ProjectSupervisor.year_group_id == year_group_id).\
|
||||
group_by(ProjectSupervisor.id)
|
||||
|
||||
data = paginate_models(page, ps_query, per_page)
|
||||
|
@ -1,32 +0,0 @@
|
||||
from apiflask import APIBlueprint
|
||||
from flask import abort
|
||||
|
||||
from ...students.models import YearGroup, YearGroupStudents
|
||||
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 = db.session.query(YearGroup).join(YearGroupStudents, isouter=True). \
|
||||
filter(YearGroupStudents.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']
|
||||
}
|
@ -24,10 +24,6 @@ class TemporaryStudentSchema(Schema):
|
||||
student_index = fields.Integer(required=True)
|
||||
|
||||
|
||||
class MessageSchema(Schema):
|
||||
message = fields.Str()
|
||||
|
||||
|
||||
class ExaminationScheduleSchema(Schema):
|
||||
id = fields.Integer()
|
||||
title = fields.Str()
|
||||
@ -51,39 +47,26 @@ class TermOfDefenceStudentItemSchema(Schema):
|
||||
end_date = fields.DateTime()
|
||||
members_of_committee = fields.List(fields.Nested(ProjectSupervisorCommitteeSchema))
|
||||
|
||||
|
||||
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(TermOfDefenceStudentItemSchema):
|
||||
group = fields.Nested(GroupDataItemSchema)
|
||||
|
||||
|
||||
class TermOfDefenceStudentListSchema(Schema):
|
||||
term_of_defences = fields.List(fields.Nested(AssignedGroupToTermOfDefenceItemSchema))
|
||||
|
||||
|
||||
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()
|
||||
|
||||
|
||||
class StudentIndexQueryTempSchema(Schema):
|
||||
index = fields.Integer(required=True) # it will be removed
|
||||
term = fields.Integer(required=True, validate=validate.OneOf([1, 2]))
|
||||
|
@ -1,8 +1,8 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: b3003ddd0564
|
||||
Revision ID: 3fd120fc5e12
|
||||
Revises:
|
||||
Create Date: 2023-01-03 18:37:53.103562
|
||||
Create Date: 2023-01-14 00:03:06.327441
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
@ -10,7 +10,7 @@ import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'b3003ddd0564'
|
||||
revision = '3fd120fc5e12'
|
||||
down_revision = None
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
@ -18,27 +18,6 @@ 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('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.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('year_groups',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('name', sa.String(length=50), nullable=False),
|
||||
@ -59,6 +38,32 @@ def upgrade():
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('title')
|
||||
)
|
||||
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=False),
|
||||
sa.Column('limit_group', sa.Integer(), nullable=False),
|
||||
sa.Column('year_group_id', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['year_group_id'], ['year_groups.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_project_supervisors_email'), 'project_supervisors', ['email'], unique=False)
|
||||
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('students',
|
||||
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=False),
|
||||
sa.Column('index', sa.Integer(), nullable=False),
|
||||
sa.Column('year_group_id', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['year_group_id'], ['year_groups.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_students_email'), 'students', ['email'], unique=False)
|
||||
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('groups',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('name', sa.String(length=60), nullable=False),
|
||||
@ -73,21 +78,14 @@ def upgrade():
|
||||
sa.ForeignKeyConstraint(['year_group_id'], ['year_groups.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('year_group_project_supervisors',
|
||||
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.Column('year_group_id', sa.Integer(), nullable=False),
|
||||
sa.Column('limit_group', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['examination_schedule_id'], ['examination_schedules.id'], ),
|
||||
sa.ForeignKeyConstraint(['project_supervisor_id'], ['project_supervisors.id'], ),
|
||||
sa.ForeignKeyConstraint(['year_group_id'], ['year_groups.id'], ),
|
||||
sa.PrimaryKeyConstraint('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')
|
||||
)
|
||||
op.create_table('project_grade_sheets',
|
||||
@ -154,19 +152,9 @@ def upgrade():
|
||||
)
|
||||
op.create_table('students_groups',
|
||||
sa.Column('group_id', sa.Integer(), nullable=False),
|
||||
sa.Column('student_index', sa.Integer(), nullable=False),
|
||||
sa.Column('student_id', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['group_id'], ['groups.id'], ),
|
||||
sa.ForeignKeyConstraint(['student_index'], ['students.index'], )
|
||||
)
|
||||
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')
|
||||
sa.ForeignKeyConstraint(['student_id'], ['students.id'], )
|
||||
)
|
||||
op.create_table('term_of_defences',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
@ -191,18 +179,18 @@ def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('committees')
|
||||
op.drop_table('term_of_defences')
|
||||
op.drop_table('temporary_availabilities')
|
||||
op.drop_table('students_groups')
|
||||
op.drop_table('project_grade_sheets')
|
||||
op.drop_table('year_group_students')
|
||||
op.drop_table('year_group_project_supervisors')
|
||||
op.drop_table('temporary_availabilities')
|
||||
op.drop_table('groups')
|
||||
op.drop_table('examination_schedules')
|
||||
op.drop_table('year_groups')
|
||||
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_index(op.f('ix_students_email'), table_name='students')
|
||||
op.drop_table('students')
|
||||
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_index(op.f('ix_project_supervisors_email'), table_name='project_supervisors')
|
||||
op.drop_table('project_supervisors')
|
||||
op.drop_table('examination_schedules')
|
||||
op.drop_table('year_groups')
|
||||
# ### end Alembic commands ###
|
Loading…
Reference in New Issue
Block a user