fix: improve import, evidence indexing and material-scoped grading
Preserve document revisions and evidence, harden indexing and export behavior, add explicit material selection and direct-folder import, validate model JSON schemas with safe error messages, and retain student selection while loading scoring data.
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
"""Explicit batch membership regressions, isolated from real grading and credentials."""
|
||||
import pytest
|
||||
from fastapi import HTTPException
|
||||
from pydantic import ValidationError
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from wisegrade import api, services
|
||||
from wisegrade.db import Base
|
||||
from wisegrade.models import CourseVersion, Project, ScoringBatch, Student
|
||||
from wisegrade.seed import seed_initial
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def batch_db(environment):
|
||||
engine = create_engine("sqlite://")
|
||||
Base.metadata.create_all(engine)
|
||||
with Session(engine, expire_on_commit=False) as db:
|
||||
seed_initial(db)
|
||||
students = [Student(student_no=f"900000000{i}", name=f"测试{i}") for i in (1, 2, 3)]
|
||||
db.add_all(students)
|
||||
db.flush()
|
||||
other = CourseVersion(course_id=1, version=2, title="另一课程版本", published=True,
|
||||
objectives=[], indicators=[], directions=[], prompts={}, templates=[])
|
||||
db.add(other)
|
||||
db.flush()
|
||||
db.add_all([Project(student_id=students[0].id, course_version_id=1, topic="项目甲", source_dir="a"),
|
||||
Project(student_id=students[1].id, course_version_id=1, topic="项目乙", source_dir="b"),
|
||||
Project(student_id=students[2].id, course_version_id=other.id, topic="其他版本", source_dir="c")])
|
||||
db.commit()
|
||||
yield db
|
||||
engine.dispose()
|
||||
|
||||
|
||||
def test_select_one_project_and_preserve_batch_membership(batch_db):
|
||||
data = api.BatchInput(course_version_id=1, rubric_version_id=1, project_ids=[2])
|
||||
created = api.create_batch(data, batch_db)
|
||||
batch = batch_db.get(ScoringBatch, created["id"])
|
||||
assert batch.project_ids == [2]
|
||||
# Importing another project later cannot change this batch's selected membership.
|
||||
batch_db.add(Project(student_id=1, course_version_id=1, topic="后来导入", source_dir="later"))
|
||||
batch_db.commit()
|
||||
assert api.batch_progress(batch.id, batch_db)["project_ids"] == [2]
|
||||
|
||||
|
||||
def test_omitted_selection_preserves_existing_all_projects_api(batch_db):
|
||||
created = api.create_batch(api.BatchInput(course_version_id=1, rubric_version_id=1), batch_db)
|
||||
assert batch_db.get(ScoringBatch, created["id"]).project_ids == [1, 2]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("ids", [[999], [3], [2, 2]])
|
||||
def test_reject_unknown_cross_version_and_duplicate_project_ids(batch_db, ids):
|
||||
with pytest.raises(HTTPException) as exc:
|
||||
api.create_batch(api.BatchInput(course_version_id=1, rubric_version_id=1, project_ids=ids), batch_db)
|
||||
assert exc.value.status_code == 400
|
||||
assert batch_db.query(ScoringBatch).count() == 0
|
||||
|
||||
|
||||
def test_empty_explicit_selection_cannot_silently_grade_everyone():
|
||||
with pytest.raises(ValidationError):
|
||||
api.BatchInput(course_version_id=1, rubric_version_id=1, project_ids=[])
|
||||
|
||||
|
||||
def test_batch_list_uses_saved_human_readable_version_metadata(batch_db):
|
||||
created = api.create_batch(api.BatchInput(course_version_id=1, rubric_version_id=1, project_ids=[1]), batch_db)
|
||||
batch = batch_db.get(ScoringBatch, created["id"])
|
||||
batch.course_snapshot = {**batch.course_snapshot, "version": 7, "title": "创建时课程"}
|
||||
batch.rubric_snapshot = {**batch.rubric_snapshot, "version": 9}
|
||||
batch_db.commit()
|
||||
listed = api.list_batches(batch_db)[0]
|
||||
assert listed["course_version"] == 7 and listed["course_title"] == "创建时课程"
|
||||
assert listed["rubric_version"] == 9 and listed["criterion_count"] == 22
|
||||
Reference in New Issue
Block a user