mobile . themes
This commit is contained in:
255
excel_export.py
255
excel_export.py
@@ -1,217 +1,78 @@
|
||||
# excel_export.py
|
||||
from openpyxl import Workbook
|
||||
from openpyxl.styles import Font, Alignment, PatternFill, Border, Side
|
||||
from datetime import datetime
|
||||
from io import BytesIO
|
||||
import csv
|
||||
from io import StringIO, BytesIO
|
||||
|
||||
|
||||
def _to_csv(headers, rows):
|
||||
"""生成 UTF-8 BOM 编码的 CSV,兼容 VSCode 和 Excel"""
|
||||
buf = StringIO()
|
||||
writer = csv.writer(buf)
|
||||
writer.writerow(headers)
|
||||
writer.writerows(rows)
|
||||
|
||||
output = BytesIO()
|
||||
output.write(b'\xef\xbb\xbf') # UTF-8 BOM
|
||||
output.write(buf.getvalue().encode('utf-8'))
|
||||
output.seek(0)
|
||||
return output
|
||||
|
||||
|
||||
def export_users_to_excel(users):
|
||||
"""导出用户数据到Excel"""
|
||||
wb = Workbook()
|
||||
ws = wb.active
|
||||
ws.title = "用户数据"
|
||||
|
||||
# 设置表头样式
|
||||
header_fill = PatternFill(start_color="4472C4", end_color="4472C4", fill_type="solid")
|
||||
header_font = Font(bold=True, color="FFFFFF", size=12)
|
||||
header_alignment = Alignment(horizontal="center", vertical="center")
|
||||
|
||||
# 表头
|
||||
headers = ["ID", "用户名", "邮箱", "手机号", "角色", "状态", "完成考试数", "注册时间"]
|
||||
for col, header in enumerate(headers, 1):
|
||||
cell = ws.cell(row=1, column=col, value=header)
|
||||
cell.fill = header_fill
|
||||
cell.font = header_font
|
||||
cell.alignment = header_alignment
|
||||
rows = []
|
||||
for u in users:
|
||||
rows.append([
|
||||
u.id, u.name, u.email or "", u.phone or "", u.role,
|
||||
"已封禁" if u.is_banned else "正常", u.completed_exams,
|
||||
u.created_at.strftime("%Y-%m-%d %H:%M:%S") if u.created_at else ""
|
||||
])
|
||||
return _to_csv(headers, rows)
|
||||
|
||||
# 数据行
|
||||
for row, user in enumerate(users, 2):
|
||||
ws.cell(row=row, column=1, value=user.id)
|
||||
ws.cell(row=row, column=2, value=user.name)
|
||||
ws.cell(row=row, column=3, value=user.email or "")
|
||||
ws.cell(row=row, column=4, value=user.phone or "")
|
||||
ws.cell(row=row, column=5, value=user.role)
|
||||
ws.cell(row=row, column=6, value="已封禁" if user.is_banned else "正常")
|
||||
ws.cell(row=row, column=7, value=user.completed_exams)
|
||||
ws.cell(row=row, column=8, value=user.created_at.strftime("%Y-%m-%d %H:%M:%S") if user.created_at else "")
|
||||
|
||||
# 调整列宽
|
||||
ws.column_dimensions['A'].width = 8
|
||||
ws.column_dimensions['B'].width = 20
|
||||
ws.column_dimensions['C'].width = 30
|
||||
ws.column_dimensions['D'].width = 15
|
||||
ws.column_dimensions['E'].width = 12
|
||||
ws.column_dimensions['F'].width = 10
|
||||
ws.column_dimensions['G'].width = 12
|
||||
ws.column_dimensions['H'].width = 20
|
||||
|
||||
# 保存到内存
|
||||
output = BytesIO()
|
||||
wb.save(output)
|
||||
output.seek(0)
|
||||
return output
|
||||
|
||||
def export_exams_to_excel(exams):
|
||||
"""导出考试数据到Excel"""
|
||||
wb = Workbook()
|
||||
ws = wb.active
|
||||
ws.title = "考试数据"
|
||||
|
||||
header_fill = PatternFill(start_color="4472C4", end_color="4472C4", fill_type="solid")
|
||||
header_font = Font(bold=True, color="FFFFFF", size=12)
|
||||
header_alignment = Alignment(horizontal="center", vertical="center")
|
||||
|
||||
headers = ["ID", "考试名称", "创建者ID", "总分", "时长(分钟)", "状态", "提交数", "创建时间"]
|
||||
for col, header in enumerate(headers, 1):
|
||||
cell = ws.cell(row=1, column=col, value=header)
|
||||
cell.fill = header_fill
|
||||
cell.font = header_font
|
||||
cell.alignment = header_alignment
|
||||
rows = []
|
||||
for e in exams:
|
||||
rows.append([
|
||||
e.id, e.title, e.creator_id, e.total_score, e.duration, e.status,
|
||||
len(e.submissions) if e.submissions else 0,
|
||||
e.created_at.strftime("%Y-%m-%d %H:%M:%S") if e.created_at else ""
|
||||
])
|
||||
return _to_csv(headers, rows)
|
||||
|
||||
for row, exam in enumerate(exams, 2):
|
||||
ws.cell(row=row, column=1, value=exam.id)
|
||||
ws.cell(row=row, column=2, value=exam.title)
|
||||
ws.cell(row=row, column=3, value=exam.creator_id)
|
||||
ws.cell(row=row, column=4, value=exam.total_score)
|
||||
ws.cell(row=row, column=5, value=exam.duration)
|
||||
ws.cell(row=row, column=6, value=exam.status)
|
||||
ws.cell(row=row, column=7, value=len(exam.submissions) if exam.submissions else 0)
|
||||
ws.cell(row=row, column=8, value=exam.created_at.strftime("%Y-%m-%d %H:%M:%S") if exam.created_at else "")
|
||||
|
||||
ws.column_dimensions['A'].width = 8
|
||||
ws.column_dimensions['B'].width = 30
|
||||
ws.column_dimensions['C'].width = 12
|
||||
ws.column_dimensions['D'].width = 10
|
||||
ws.column_dimensions['E'].width = 15
|
||||
ws.column_dimensions['F'].width = 12
|
||||
ws.column_dimensions['G'].width = 10
|
||||
ws.column_dimensions['H'].width = 20
|
||||
|
||||
output = BytesIO()
|
||||
wb.save(output)
|
||||
output.seek(0)
|
||||
return output
|
||||
|
||||
def export_submissions_to_excel(submissions):
|
||||
"""导出提交记录到Excel"""
|
||||
wb = Workbook()
|
||||
ws = wb.active
|
||||
ws.title = "提交记录"
|
||||
|
||||
header_fill = PatternFill(start_color="4472C4", end_color="4472C4", fill_type="solid")
|
||||
header_font = Font(bold=True, color="FFFFFF", size=12)
|
||||
header_alignment = Alignment(horizontal="center", vertical="center")
|
||||
|
||||
headers = ["ID", "考试ID", "考试名称", "用户ID", "用户名", "得分", "总分", "状态", "提交时间"]
|
||||
for col, header in enumerate(headers, 1):
|
||||
cell = ws.cell(row=1, column=col, value=header)
|
||||
cell.fill = header_fill
|
||||
cell.font = header_font
|
||||
cell.alignment = header_alignment
|
||||
rows = []
|
||||
for s in submissions:
|
||||
rows.append([
|
||||
s.id, s.exam_id, s.exam.title if s.exam else "", s.user_id,
|
||||
s.user.name if s.user else "", s.score if s.score is not None else "",
|
||||
s.total_score, s.status,
|
||||
s.submitted_at.strftime("%Y-%m-%d %H:%M:%S") if s.submitted_at else ""
|
||||
])
|
||||
return _to_csv(headers, rows)
|
||||
|
||||
for row, sub in enumerate(submissions, 2):
|
||||
ws.cell(row=row, column=1, value=sub.id)
|
||||
ws.cell(row=row, column=2, value=sub.exam_id)
|
||||
ws.cell(row=row, column=3, value=sub.exam.title if sub.exam else "")
|
||||
ws.cell(row=row, column=4, value=sub.user_id)
|
||||
ws.cell(row=row, column=5, value=sub.user.name if sub.user else "")
|
||||
ws.cell(row=row, column=6, value=sub.score if sub.score is not None else "")
|
||||
ws.cell(row=row, column=7, value=sub.total_score)
|
||||
ws.cell(row=row, column=8, value=sub.status)
|
||||
ws.cell(row=row, column=9, value=sub.submitted_at.strftime("%Y-%m-%d %H:%M:%S") if sub.submitted_at else "")
|
||||
|
||||
ws.column_dimensions['A'].width = 8
|
||||
ws.column_dimensions['B'].width = 10
|
||||
ws.column_dimensions['C'].width = 30
|
||||
ws.column_dimensions['D'].width = 10
|
||||
ws.column_dimensions['E'].width = 20
|
||||
ws.column_dimensions['F'].width = 10
|
||||
ws.column_dimensions['G'].width = 10
|
||||
ws.column_dimensions['H'].width = 12
|
||||
ws.column_dimensions['I'].width = 20
|
||||
|
||||
output = BytesIO()
|
||||
wb.save(output)
|
||||
output.seek(0)
|
||||
return output
|
||||
|
||||
def export_contests_to_excel(contests):
|
||||
"""导出杯赛数据到Excel"""
|
||||
wb = Workbook()
|
||||
ws = wb.active
|
||||
ws.title = "杯赛数据"
|
||||
|
||||
header_fill = PatternFill(start_color="4472C4", end_color="4472C4", fill_type="solid")
|
||||
header_font = Font(bold=True, color="FFFFFF", size=12)
|
||||
header_alignment = Alignment(horizontal="center", vertical="center")
|
||||
|
||||
headers = ["ID", "杯赛名称", "主办方", "开始日期", "结束日期", "状态", "参与人数", "创建时间"]
|
||||
for col, header in enumerate(headers, 1):
|
||||
cell = ws.cell(row=1, column=col, value=header)
|
||||
cell.fill = header_fill
|
||||
cell.font = header_font
|
||||
cell.alignment = header_alignment
|
||||
rows = []
|
||||
for c in contests:
|
||||
rows.append([
|
||||
c.id, c.name, c.organizer or "", c.start_date or "", c.end_date or "",
|
||||
c.status, c.participants,
|
||||
c.created_at.strftime("%Y-%m-%d %H:%M:%S") if c.created_at else ""
|
||||
])
|
||||
return _to_csv(headers, rows)
|
||||
|
||||
for row, contest in enumerate(contests, 2):
|
||||
ws.cell(row=row, column=1, value=contest.id)
|
||||
ws.cell(row=row, column=2, value=contest.name)
|
||||
ws.cell(row=row, column=3, value=contest.organizer or "")
|
||||
ws.cell(row=row, column=4, value=contest.start_date or "")
|
||||
ws.cell(row=row, column=5, value=contest.end_date or "")
|
||||
ws.cell(row=row, column=6, value=contest.status)
|
||||
ws.cell(row=row, column=7, value=contest.participants)
|
||||
ws.cell(row=row, column=8, value=contest.created_at.strftime("%Y-%m-%d %H:%M:%S") if contest.created_at else "")
|
||||
|
||||
ws.column_dimensions['A'].width = 8
|
||||
ws.column_dimensions['B'].width = 30
|
||||
ws.column_dimensions['C'].width = 20
|
||||
ws.column_dimensions['D'].width = 15
|
||||
ws.column_dimensions['E'].width = 15
|
||||
ws.column_dimensions['F'].width = 12
|
||||
ws.column_dimensions['G'].width = 12
|
||||
ws.column_dimensions['H'].width = 20
|
||||
|
||||
output = BytesIO()
|
||||
wb.save(output)
|
||||
output.seek(0)
|
||||
return output
|
||||
|
||||
def export_posts_to_excel(posts):
|
||||
"""导出论坛帖子到Excel"""
|
||||
wb = Workbook()
|
||||
ws = wb.active
|
||||
ws.title = "论坛帖子"
|
||||
|
||||
header_fill = PatternFill(start_color="4472C4", end_color="4472C4", fill_type="solid")
|
||||
header_font = Font(bold=True, color="FFFFFF", size=12)
|
||||
header_alignment = Alignment(horizontal="center", vertical="center")
|
||||
|
||||
headers = ["ID", "标题", "作者", "分类", "浏览数", "回复数", "点赞数", "创建时间"]
|
||||
for col, header in enumerate(headers, 1):
|
||||
cell = ws.cell(row=1, column=col, value=header)
|
||||
cell.fill = header_fill
|
||||
cell.font = header_font
|
||||
cell.alignment = header_alignment
|
||||
|
||||
for row, post in enumerate(posts, 2):
|
||||
ws.cell(row=row, column=1, value=post.id)
|
||||
ws.cell(row=row, column=2, value=post.title)
|
||||
ws.cell(row=row, column=3, value=post.author.name if post.author else "")
|
||||
ws.cell(row=row, column=4, value=post.category or "")
|
||||
ws.cell(row=row, column=5, value=post.views)
|
||||
ws.cell(row=row, column=6, value=post.reply_count)
|
||||
ws.cell(row=row, column=7, value=post.like_count)
|
||||
ws.cell(row=row, column=8, value=post.created_at.strftime("%Y-%m-%d %H:%M:%S") if post.created_at else "")
|
||||
|
||||
ws.column_dimensions['A'].width = 8
|
||||
ws.column_dimensions['B'].width = 40
|
||||
ws.column_dimensions['C'].width = 20
|
||||
ws.column_dimensions['D'].width = 15
|
||||
ws.column_dimensions['E'].width = 10
|
||||
ws.column_dimensions['F'].width = 10
|
||||
ws.column_dimensions['G'].width = 10
|
||||
ws.column_dimensions['H'].width = 20
|
||||
|
||||
output = BytesIO()
|
||||
wb.save(output)
|
||||
output.seek(0)
|
||||
return output
|
||||
rows = []
|
||||
for p in posts:
|
||||
rows.append([
|
||||
p.id, p.title, p.author.name if p.author else "", p.category or "",
|
||||
p.views, p.reply_count, p.like_count,
|
||||
p.created_at.strftime("%Y-%m-%d %H:%M:%S") if p.created_at else ""
|
||||
])
|
||||
return _to_csv(headers, rows)
|
||||
|
||||
Reference in New Issue
Block a user