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:
2026-10-14 13:52:56 +08:00
parent 29c95ce5ad
commit ebe33392ca
42 changed files with 2332 additions and 304 deletions
+22 -9
View File
@@ -46,9 +46,9 @@ def parse_student_folder(name: str):
def extract_identity(text: str):
patterns = {
"student_no": r"学号\s*[::]?\s*(\d{5,20})",
"name": r"姓名\s*[::]?\s*([^\s::]{2,10})",
"topic": r"(?:课题名称|项目名称|题目)\s*[::]?\s*([^\n\r]{3,100})",
"student_no": r"学\s*号\s*[::]?\s*(\d{5,20})",
"name": r"姓\s*名\s*[::]?\s*([^\s::]{2,10})",
"topic": r"(?:课\s*题\s*名\s*称|项\s*目\s*名\s*称|题\s*目)\s*[::]?\s*([^\n\r]{3,100})",
}
return {key: (match.group(1).strip() if (match := re.search(pattern, text)) else "") for key, pattern in patterns.items()}
@@ -103,14 +103,20 @@ def scan(relative: str, *, root: Path | None = None):
base = (root or get_settings().inbox_path).resolve()
path = secure_path(relative, root=base)
if not path.is_dir():
raise ValueError("请选择学生总目录")
raise ValueError("请选择学生总目录或资料目录")
projects = []
for folder in sorted(path.iterdir()):
if not folder.is_dir() or folder.is_symlink():
continue
def has_materials(folder):
return any(item.is_file() and not item.is_symlink() and item.suffix.lower() in SUPPORTED
for item in folder.iterdir())
folders = ([path] if has_materials(path) else []) + [
item for item in sorted(path.iterdir())
if item.is_dir() and not item.is_symlink() and has_materials(item)]
for folder in folders:
info = parse_student_folder(folder.name)
files = []
extracted = {}
identity_values = {}
warnings = []
for candidate in sorted(folder.iterdir()):
if not candidate.is_file() or candidate.is_symlink():
@@ -123,8 +129,8 @@ def scan(relative: str, *, root: Path | None = None):
category, status = classify(candidate, text)
identity = extract_identity(text)
for key, value in identity.items():
if value and key not in extracted:
extracted[key] = value
if value:
identity_values.setdefault(key, set()).add(value)
except ValueError as exc:
category, status, error = "unknown", "unparseable", str(exc)
stat = candidate.stat()
@@ -137,9 +143,16 @@ def scan(relative: str, *, root: Path | None = None):
if len(matches) > 1:
for file in matches:
file["status"] = "duplicate"
for key, values in identity_values.items():
if len(values) > 1:
warnings.append(f"不同材料封面的{key}不一致,请核对后填写")
else:
extracted[key] = next(iter(values))
for key, value in extracted.items():
if info.get(key) and value != info[key]:
warnings.append(f"封面{key}“{value}”与目录名“{info[key]}”不一致")
elif not info.get(key):
info[key] = value
if warnings:
info["status"] = "needs_confirmation"
info.update({"relative_dir": folder.relative_to(base).as_posix(), "files": files,