184 lines
5.9 KiB
Python
184 lines
5.9 KiB
Python
from itertools import islice
|
|
from random import randint
|
|
|
|
from apiflask import APIBlueprint
|
|
from flask import Response, abort
|
|
from sqlalchemy import or_
|
|
|
|
from ...base.schemas import MessageSchema
|
|
from ...base.utils import is_allowed_extensions, paginate_models
|
|
from ...dependencies import db
|
|
from ...project_supervisor.models import ProjectSupervisor
|
|
from ...students.models import Group, Student, YearGroup
|
|
from ..exceptions import InvalidNameOrTypeHeaderException
|
|
from ..schemas.students import (
|
|
FileSchema,
|
|
StudentCreateSchema,
|
|
StudentEditSchema,
|
|
StudentListFileDownloaderSchema,
|
|
StudentQuerySchema,
|
|
StudentSchema,
|
|
StudentsPaginationSchema,
|
|
YearGroupInfoQuery,
|
|
)
|
|
from ..utils import generate_csv, parse_csv
|
|
|
|
bp = APIBlueprint("students", __name__, url_prefix="/students")
|
|
|
|
|
|
@bp.get("/<int:year_group_id>/")
|
|
@bp.input(StudentQuerySchema, location="query")
|
|
@bp.output(StudentsPaginationSchema)
|
|
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")
|
|
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(
|
|
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])
|
|
return {"students": data["items"], "max_pages": data["max_pages"]}
|
|
|
|
|
|
@bp.get("/<int:student_id>/detail/")
|
|
@bp.output(StudentSchema)
|
|
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:student_id>/")
|
|
@bp.output(MessageSchema, status_code=202)
|
|
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)
|
|
db.session.commit()
|
|
return {"message": "Student was deleted!"}
|
|
|
|
|
|
@bp.put("/<int:student_id>/")
|
|
@bp.input(StudentEditSchema)
|
|
@bp.output(MessageSchema)
|
|
def edit_student(student_id: int, data: dict) -> dict:
|
|
if not data:
|
|
abort(400, "You have passed empty data!")
|
|
|
|
student_query = Student.query.filter(Student.id == student_id)
|
|
student = student_query.first()
|
|
|
|
if student is None:
|
|
abort(404, "Not found student!")
|
|
|
|
student_query.update(data)
|
|
db.session.commit()
|
|
|
|
return {"message": "Student was updated!"}
|
|
|
|
|
|
@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(
|
|
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!")
|
|
|
|
year_group = YearGroup.query.filter(YearGroup.id == yg_id).first()
|
|
if year_group is None:
|
|
abort(404, "Not found year group!")
|
|
|
|
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!"}
|
|
|
|
|
|
@bp.post("/upload/")
|
|
@bp.input(YearGroupInfoQuery, location="query")
|
|
@bp.input(FileSchema, location="form_and_files")
|
|
@bp.output(MessageSchema)
|
|
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("year_group_id")
|
|
yg = YearGroup.query.filter(YearGroup.id == year_group_id).first()
|
|
if yg is None:
|
|
abort(404, "Not found year group!")
|
|
|
|
uploaded_file = file.get("file")
|
|
if uploaded_file and is_allowed_extensions(uploaded_file.filename):
|
|
try:
|
|
students = parse_csv(uploaded_file, year_group_id)
|
|
while True:
|
|
sliced_students = islice(students, 5)
|
|
list_of_students = list(sliced_students)
|
|
|
|
if len(list_of_students) == 0:
|
|
break
|
|
|
|
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]
|
|
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()
|
|
|
|
except InvalidNameOrTypeHeaderException:
|
|
abort(400, "Invalid format of csv file!")
|
|
else:
|
|
abort(400, "Invalid extension of file")
|
|
|
|
return {"message": "Students was created by uploading csv file!"}
|
|
|
|
|
|
@bp.post("/download/")
|
|
@bp.input(StudentListFileDownloaderSchema, location="query")
|
|
def download_students(query: dict) -> Response:
|
|
year_group_id = query.get("year_group_id")
|
|
students_and_groups = (
|
|
db.session.query(Student, Group)
|
|
.join(Group, Student.groups)
|
|
.filter(Group.year_group_id == year_group_id)
|
|
.join(ProjectSupervisor)
|
|
.all()
|
|
)
|
|
|
|
if len(students_and_groups) == 0:
|
|
abort(404, "Not found students!")
|
|
|
|
csv_file = generate_csv(students_and_groups)
|
|
response = Response(csv_file, mimetype="text/csv")
|
|
response.headers.set(
|
|
"Content-Disposition", "attachment", filename="students_list.csv"
|
|
)
|
|
return response
|