add endpoint of download examination schedule pdf file
This commit is contained in:
parent
03ffd355ed
commit
c71531ca10
@ -5,7 +5,8 @@ ENV PYTHONUNBUFFERED 1
|
|||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
RUN apt update && pip install --upgrade pip
|
RUN apt update && \
|
||||||
|
pip install --upgrade pip
|
||||||
|
|
||||||
COPY requirements.txt .
|
COPY requirements.txt .
|
||||||
|
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
from apiflask import APIBlueprint
|
import datetime
|
||||||
from flask import abort
|
|
||||||
|
|
||||||
|
from apiflask import APIBlueprint
|
||||||
|
from flask import abort, Response, make_response
|
||||||
|
|
||||||
|
from ...base.utils import paginate_models
|
||||||
|
from ...dependencies import db
|
||||||
|
from ...examination_schedule.models import ExaminationSchedule, Enrollment
|
||||||
|
from ...students.models import Group
|
||||||
|
from ...project_supervisor.models import ProjectSupervisor
|
||||||
from ..schemas import ExaminationScheduleSchema, ExaminationScheduleUpdateSchema, MessageSchema, \
|
from ..schemas import ExaminationScheduleSchema, ExaminationScheduleUpdateSchema, MessageSchema, \
|
||||||
ExaminationSchedulesQuerySchema, ExaminationSchedulesPaginationSchema
|
ExaminationSchedulesQuerySchema, ExaminationSchedulesPaginationSchema
|
||||||
from ...examination_schedule.models import ExaminationSchedule
|
from ..utils import generate_examination_schedule_pdf_file
|
||||||
from ...dependencies import db
|
|
||||||
from ...base.utils import paginate_models
|
|
||||||
|
|
||||||
bp = APIBlueprint("examination_schedule", __name__, url_prefix="/examination_schedule")
|
bp = APIBlueprint("examination_schedule", __name__, url_prefix="/examination_schedule")
|
||||||
|
|
||||||
@ -71,3 +76,33 @@ def set_date_of_examination_schedule(id: int, data: dict) -> dict:
|
|||||||
examination_schedule_query.update(data)
|
examination_schedule_query.update(data)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return {"message": "You set date of examination schedule!"}
|
return {"message": "You set date of examination schedule!"}
|
||||||
|
|
||||||
|
|
||||||
|
@bp.post('/<int:examination_schedule_id>/download/')
|
||||||
|
def download_examination_schedule(examination_schedule_id: int) -> Response:
|
||||||
|
examination_schedule = db.session.query(ExaminationSchedule). \
|
||||||
|
filter(ExaminationSchedule.id == examination_schedule_id).first()
|
||||||
|
|
||||||
|
if examination_schedule is None:
|
||||||
|
abort(404, "Examination schedule doesn't exist!")
|
||||||
|
|
||||||
|
distinct_dates = db.session.query(db.func.Date(Enrollment.start_date)).distinct().all()
|
||||||
|
# print(distinct_dates)
|
||||||
|
nested_enrollments = []
|
||||||
|
for d in distinct_dates:
|
||||||
|
date_tmp = datetime.datetime.strptime(d[0], "%Y-%m-%d").date()
|
||||||
|
enrollment = db.session.query(Enrollment).join(ExaminationSchedule, isouter=True).\
|
||||||
|
join(Group, isouter=True).join(ProjectSupervisor, isouter=True). \
|
||||||
|
filter(ExaminationSchedule.id == examination_schedule_id).filter(
|
||||||
|
db.func.Date(Enrollment.start_date) == date_tmp).all()
|
||||||
|
nested_enrollments.append(enrollment)
|
||||||
|
|
||||||
|
# print(nested_enrollments)
|
||||||
|
pdf = generate_examination_schedule_pdf_file(examination_schedule.title, nested_enrollments)
|
||||||
|
title = examination_schedule.title.replace("-", "_").split()
|
||||||
|
filename = "_".join(title)
|
||||||
|
|
||||||
|
response = make_response(pdf)
|
||||||
|
response.headers['Content-Type'] = 'application/pdf'
|
||||||
|
response.headers['Content-Disposition'] = 'attachment; filename=%s.pdf' % filename
|
||||||
|
return response
|
||||||
|
@ -1,10 +1,19 @@
|
|||||||
from typing import Generator, Any, List
|
import datetime
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
from io import BytesIO
|
||||||
|
from itertools import chain
|
||||||
|
from typing import Generator, Any, List
|
||||||
|
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
from reportlab.lib import colors
|
||||||
|
from reportlab.lib.enums import TA_CENTER
|
||||||
|
from reportlab.lib.styles import getSampleStyleSheet
|
||||||
|
from reportlab.lib.units import mm, inch
|
||||||
|
from reportlab.platypus import SimpleDocTemplate, Paragraph, PageBreak, Table
|
||||||
|
|
||||||
from .exceptions import InvalidNameOrTypeHeaderException
|
from .exceptions import InvalidNameOrTypeHeaderException
|
||||||
from ..students.models import Student
|
from ..students.models import Student
|
||||||
|
from ..examination_schedule.models import Enrollment
|
||||||
|
|
||||||
|
|
||||||
def check_columns(df: pd.DataFrame) -> bool:
|
def check_columns(df: pd.DataFrame) -> bool:
|
||||||
@ -55,3 +64,82 @@ def generate_csv(students: List[Student]) -> str:
|
|||||||
|
|
||||||
df = pd.DataFrame(dataframe)
|
df = pd.DataFrame(dataframe)
|
||||||
return df.to_csv(index=False)
|
return df.to_csv(index=False)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_examination_schedule_pdf_file(title: str, nested_enrollments: List[List[Enrollment]]) -> bytes:
|
||||||
|
pagesize = (297 * mm, 210 * mm)
|
||||||
|
headers = ["lp.", "Godzina", "Nazwa projektu", "Opiekun", "Zespol", "Komisja"]
|
||||||
|
pdf_buffer = BytesIO()
|
||||||
|
my_doc = SimpleDocTemplate(
|
||||||
|
pdf_buffer,
|
||||||
|
pagesize=pagesize,
|
||||||
|
topMargin=1 * inch,
|
||||||
|
leftMargin=1 * inch,
|
||||||
|
rightMargin=1 * inch,
|
||||||
|
bottomMargin=1 * inch,
|
||||||
|
title=title
|
||||||
|
)
|
||||||
|
|
||||||
|
style = getSampleStyleSheet()
|
||||||
|
bodyText = style['BodyText']
|
||||||
|
bodyText.fontName = 'Helvetica'
|
||||||
|
normal = style["Heading1"]
|
||||||
|
normal.alignment = TA_CENTER
|
||||||
|
flowables = []
|
||||||
|
|
||||||
|
# print(nested_enrollments)
|
||||||
|
for enrollments in nested_enrollments:
|
||||||
|
if len(enrollments) == 0:
|
||||||
|
continue
|
||||||
|
date = datetime.datetime.strftime(enrollments[0].start_date, '%d/%m/%Y')
|
||||||
|
paragraph_1 = Paragraph(f"{title} ~ {date}", normal)
|
||||||
|
flowables.append(paragraph_1)
|
||||||
|
data = [headers]
|
||||||
|
|
||||||
|
for idx, e in enumerate(enrollments, start=1):
|
||||||
|
new_date = e.start_date + datetime.timedelta(hours=2)
|
||||||
|
group_name = e.group.name if e.group is not None else ""
|
||||||
|
if group_name != '':
|
||||||
|
ps = e.group.project_supervisor
|
||||||
|
project_supervisor_fullname = f"{ps.first_name[0]}. {ps.last_name}"
|
||||||
|
students = e.group.students
|
||||||
|
# print(students)
|
||||||
|
team = ", ".join([f"{s.first_name} {s.last_name}" for s in students])
|
||||||
|
else:
|
||||||
|
project_supervisor_fullname = ""
|
||||||
|
team = ""
|
||||||
|
|
||||||
|
members = e.committee.members
|
||||||
|
# print(members)
|
||||||
|
if len(members) == 0:
|
||||||
|
committee = ''
|
||||||
|
else:
|
||||||
|
members_iter = (f"{m.first_name[0]} {m.last_name}" for m in members)
|
||||||
|
if project_supervisor_fullname != '':
|
||||||
|
members_iter = chain(members_iter, [project_supervisor_fullname])
|
||||||
|
|
||||||
|
committee = ", ".join(members_iter)
|
||||||
|
|
||||||
|
data.append([str(idx), new_date.strftime("%H:%M"),
|
||||||
|
Paragraph(group_name, bodyText),
|
||||||
|
Paragraph(project_supervisor_fullname, bodyText),
|
||||||
|
Paragraph(team, bodyText),
|
||||||
|
Paragraph(committee, bodyText),
|
||||||
|
])
|
||||||
|
# print(data)
|
||||||
|
|
||||||
|
table = Table(data=data,
|
||||||
|
style=[
|
||||||
|
('GRID', (0, 0), (-1, -1), 0.5, colors.black),
|
||||||
|
('BACKGROUND', (0, 0), (-1, 0), colors.HexColor("#A6F1A6")),
|
||||||
|
('BACKGROUND', (0, 0), (1, -1), colors.HexColor("#A6F1A6"))
|
||||||
|
],
|
||||||
|
colWidths=[0.25 * inch, 0.7 * inch, 1.6 * inch, 1.5 * inch, 4 * inch, 3 * inch]
|
||||||
|
)
|
||||||
|
flowables.append(table)
|
||||||
|
flowables.append(PageBreak())
|
||||||
|
|
||||||
|
my_doc.build(flowables)
|
||||||
|
pdf_value = pdf_buffer.getvalue()
|
||||||
|
pdf_buffer.close()
|
||||||
|
return pdf_value
|
||||||
|
@ -20,6 +20,7 @@ class Enrollment(Base):
|
|||||||
examination_schedule = db.relationship('ExaminationSchedule', backref='enrollments')
|
examination_schedule = db.relationship('ExaminationSchedule', backref='enrollments')
|
||||||
committee = db.relationship("Committee", uselist=False, backref=db.backref('enrollment', passive_deletes=True))
|
committee = db.relationship("Committee", uselist=False, backref=db.backref('enrollment', passive_deletes=True))
|
||||||
group_id = db.Column(db.Integer, db.ForeignKey('groups.id'))
|
group_id = db.Column(db.Integer, db.ForeignKey('groups.id'))
|
||||||
|
group = db.relationship("Group", uselist=False, backref='enrollment')
|
||||||
|
|
||||||
|
|
||||||
class Committee(Base):
|
class Committee(Base):
|
||||||
|
@ -17,7 +17,7 @@ class Group(Base):
|
|||||||
project_supervisor = db.relationship('ProjectSupervisor', backref='groups', lazy=True)
|
project_supervisor = db.relationship('ProjectSupervisor', backref='groups', lazy=True)
|
||||||
points_for_first_term = db.Column(db.Integer, default=0, nullable=False)
|
points_for_first_term = db.Column(db.Integer, default=0, nullable=False)
|
||||||
points_for_second_term = db.Column(db.Integer, default=0, nullable=False)
|
points_for_second_term = db.Column(db.Integer, default=0, nullable=False)
|
||||||
enrollment = db.relationship('Enrollment', uselist=False, backref='group')
|
# enrollment = db.relationship('Enrollment', uselist=False, backref='group')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def search_by_name(cls, search_name: str = None) -> BaseQuery:
|
def search_by_name(cls, search_name: str = None) -> BaseQuery:
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
alembic==1.7.7
|
click>=8.1.3,<8.2.0
|
||||||
click==8.1.3
|
Flask>=2.1.2,<2.2.0
|
||||||
Flask==2.1.2
|
Flask-Cors>=3.0.10,<3.1.0
|
||||||
Flask-Cors==3.0.10
|
flask-marshmallow>=0.14.0,<0.15.0
|
||||||
flask-marshmallow==0.14.0
|
Flask-Migrate>=3.1.0,<3.2.0
|
||||||
Flask-Migrate==3.1.0
|
Flask-SQLAlchemy>=2.5.1,<2.6.0
|
||||||
Flask-SQLAlchemy==2.5.1
|
MarkupSafe>=2.1.1,<2.2.0
|
||||||
MarkupSafe==2.1.1
|
marshmallow-sqlalchemy>=0.28.0,<0.29.0
|
||||||
marshmallow-sqlalchemy==0.28.0
|
pandas>=1.4.2,<1.5.0
|
||||||
pandas==1.4.2
|
pytest>=7.1.2,<7.2.0
|
||||||
pytest==7.1.2
|
pytest-flask>=1.2.0,<1.3.0
|
||||||
pytest-flask==1.2.0
|
|
||||||
apiflask>=1.0.2,<1.1.0
|
apiflask>=1.0.2,<1.1.0
|
||||||
python-dotenv==0.21.0
|
python-dotenv==0.21.0
|
||||||
factory_boy==3.2.1
|
factory_boy>=3.2.1,<3.3.0
|
||||||
|
reportlab>=3.6.12,<3.7.0
|
||||||
|
Loading…
Reference in New Issue
Block a user