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:
@@ -1,4 +1,28 @@
|
||||
from pydantic import BaseModel, Field, model_validator
|
||||
from typing import Literal
|
||||
from uuid import uuid4
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
||||
|
||||
|
||||
class EvidenceTarget(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
target_id: str = Field(default_factory=lambda: uuid4().hex, min_length=1, max_length=50)
|
||||
label: str = Field(min_length=1, max_length=300)
|
||||
use: Literal["content", "structure"]
|
||||
allowed_page_types: list[Literal["cover", "abstract", "toc", "body", "appendix", "references"]] = Field(default_factory=list)
|
||||
query: str = ""
|
||||
|
||||
@model_validator(mode="after")
|
||||
def valid_sources(self):
|
||||
from .pages import CONTENT_PAGE_TYPES, STRUCTURE_PAGE_TYPES
|
||||
permitted = CONTENT_PAGE_TYPES if self.use == "content" else STRUCTURE_PAGE_TYPES
|
||||
if not self.label.strip() or not self.target_id.strip():
|
||||
raise ValueError("证据目标名称和标识不能为空")
|
||||
if not self.allowed_page_types:
|
||||
self.allowed_page_types = list(permitted)
|
||||
if any(value not in permitted for value in self.allowed_page_types):
|
||||
raise ValueError("证据目标的页面类型不能扩大用途权限")
|
||||
return self
|
||||
|
||||
|
||||
class ScoreLevel(BaseModel):
|
||||
@@ -15,11 +39,15 @@ class Criterion(BaseModel):
|
||||
description: str = Field(min_length=1)
|
||||
linked_objectives: list[str] = Field(default_factory=list)
|
||||
linked_outcomes: list[str] = Field(default_factory=list)
|
||||
required_evidence: list[str] = Field(default_factory=list)
|
||||
required_evidence: list[EvidenceTarget | str] = Field(default_factory=list)
|
||||
score_levels: list[ScoreLevel] = Field(default_factory=list)
|
||||
|
||||
@model_validator(mode="after")
|
||||
def check_levels(self):
|
||||
ids = [target.target_id if isinstance(target, EvidenceTarget) else f"t{index}"
|
||||
for index, target in enumerate(self.required_evidence) if not isinstance(target, str) or target.strip()]
|
||||
if len(ids) != len(set(ids)):
|
||||
raise ValueError("duplicate evidence target_id")
|
||||
if not self.score_levels:
|
||||
raise ValueError(f"{self.criterion_id}: score levels are required")
|
||||
ranges = sorted(self.score_levels, key=lambda x: x.min)
|
||||
|
||||
Reference in New Issue
Block a user