fix: revalidate review context and serialize index retry
This commit is contained in:
@@ -175,6 +175,17 @@ def test_fixed_batch_membership_duplicate_queue_and_review(db, monkeypatch, sess
|
||||
"comment": "人工核定", "reason": "核查纸质材料"}, "给 3 分")
|
||||
action_id = prepared["pending_action"]["id"]
|
||||
assert score.teacher_score is None
|
||||
student = db.get(Student, first.student_id)
|
||||
student.name = "身份变化"
|
||||
db.commit()
|
||||
with pytest.raises(HTTPException):
|
||||
confirm(db, conversation, action_id)
|
||||
assert score.teacher_score is None
|
||||
student.name = "学生4"
|
||||
db.commit()
|
||||
prepared = execute(db, conversation, "prepare_review", {"score_id": score.id, "score": 3,
|
||||
"comment": "人工核定", "reason": "核查纸质材料"}, "给 3 分")
|
||||
action_id = prepared["pending_action"]["id"]
|
||||
confirm(db, conversation, action_id)
|
||||
db.refresh(score)
|
||||
assert score.teacher_score == 3 and score.model_score is None and score.status == "reviewed"
|
||||
|
||||
@@ -101,15 +101,27 @@ def _state(db, kind, payload):
|
||||
"rubric": digest({"criteria": rubric.criteria, "weights": rubric.weights}), "project_ids": projects}
|
||||
if kind == "start_batch":
|
||||
batch = services.found(db, ScoringBatch, payload["batch_id"])
|
||||
version = services.found(db, CourseVersion, batch.course_version_id)
|
||||
rubric = services.found(db, RubricVersion, batch.rubric_version_id)
|
||||
if not version.published or not rubric.published or rubric.course_version_id != version.id:
|
||||
raise HTTPException(409, "课程或规则发布状态已变化,请重新预览")
|
||||
return {"status": batch.status, "project_ids": batch.project_ids,
|
||||
"snapshot": digest(batch.rubric_snapshot)}
|
||||
if kind == "review":
|
||||
score = services.found(db, CriterionScore, payload["score_id"])
|
||||
batch = services.found(db, ScoringBatch, score.batch_id)
|
||||
project = services.found(db, Project, score.project_id)
|
||||
if project.course_version_id != batch.course_version_id or batch.project_ids is not None and project.id not in batch.project_ids:
|
||||
raise HTTPException(409, "评分项与批次项目关系已变化,请重新预览")
|
||||
if not any(item["criterion_id"] == score.criterion_id for item in batch.rubric_snapshot["criteria"]):
|
||||
raise HTTPException(409, "评分项不属于批次规则快照")
|
||||
student = services.found(db, Student, project.student_id)
|
||||
evidence = db.scalars(select(ScoreEvidence).where(ScoreEvidence.score_id == score.id).order_by(ScoreEvidence.id)).all()
|
||||
return {"status": score.status, "model_score": score.model_score,
|
||||
"teacher_score": score.teacher_score, "teacher_comment": score.teacher_comment,
|
||||
"error": score.error, "batch_id": score.batch_id, "project_id": score.project_id,
|
||||
"criterion_id": score.criterion_id,
|
||||
"student_id": student.id, "student_no": student.student_no, "student_name": student.name,
|
||||
"criterion_id": score.criterion_id, "rubric_snapshot": digest(batch.rubric_snapshot),
|
||||
"evidence": digest([[item.id, item.page, item.quote] for item in evidence])}
|
||||
if kind == "export":
|
||||
batch = services.found(db, ScoringBatch, payload["batch_id"])
|
||||
@@ -235,7 +247,11 @@ def confirm(db, conversation: Conversation, action_id: int):
|
||||
if action.kind == "import" and action.preview.get("requires_edit"):
|
||||
raise HTTPException(409, "导入候选存在歧义,请教师修正文件选择并更新预览")
|
||||
expires = action.expires_at.replace(tzinfo=timezone.utc) if action.expires_at.tzinfo is None else action.expires_at
|
||||
if expires <= now() or _state(db, action.kind, action.payload) != action.expected:
|
||||
try:
|
||||
current = _state(db, action.kind, action.payload) if expires > now() else None
|
||||
except (HTTPException, ValueError):
|
||||
current = None
|
||||
if current != action.expected:
|
||||
action.status = "stale"
|
||||
db.commit()
|
||||
raise HTTPException(409, "目标或文件已变化,请重新预览")
|
||||
|
||||
@@ -137,8 +137,11 @@ def enqueue_index_task(db, task_id: int):
|
||||
|
||||
|
||||
def retry_index_task(db, source_id: int):
|
||||
source = found(db, IndexTask, source_id)
|
||||
source = db.scalar(select(IndexTask).where(IndexTask.id == source_id).with_for_update())
|
||||
if not source:
|
||||
raise HTTPException(404, "索引任务不存在")
|
||||
if source.status != "failed":
|
||||
db.rollback()
|
||||
raise HTTPException(409, "仅失败的索引任务可重试")
|
||||
existing = db.scalar(select(IndexTask).where(IndexTask.id > source.id,
|
||||
IndexTask.course_version_id == source.course_version_id,
|
||||
@@ -146,6 +149,7 @@ def retry_index_task(db, source_id: int):
|
||||
IndexTask.kind == source.kind, IndexTask.status.in_(["queued", "running", "completed"]))
|
||||
.order_by(IndexTask.id.desc()))
|
||||
if existing:
|
||||
db.commit()
|
||||
return existing
|
||||
retry = IndexTask(course_version_id=source.course_version_id, submission_file_id=source.submission_file_id,
|
||||
kind=source.kind, status="queued")
|
||||
|
||||
Reference in New Issue
Block a user