feat: separate contents, abstract and body evidence
This commit is contained in:
@@ -11,6 +11,32 @@ from .knowledge_pages import evidence_links
|
||||
from .models import (CriterionScore, ExportTask, Project, ScoreEvidence, ScoringBatch, Student, SubmissionFile)
|
||||
from .rules import totals
|
||||
|
||||
#: Teacher-facing wording for a criterion status, shared by exports and the API.
|
||||
STATUS_LABELS = {
|
||||
"pending": "待评分",
|
||||
"scored": "模型已评分",
|
||||
"reviewed": "教师已复核",
|
||||
"needs_review": "待教师复核",
|
||||
"unscorable": "材料缺失或解析失败",
|
||||
"failed": "模型调用失败",
|
||||
}
|
||||
PAGE_TYPE_LABELS = {
|
||||
"cover": "封面", "abstract": "摘要", "toc": "目录", "body": "正文",
|
||||
"appendix": "附录", "references": "参考文献", "unknown": "识别不确定",
|
||||
"mixed": "目录与正文混合页",
|
||||
}
|
||||
#: Batch status wording. ``review_required`` means grading finished but some criteria
|
||||
#: still need a teacher decision, which is not the same as an incomplete batch.
|
||||
BATCH_STATUS_LABELS = {
|
||||
"created": "待启动", "queued": "排队中", "running": "运行中",
|
||||
"completed": "已完成", "review_required": "已完成,有待复核项",
|
||||
"partial": "部分完成", "failed": "失败",
|
||||
}
|
||||
|
||||
|
||||
def page_type_label(value) -> str:
|
||||
return PAGE_TYPE_LABELS.get(str(value or "unknown"), "未分类页面")
|
||||
|
||||
|
||||
def project_result(db, batch: ScoringBatch, project: Project):
|
||||
scores = db.scalars(select(CriterionScore).where(CriterionScore.batch_id == batch.id,
|
||||
@@ -19,10 +45,15 @@ def project_result(db, batch: ScoringBatch, project: Project):
|
||||
criteria = batch.rubric_snapshot["criteria"]
|
||||
totals_ = totals({key: score.final_score for key, score in by_id.items()}, criteria, batch.rubric_snapshot["weights"])
|
||||
rows = []
|
||||
def evidence_dict(item):
|
||||
links = evidence_links(db, item)
|
||||
def evidence_dict(item, links):
|
||||
return {"page": item.page, "quote": item.quote, "file_id": item.submission_file_id,
|
||||
"document_id": item.document_id,
|
||||
"target_id": item.target_id or "", "target_label": item.target_label or "",
|
||||
"page_type": item.page_type or links.get("page_type") or "unknown",
|
||||
"source_role": item.source_role or "content",
|
||||
# A quotation saved before the evidence policy existed is labelled as a
|
||||
# historical citation and never presented as report body evidence.
|
||||
"historical": bool(item.historical),
|
||||
"document": db.get(SubmissionFile, item.submission_file_id).relative_path if item.submission_file_id else "课程资料",
|
||||
**links}
|
||||
for criterion in criteria:
|
||||
@@ -35,7 +66,8 @@ def project_result(db, batch: ScoringBatch, project: Project):
|
||||
"model_comment": score.model_comment if score else "", "teacher_comment": score.teacher_comment if score else None,
|
||||
"missing_evidence": score.missing_evidence if score else [], "suggestion": score.suggestion if score else "",
|
||||
"status": score.status if score else "pending", "error": score.error if score else "",
|
||||
"evidence": [evidence_dict(item) for item in evidence]})
|
||||
"evidence_gaps": (score.evidence_gaps or []) if score else [],
|
||||
"evidence": [evidence_dict(item, evidence_links(db, item)) for item in evidence]})
|
||||
student = db.get(Student, project.student_id)
|
||||
return {"project_id": project.id, "student_no": student.student_no, "name": student.name, "topic": project.topic,
|
||||
"totals": totals_, "rows": rows, "facts": project.facts}
|
||||
@@ -97,14 +129,21 @@ def _html_report(result, batch):
|
||||
for row in result["rows"]:
|
||||
criterion = row["criterion"]
|
||||
evidences = "".join(
|
||||
f"<li>{esc(e['document'])} · 第{e['page']}{'张幻灯片' if str(e['document']).lower().endswith('.pptx') else '页'}:{esc(e['quote'])}</li>"
|
||||
f"<li>{esc(e['document'])} · 第{e['page']}{'张幻灯片' if str(e['document']).lower().endswith('.pptx') else '页'}"
|
||||
f"({esc(page_type_label(e.get('page_type')))}{',历史评分引用' if e.get('historical') else ''})"
|
||||
f"{' 支持:' + esc(e['target_label']) if e.get('target_label') else ''}:{esc(e['quote'])}</li>"
|
||||
for e in row["evidence"]
|
||||
)
|
||||
missing = "、".join(row["missing_evidence"] or [])
|
||||
gaps = ";".join(f"{gap.get('target_label')}(检索范围:{'、'.join(page_type_label(t) for t in gap.get('searched_page_types') or []) or '正文'})"
|
||||
for gap in row.get("evidence_gaps") or [])
|
||||
review = "本项缺少正文证据,已转教师复核。" if row["status"] == "needs_review" else ""
|
||||
rows.append(f"<section><h3>{esc(criterion['name'])}({esc(row['final_score'])} / {criterion['max_score']})</h3>"
|
||||
f"<p>模型原始分:{esc(row['model_score'])};教师修改分:{esc(row['teacher_score'])};状态:{esc(row['status'])}</p>"
|
||||
f"<p>模型原始分:{esc(row['model_score'])};教师修改分:{esc(row['teacher_score'])};状态:{esc(STATUS_LABELS.get(row['status'], row['status']))}</p>"
|
||||
f"<p>模型评语:{esc(row['model_comment'])}</p><p>教师评语:{esc(row['teacher_comment'])}</p>"
|
||||
f"<p>缺失证据:{esc(missing)}</p><p>建议:{esc(row['suggestion'])}</p><p>异常:{esc(row['error'])}</p>"
|
||||
f"<p>{esc(review)}</p>"
|
||||
f"<p>缺失证据:{esc(missing)}</p><p>缺正文证据的目标:{esc(gaps)}</p>"
|
||||
f"<p>建议:{esc(row['suggestion'])}</p><p>异常:{esc(row['error'])}</p>"
|
||||
f"<ul>{evidences}</ul></section>")
|
||||
totals_ = result["totals"]
|
||||
return f"""<!doctype html><html lang="zh"><meta charset="utf-8"><style>
|
||||
|
||||
Reference in New Issue
Block a user