update endpoints for download and upload students list in csv file

This commit is contained in:
dominik24c 2022-11-17 14:56:19 +01:00
parent 247dfe3f31
commit 0722eb9648
5 changed files with 54 additions and 28 deletions

View File

@ -80,7 +80,7 @@ def create_group(year_group_id: int, data: dict) -> dict:
return {"message": "Group was created!"} return {"message": "Group was created!"}
@bp.get("/<int:id>/") @bp.get("/<int:id>/detail/")
@bp.output(GroupSchema) @bp.output(GroupSchema)
def detail_group(id: int) -> Group: def detail_group(id: int) -> Group:
group = Group.query.filter_by(id=id).first() group = Group.query.filter_by(id=id).first()

View File

@ -3,12 +3,12 @@ from itertools import islice
from flask import Response, abort from flask import Response, abort
from apiflask import APIBlueprint from apiflask import APIBlueprint
from sqlalchemy.exc import IntegrityError from sqlalchemy import or_
from flask_sqlalchemy import get_debug_queries from flask_sqlalchemy import get_debug_queries
from ...students.models import Student, Group, YearGroup, YearGroupStudents from ...students.models import Student, Group, YearGroup, YearGroupStudents
from ...project_supervisor.models import ProjectSupervisor from ...project_supervisor.models import ProjectSupervisor
from ..schemas import StudentSchema, StudentEditSchema, StudentsPaginationSchema, \ from ..schemas import StudentSchema, StudentEditSchema, StudentsPaginationSchema, YearGroupInfoQuery, \
StudentCreateSchema, MessageSchema, FileSchema, StudentQuerySchema, StudentListFileDownloaderSchema StudentCreateSchema, MessageSchema, FileSchema, StudentQuerySchema, StudentListFileDownloaderSchema
from ...dependencies import db from ...dependencies import db
from ..utils import parse_csv, generate_csv from ..utils import parse_csv, generate_csv
@ -109,27 +109,49 @@ def create_student(data: dict) -> dict:
@bp.post("/upload/") @bp.post("/upload/")
@bp.input(YearGroupInfoQuery, location='query')
@bp.input(FileSchema, location='form_and_files') @bp.input(FileSchema, location='form_and_files')
@bp.output(MessageSchema) @bp.output(MessageSchema)
def upload_students(file: 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')
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') uploaded_file = file.get('file')
if uploaded_file and is_allowed_extensions(uploaded_file.filename): if uploaded_file and is_allowed_extensions(uploaded_file.filename):
try: try:
students = parse_csv(uploaded_file, mode=True) students = parse_csv(uploaded_file)
while True: while True:
sliced_students = islice(students, 5) sliced_students = islice(students, 5)
list_of_students = list(sliced_students) list_of_students = list(sliced_students)
if len(list_of_students) == 0: if len(list_of_students) == 0:
break break
db.session.add_all(list_of_students)
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()
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() 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: except InvalidNameOrTypeHeaderException:
abort(400, "Invalid format of csv file!") abort(400, "Invalid format of csv file!")
except IntegrityError as e:
# in the future create sql query checks index and add only these students, which didn't exist in db
abort(400, "These students have already exist!")
else: else:
abort(400, "Invalid extension of file") abort(400, "Invalid extension of file")
@ -139,14 +161,15 @@ def upload_students(file: dict) -> dict:
@bp.post("/download/") @bp.post("/download/")
@bp.input(StudentListFileDownloaderSchema, location='query') @bp.input(StudentListFileDownloaderSchema, location='query')
def download_students(query: dict) -> Response: def download_students(query: dict) -> Response:
mode = query.get('mode') year_group_id = query.get('year_group_id')
mode = mode if mode is not None else True students_and_groups = db.session.query(Student, Group).join(Group, Student.groups). \
students = db.session.query(Student).join(Group). \ filter(Group.year_group_id == year_group_id). \
join(ProjectSupervisor).filter(Student.mode == mode).all() join(ProjectSupervisor).all()
if len(students) == 0: if len(students_and_groups) == 0:
abort(404, "Not found students, which are assigned to group!") abort(404, "Not found students, which are assigned to group!")
csv_file = generate_csv(students)
csv_file = generate_csv(students_and_groups)
response = Response(csv_file, mimetype='text/csv') response = Response(csv_file, mimetype='text/csv')
response.headers.set("Content-Disposition", "attachment", filename="students_list.csv") response.headers.set("Content-Disposition", "attachment", filename="students_list.csv")
return response return response

View File

@ -6,5 +6,5 @@ from .project_supervisor import ProjectSupervisorQuerySchema, ProjectSupervisors
ProjectSupervisorCreateSchema, ProjectSupervisorEditSchema, ProjectSupervisorYearGroupSchema ProjectSupervisorCreateSchema, ProjectSupervisorEditSchema, ProjectSupervisorYearGroupSchema
from .students import ProjectSupervisorSchema, GroupSchema, StudentSchema, StudentsPaginationSchema, \ from .students import ProjectSupervisorSchema, GroupSchema, StudentSchema, StudentsPaginationSchema, \
StudentListFileDownloaderSchema, StudentCreateSchema, StudentEditSchema, MessageSchema, FileSchema, \ StudentListFileDownloaderSchema, StudentCreateSchema, StudentEditSchema, MessageSchema, FileSchema, \
StudentQuerySchema StudentQuerySchema, YearGroupInfoQuery
from .year_group import YearGroupSchema, YearGroupPaginationSchema, YearGroupQuerySchema from .year_group import YearGroupSchema, YearGroupPaginationSchema, YearGroupQuerySchema

View File

@ -1,4 +1,4 @@
from marshmallow import fields, validate from marshmallow import fields, validate, Schema
from ...dependencies import ma from ...dependencies import ma
from ...students.models import Student, Group from ...students.models import Student, Group
@ -32,7 +32,7 @@ class StudentsPaginationSchema(ma.Schema):
class StudentListFileDownloaderSchema(ma.Schema): class StudentListFileDownloaderSchema(ma.Schema):
mode = fields.Integer() year_group_id = fields.Integer(required=True)
class StudentCreateSchema(ma.Schema): class StudentCreateSchema(ma.Schema):
@ -64,3 +64,7 @@ class StudentQuerySchema(ma.Schema):
order_by_last_name = fields.Str() order_by_last_name = fields.Str()
page = fields.Integer() page = fields.Integer()
per_page = fields.Integer() per_page = fields.Integer()
class YearGroupInfoQuery(Schema):
id = fields.Integer(required=True)

View File

@ -2,7 +2,7 @@ import datetime
from collections import defaultdict from collections import defaultdict
from io import BytesIO from io import BytesIO
from itertools import chain from itertools import chain
from typing import Generator, Any, List from typing import Generator, Any, List, Tuple
import pandas as pd import pandas as pd
from reportlab.lib import colors from reportlab.lib import colors
@ -10,10 +10,10 @@ from reportlab.lib.enums import TA_CENTER
from reportlab.lib.styles import getSampleStyleSheet from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.units import mm, inch from reportlab.lib.units import mm, inch
from reportlab.platypus import SimpleDocTemplate, Paragraph, PageBreak, Table from reportlab.platypus import SimpleDocTemplate, Paragraph, PageBreak, Table
from werkzeug.datastructures import FileStorage
from .exceptions import InvalidNameOrTypeHeaderException from .exceptions import InvalidNameOrTypeHeaderException
from ..students.models import Student from ..students.models import Student, Group
# from ..examination_schedule.models import Enrollment
def check_columns(df: pd.DataFrame) -> bool: def check_columns(df: pd.DataFrame) -> bool:
@ -34,7 +34,7 @@ def check_columns(df: pd.DataFrame) -> bool:
return flag return flag
def parse_csv(file, mode) -> Generator[Student, Any, None]: def parse_csv(file: FileStorage) -> Generator[Student, Any, None]:
df = pd.read_csv(file) df = pd.read_csv(file)
if not check_columns(df): if not check_columns(df):
@ -45,18 +45,17 @@ def parse_csv(file, mode) -> Generator[Student, Any, None]:
index=dict(item.items())['INDEKS'], index=dict(item.items())['INDEKS'],
pesel=str(int(dict(item.items())['PESEL'])) if not pd.isna( pesel=str(int(dict(item.items())['PESEL'])) if not pd.isna(
dict(item.items())['PESEL']) else None, dict(item.items())['PESEL']) else None,
email=dict(item.items())['EMAIL'], email=dict(item.items())['EMAIL'])
mode=mode)
for _, item in df.iterrows()) for _, item in df.iterrows())
return students return students
def generate_csv(students: List[Student]) -> str: 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'] 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, data = [(student.pesel, student.index, student.first_name, student.last_name, student.email,
student.group.cdyd_kod, student.group.prz_kod, student.group.tzaj_kod, student.group.project_supervisor_id, group.cdyd_kod, group.prz_kod, group.tzaj_kod, group.project_supervisor_id,
None) for student in students] None) for student, group in students_and_groups]
dataframe = defaultdict(list) dataframe = defaultdict(list)
for row in data: for row in data:
for idx, item in enumerate(row): for idx, item in enumerate(row):