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:
dekun
2026-06-28 11:18:58 +08:00
commit e329d3398a
76 changed files with 8506 additions and 0 deletions
+54
View File
@@ -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}"'},
)