update endpoints for download and upload students list in csv file
This commit is contained in:
parent
247dfe3f31
commit
0722eb9648
@ -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()
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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):
|
||||||
|
Loading…
Reference in New Issue
Block a user