diff --git a/src/parser.js b/src/parser.js index 515cc5d..bc93d55 100644 --- a/src/parser.js +++ b/src/parser.js @@ -15,20 +15,28 @@ const HEADER_MAP = { 'код оо': 'school_code', 'серия': 'passport_series', 'номер': 'passport_number', + 'документ': 'document', 'первичный балл': 'primary_score', - 'оценка': 'grade' + 'оценка': 'grade', + 'тестовый балл': 'grade' }; function normalize(s) { return String(s || '').trim().toLowerCase(); } -function parseXls(filePath) { +function parseXls(filePath, originalName) { const workbook = XLSX.readFile(filePath); const sheetName = workbook.SheetNames[0]; const sheet = workbook.Sheets[sheetName]; const rows = XLSX.utils.sheet_to_json(sheet, { header: 1, defval: '' }); + let fileSubjectCode = null; + if (originalName) { + const m = originalName.match(/^(\d{2})_/); + if (m) fileSubjectCode = m[1]; + } + const parsedSubjects = []; const studentsMap = new Map(); const results = []; @@ -50,6 +58,9 @@ function parseXls(filePath) { const subjectInfo = detectSubject(firstCell); if (subjectInfo) { + if (!subjectInfo.code && fileSubjectCode) { + subjectInfo.code = fileSubjectCode; + } currentSubject = subjectInfo; parsedSubjects.push(subjectInfo); colIdx = null; @@ -106,14 +117,29 @@ function uniqSubjects(subjects) { function detectSubject(firstCell) { if (!firstCell) return null; - const match = firstCell.match(/^(\d{2})\s*-\s*(.+?)\s+(\d{4}\.\d{2}\.\d{2})$/); - if (!match) return null; - const code = match[1]; - const name = match[2]; - const date = match[3]; - if (SUBJECT_NAMES.some(sn => sn.toLowerCase() === name.toLowerCase())) { - return { code, name, date }; + + // Old format: "09 - Английский язык 2024.06.05" + const oldMatch = firstCell.match(/^(\d{2})\s*-\s*(.+?)\s+(\d{4}\.\d{2}\.\d{2})$/); + if (oldMatch) { + const code = oldMatch[1]; + const name = oldMatch[2]; + const date = oldMatch[3]; + if (SUBJECT_NAMES.some(sn => sn.toLowerCase() === name.toLowerCase())) { + return { code, name, date }; + } + return null; } + + // New format: "Английский язык п.: 2026.06.06, у.: 2026.06.06" + const newMatch = firstCell.match(/^(.+?)\s+п\.:\s*(\d{4}\.\d{2}\.\d{2})/); + if (newMatch) { + const name = newMatch[1].trim(); + const date = newMatch[2]; + if (SUBJECT_NAMES.some(sn => sn.toLowerCase() === name.toLowerCase())) { + return { code: null, name, date }; + } + } + return null; } @@ -149,6 +175,17 @@ function extractStudentData(row, colIdx) { const last_name = get('last_name'); if (!last_name || last_name.length < 2) return null; + let passport_series = get('passport_series'); + let passport_number = get('passport_number'); + if (!passport_series && !passport_number) { + const doc = get('document'); + if (doc) { + const parts = doc.split(/\s+/); + passport_series = parts[0] || ''; + passport_number = parts.slice(1).join('') || ''; + } + } + const toInt = (v) => { const n = parseInt(v, 10); return isNaN(n) ? null : n; @@ -160,8 +197,8 @@ function extractStudentData(row, colIdx) { patronymic: get('patronymic'), class: get('class'), school_code: get('school_code'), - passport_series: get('passport_series'), - passport_number: get('passport_number'), + passport_series, + passport_number, primary_score: toInt(get('primary_score')), grade: toInt(get('grade')) }; diff --git a/src/routes/api.js b/src/routes/api.js index b29fbaa..6b0d0ac 100644 --- a/src/routes/api.js +++ b/src/routes/api.js @@ -23,7 +23,7 @@ router.post('/import', upload.single('xlsfile'), (req, res) => { try { if (!req.file) return res.status(400).json({ error: 'Файл не загружен' }); - const parsed = parseXls(req.file.path); + const parsed = parseXls(req.file.path, req.file.originalname); const studentIdByKey = {}; dbBegin();