Files
WiseGrade/frontend/src/importReview.ts
T

67 lines
3.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const fields = ['student_no', 'name', 'topic'] as const;
type IdentityField = typeof fields[number];
const labels = { student_no: '学号', name: '姓名', topic: '选题' };
const categories: Record<string, string> = { stage1: '阶段报告1', stage2: '阶段报告2', stage3: '阶段报告3',
final_report: '最终报告', presentation: '汇报PPT/PDF' };
const text = (value: unknown) => String(value ?? '').trim();
const fileName = (file: any) => text(file.relative_path).split('/').pop() || '文件';
export function reviewImport(project: any) {
const selected = (project.files || []).filter((file: any) => file.status !== 'ignored');
const metadata = (project.files || []).some((file: any) => file.detected_identity != null);
const identity: Record<IdentityField, string> = { student_no: '', name: '', topic: '' };
const warnings: string[] = [];
for (const field of fields) {
const sources = new Map<string, string[]>();
for (const file of selected) {
const value = text(file.detected_identity?.[field]);
if (value) sources.set(value, [...(sources.get(value) || []), fileName(file)]);
}
const folderValue = text(project.folder_identity?.[field]);
const values = [...sources.keys()];
identity[field] = folderValue || (values.length === 1 ? values[0] : '');
if (values.length > 1) {
warnings.push(`${labels[field]}冲突:${values.map(value => `${value}(${sources.get(value)!.join('、')})`).join(';')}`);
} else if (folderValue && values.length === 1 && folderValue !== values[0]) {
warnings.push(`${labels[field]}与目录不一致:封面为${values[0]}(${sources.get(values[0])!.join('、')}),目录为${folderValue}`);
}
}
if (!metadata) warnings.push(...(project.warnings || []));
const errors: string[] = [];
if (!/^[0-9]{5,20}$/.test(text(project.student_no))) errors.push('学号必须为5–20位数字');
if (!text(project.name)) errors.push('请填写姓名');
if (!text(project.topic)) errors.push('请填写选题');
if (!selected.length) errors.push('至少选择1份资料');
const counts = new Map<string, number>();
for (const file of selected) {
if (!categories[file.category]) errors.push(`${fileName(file)}:请选择材料类型`);
else counts.set(file.category, (counts.get(file.category) || 0) + 1);
if (file.error) errors.push(`${fileName(file)}:无法读取,请修复文件或设为忽略`);
}
for (const [category, count] of counts) {
if (count > 1) errors.push(`${categories[category]}选了${count}份,请只保留1份,其余设为忽略`);
}
const missing = Object.keys(categories).filter(category => !selected.some((file: any) => file.category === category));
return { selected, metadata, identity, warnings, errors, missing };
}
export function fillImportIdentity(project: any, force = false) {
const review = reviewImport(project);
if (!review.metadata) return project;
for (const field of fields) {
if (force || !project.identity_edits?.[field]) project[field] = review.identity[field];
}
if (force) project.identity_edits = {};
return project;
}
export function importErrors(projects: any[] | undefined, versionId: number | undefined) {
const errors = versionId ? [] : ['请选择已发布课程版本'];
if (!projects?.length) return [...errors, '当前目录没有PDF、DOCX或PPTX资料,请重新扫描资料目录'];
for (const project of projects) {
errors.push(...reviewImport(project).errors.map(error => `${project.relative_dir}:${error}`));
}
return errors;
}