Загрузка данных
from io import BytesIO
from flask import Blueprint, render_template, request, send_file
from flask_login import login_required, current_user
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Alignment
from openpyxl.utils import get_column_letter
from app.models import Intern, Attendance, User
from app.services.statistics import intern_stats, STATUS_LABELS
from app.utils import parse_period
reports_bp = Blueprint("reports", __name__, url_prefix="/reports")
def _resolve_scope():
date_from, date_to = parse_period(default_days=30)
mentor_id = request.args.get("mentor_id", type=int)
if not current_user.is_admin:
mentor_id = current_user.id
return date_from, date_to, mentor_id
@reports_bp.route("/")
@login_required
def index():
date_from, date_to, mentor_id = _resolve_scope()
q = Intern.query
if mentor_id:
q = q.filter(Intern.mentor_id == mentor_id)
interns = q.order_by(Intern.full_name).all()
rows = [
{"intern": i, "stats": intern_stats(i.id, date_from, date_to)}
for i in interns
]
mentors = User.query.filter_by(role="mentor").order_by(User.full_name).all() \
if current_user.is_admin else []
return render_template(
"reports/index.html",
rows=rows, mentors=mentors,
selected_mentor=mentor_id,
date_from=date_from, date_to=date_to,
)
@reports_bp.route("/export/excel")
@login_required
def export_excel():
date_from, date_to, mentor_id = _resolve_scope()
q = Intern.query
if mentor_id:
q = q.filter(Intern.mentor_id == mentor_id)
interns = q.order_by(Intern.full_name).all()
intern_ids = [i.id for i in interns]
wb = Workbook()
# ---- Лист 1: Сводка ----
ws = wb.active
ws.title = "Сводка"
headers = [
"Стажёр", "Направление", "Наставник",
"Всего отметок", "Присутствовал", "Опоздал",
"Отсутствовал", "Уважительная", "% посещаемости",
]
ws.append(headers)
header_fill = PatternFill("solid", fgColor="1F4E78")
for col_idx in range(1, len(headers) + 1):
c = ws.cell(row=1, column=col_idx)
c.font = Font(bold=True, color="FFFFFF")
c.fill = header_fill
c.alignment = Alignment(horizontal="center", vertical="center")
for row in interns:
s = intern_stats(row.id, date_from, date_to)
ws.append([
row.full_name,
row.direction or "",
row.mentor.full_name if row.mentor else "",
s["total"], s["present"], s["late"],
s["absent"], s["excused"], s["rate"],
])
widths = [28, 22, 28, 14, 16, 12, 16, 16, 16]
for i, w in enumerate(widths, start=1):
ws.column_dimensions[get_column_letter(i)].width = w
# ---- Лист 2: Детально ----
ws2 = wb.create_sheet("Посещаемость")
ws2.append(["Стажёр", "Направление", "Дата", "Статус", "Заметка"])
for col_idx in range(1, 6):
c = ws2.cell(row=1, column=col_idx)
c.font = Font(bold=True, color="FFFFFF")
c.fill = header_fill
c.alignment = Alignment(horizontal="center")
if intern_ids:
records = (
Attendance.query
.filter(Attendance.intern_id.in_(intern_ids))
.filter(Attendance.date >= date_from, Attendance.date <= date_to)
.order_by(Attendance.date.desc(), Attendance.intern_id)
.all()
)
intern_map = {i.id: i for i in interns}
for r in records:
i = intern_map[r.intern_id]
ws2.append([
i.full_name, i.direction or "",
r.date.strftime("%d.%m.%Y"),
STATUS_LABELS.get(r.status, r.status),
r.note or "",
])
for i, w in enumerate([28, 22, 14, 20, 40], start=1):
ws2.column_dimensions[get_column_letter(i)].width = w
# ---- Лист 3: Инфо ----
ws3 = wb.create_sheet("Инфо")
ws3.append(["Период", f"{date_from.strftime('%d.%m.%Y')} — {date_to.strftime('%d.%m.%Y')}"])
ws3.append(["Сформировал", current_user.full_name])
ws3.column_dimensions["A"].width = 20
ws3.column_dimensions["B"].width = 40
# Save
buf = BytesIO()
wb.save(buf)
buf.seek(0)
filename = f"interns_report_{date_from}_{date_to}.xlsx"
return send_file(
buf,
as_attachment=True,
download_name=filename,
mimetype="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
)