import shutil from pathlib import Path import pymupdf as fitz import pytest from fastapi.testclient import TestClient from wisegrade.db import get_db from wisegrade.main import app from wisegrade.scoring import run_batch from wisegrade.storage import scan def make_pdf(path, title): pdf = fitz.open() page = pdf.new_page() page.insert_text((72, 72), title + " results and analysis for robot control system. " * 4) pdf.save(path) @pytest.mark.integration def test_docs_examples_end_to_end(environment, session_factory): root = environment / "inbox" / "2026春季第1批" / "1023009006_李傲琦_基于ROS2系统的自瞄移动机器人" root.mkdir(parents=True) docs = Path(__file__).resolve().parents[2] / "docs" for filename in ("阶段报告1.pdf", "阶段报告2.pdf", "阶段报告3.pdf", "自动化专业综合实践报告.pdf"): shutil.copy2(docs / filename, root / filename) make_pdf(root / "汇报PPT.pdf", "Presentation") data = scan("2026春季第1批") assert len(data["projects"]) == 1 scanned = data["projects"][0] assert scanned["missing"] == [] assert next(f for f in scanned["files"] if f["category"] == "presentation")["status"] == "needs_confirmation" def override_db(): with session_factory() as session: yield session app.dependency_overrides[get_db] = override_db client = TestClient(app) try: files = [{"relative_path": f["relative_path"], "category": f["category"], "sha256": f["sha256"], "status": "recognized"} for f in scanned["files"]] response = client.post("/api/import/confirm", json={"course_version_id": 1, "projects": [{"relative_dir": scanned["relative_dir"], "student_no": scanned["student_no"], "name": scanned["name"], "topic": scanned["topic"], "files": files}]}) assert response.status_code == 200, response.text project_id = response.json()["project_ids"][0] response = client.post("/api/scoring-batches", json={"course_version_id": 1, "rubric_version_id": 1}) assert response.status_code == 200, response.text batch_id = response.json()["id"] run_batch(batch_id, session_factory=session_factory) progress = client.get(f"/api/scoring-batches/{batch_id}/progress").json() assert progress["status"] == "completed", progress assert progress["progress_done"] == progress["progress_total"] == 22 result = client.get(f"/api/projects/{project_id}/scores?batch_id={batch_id}").json() assert result["totals"]["overall"] is not None assert all(row["status"] == "scored" and (row["model_score"] == 0 or row["evidence"] and row["evidence"][0]["page"] >= 1) for row in result["rows"]), result["rows"] evidence_row = next(row for row in result["rows"] if row["evidence"] and row["evidence"][0]["image_url"]) assert client.get(evidence_row["evidence"][0]["image_url"]).content[:8] == b"\x89PNG\r\n\x1a\n" score_id = result["rows"][0]["score_id"] teacher_score = 0 if result["rows"][0]["model_score"] != 0 else 1 reviewed = client.put(f"/api/criterion-scores/{score_id}/review", json={"score": teacher_score, "comment": "教师核定", "reason": "核查原文"}) assert reviewed.status_code == 200 assert reviewed.json()["model_score"] != reviewed.json()["teacher_score"] assert len(client.get(f"/api/criterion-scores/{score_id}/revisions").json()) == 1 excel = client.post("/api/exports/excel", json={"batch_id": batch_id}) pdf = client.post("/api/exports/project-pdf", json={"batch_id": batch_id, "project_id": project_id}) assert excel.status_code == pdf.status_code == 200 assert client.get(f"/api/exports/{excel.json()['id']}/download").content[:2] == b"PK" assert client.get(f"/api/exports/{pdf.json()['id']}/download").content[:4] == b"%PDF" finally: app.dependency_overrides.clear()