Initial commit: secondary school grade archive system.
Add FastAPI/React app with Docker deployment, Ubuntu one-click install, and docs for junior/senior high score tracking and mistake bank. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
import csv
|
||||
import io
|
||||
import uuid
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from fastapi.responses import StreamingResponse
|
||||
from sqlalchemy.orm import Session, joinedload
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.core.deps import get_current_user
|
||||
from app.models.user import ExamRecord, SubjectScore, User
|
||||
from app.services.student_access import get_student_for_user
|
||||
|
||||
router = APIRouter(tags=["export"])
|
||||
|
||||
|
||||
@router.get("/students/{student_id}/scores/export")
|
||||
def export_scores_csv(
|
||||
student_id: uuid.UUID,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
student = get_student_for_user(db, student_id, current_user.id)
|
||||
exams = (
|
||||
db.query(ExamRecord)
|
||||
.options(joinedload(ExamRecord.scores).joinedload(SubjectScore.subject))
|
||||
.filter(ExamRecord.student_id == student_id)
|
||||
.order_by(ExamRecord.exam_date.asc())
|
||||
.all()
|
||||
)
|
||||
|
||||
output = io.StringIO()
|
||||
writer = csv.writer(output)
|
||||
writer.writerow(["考试日期", "考试类型", "标题", "科目", "总分", "得分", "占比"])
|
||||
type_map = {"weekly": "周考", "monthly": "月考", "final": "期末"}
|
||||
for exam in exams:
|
||||
for score in exam.scores:
|
||||
writer.writerow([
|
||||
exam.exam_date.isoformat(),
|
||||
type_map.get(exam.exam_type.value, exam.exam_type.value),
|
||||
exam.title or "",
|
||||
score.subject.name if score.subject else "",
|
||||
float(score.total_score),
|
||||
float(score.obtained_score),
|
||||
f"{float(score.ratio) * 100:.2f}%",
|
||||
])
|
||||
|
||||
output.seek(0)
|
||||
filename = f"{student.name}_scores.csv"
|
||||
return StreamingResponse(
|
||||
iter([output.getvalue().encode("utf-8-sig")]),
|
||||
media_type="text/csv",
|
||||
headers={"Content-Disposition": f'attachment; filename="{filename}"'},
|
||||
)
|
||||
Reference in New Issue
Block a user