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:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user