v3
This commit is contained in:
48
src/db.js
48
src/db.js
@@ -79,6 +79,10 @@ async function initDb() {
|
||||
db.run('INSERT OR IGNORE INTO subjects (code, name, is_mandatory) VALUES (?, ?, 1)', ['02', 'Математика']);
|
||||
|
||||
saveDb();
|
||||
|
||||
// Миграция: перенос Русского/Математики в обязательные слоты 01/02, если они пусты
|
||||
migrateMandatorySubjects();
|
||||
|
||||
console.log('БД инициализирована');
|
||||
return db;
|
||||
}
|
||||
@@ -90,6 +94,50 @@ function saveDb() {
|
||||
fs.writeFileSync(DB_PATH, buffer);
|
||||
}
|
||||
|
||||
function migrateMandatorySubjects() {
|
||||
if (!db) return;
|
||||
|
||||
// Найти русский язык с кодом ≠ 01, где слот 01 пуст (нет grade)
|
||||
const rusRows = dbAll(`
|
||||
SELECT DISTINCT er.student_id, er.subject_code, er.primary_score, er.grade
|
||||
FROM exam_results er
|
||||
JOIN subjects s ON s.code = er.subject_code
|
||||
WHERE s.name = 'Русский язык' AND er.subject_code != '01'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM exam_results er2
|
||||
WHERE er2.student_id = er.student_id AND er2.subject_code = '01' AND er2.grade IS NOT NULL
|
||||
)
|
||||
`);
|
||||
for (const row of rusRows) {
|
||||
dbRun('INSERT OR REPLACE INTO exam_results (student_id, subject_code, primary_score, grade) VALUES (?, ?, ?, ?)', [row.student_id, '01', row.primary_score, row.grade]);
|
||||
dbRun('DELETE FROM exam_results WHERE student_id = ? AND subject_code = ?', [row.student_id, row.subject_code]);
|
||||
}
|
||||
|
||||
// Найти математику с кодом ≠ 02, где слот 02 пуст
|
||||
const mathRows = dbAll(`
|
||||
SELECT DISTINCT er.student_id, er.subject_code, er.primary_score, er.grade
|
||||
FROM exam_results er
|
||||
JOIN subjects s ON s.code = er.subject_code
|
||||
WHERE s.name = 'Математика' AND er.subject_code != '02'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM exam_results er2
|
||||
WHERE er2.student_id = er.student_id AND er2.subject_code = '02' AND er2.grade IS NOT NULL
|
||||
)
|
||||
`);
|
||||
for (const row of mathRows) {
|
||||
dbRun('INSERT OR REPLACE INTO exam_results (student_id, subject_code, primary_score, grade) VALUES (?, ?, ?, ?)', [row.student_id, '02', row.primary_score, row.grade]);
|
||||
dbRun('DELETE FROM exam_results WHERE student_id = ? AND subject_code = ?', [row.student_id, row.subject_code]);
|
||||
}
|
||||
|
||||
// Удалить лишние subjects (не 01/02) для русского и математики, если они без результатов
|
||||
dbRun(`DELETE FROM subjects WHERE (name = 'Русский язык' AND code != '01') OR (name = 'Математика' AND code != '02')`);
|
||||
|
||||
if (rusRows.length > 0 || mathRows.length > 0) {
|
||||
console.log(`Миграция: перенесено в обязательные — русский: ${rusRows.length}, математика: ${mathRows.length}`);
|
||||
saveDb();
|
||||
}
|
||||
}
|
||||
|
||||
function getDb() {
|
||||
if (!db) throw new Error('БД не инициализирована.');
|
||||
return db;
|
||||
|
||||
@@ -49,7 +49,18 @@ router.post('/import', upload.single('xlsfile'), (req, res) => {
|
||||
for (const r of parsed.results) {
|
||||
const sid = studentIdByKey[r.student_key.toLowerCase()];
|
||||
if (sid) {
|
||||
dbRun('INSERT OR REPLACE INTO exam_results (student_id, subject_code, primary_score, grade) VALUES (?, ?, ?, ?)', [sid, r.subject_code, r.primary_score, r.grade]);
|
||||
let code = r.subject_code;
|
||||
const name = (r.subject_name || '').toLowerCase();
|
||||
|
||||
if (name === 'русский язык') {
|
||||
const existing = dbGet("SELECT grade FROM exam_results WHERE student_id = ? AND subject_code = '01'", [sid]);
|
||||
if (!existing || existing.grade === null) code = '01';
|
||||
} else if (name === 'математика') {
|
||||
const existing = dbGet("SELECT grade FROM exam_results WHERE student_id = ? AND subject_code = '02'", [sid]);
|
||||
if (!existing || existing.grade === null) code = '02';
|
||||
}
|
||||
|
||||
dbRun('INSERT OR REPLACE INTO exam_results (student_id, subject_code, primary_score, grade) VALUES (?, ?, ?, ?)', [sid, code, r.primary_score, r.grade]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,6 +115,20 @@ router.get('/students', (req, res) => {
|
||||
havingClause = 'HAVING elective_count = 0';
|
||||
} else if (filter === 'nodata') {
|
||||
havingClause = 'HAVING mandatory_grades < 2';
|
||||
} else if (filter === 'conflict') {
|
||||
whereClauses.push(`s.id IN (
|
||||
SELECT DISTINCT er1.student_id FROM exam_results er1
|
||||
JOIN subjects s1 ON s1.code = er1.subject_code
|
||||
WHERE s1.name = 'Русский язык' AND er1.subject_code != '01'
|
||||
AND EXISTS (SELECT 1 FROM exam_results er2 WHERE er2.student_id = er1.student_id AND er2.subject_code = '01')
|
||||
UNION
|
||||
SELECT DISTINCT er1.student_id FROM exam_results er1
|
||||
JOIN subjects s1 ON s1.code = er1.subject_code
|
||||
WHERE s1.name = 'Математика' AND er1.subject_code != '02'
|
||||
AND EXISTS (SELECT 1 FROM exam_results er2 WHERE er2.student_id = er1.student_id AND er2.subject_code = '02')
|
||||
)`);
|
||||
} else if (filter === 'secondyear') {
|
||||
havingClause = 'HAVING elective_count = 0 AND COUNT(er.id) = 1';
|
||||
}
|
||||
|
||||
const wherePart = whereClauses.length ? 'WHERE ' + whereClauses.join(' AND ') : '';
|
||||
|
||||
@@ -196,8 +196,9 @@ router.get('/batch-print', requireAuth, (req, res) => {
|
||||
const mathResult = results.find(r => r.subject_code === '02') || null;
|
||||
const electives = results.filter(r => !r.is_mandatory);
|
||||
|
||||
// Автоопределение: если есть элективы — ОГЭ, иначе ГВЭ; ручной type переопределяет
|
||||
const autoType = electives.length > 0 ? 'gia' : 'gve';
|
||||
// Автоопределение: элективы есть → ОГЭ, 1 предмет всего → второгодник ОГЭ, иначе ГВЭ
|
||||
const totalResults = results.length;
|
||||
const autoType = (electives.length > 0 || totalResults === 1) ? 'gia' : 'gve';
|
||||
const finalType = (type === 'gia' || type === 'gve') ? type : autoType;
|
||||
|
||||
const gender = detectGender(st.patronymic);
|
||||
|
||||
@@ -57,6 +57,12 @@
|
||||
<label class="filter-label filter-nodata">
|
||||
<input type="radio" name="filter" value="nodata" onchange="onFilterChange()"> Нет данных
|
||||
</label>
|
||||
<label class="filter-label" style="background:#ffe0e0;color:#900">
|
||||
<input type="radio" name="filter" value="conflict" onchange="onFilterChange()"> Конфликт
|
||||
</label>
|
||||
<label class="filter-label" style="background:#fff0d0;color:#860">
|
||||
<input type="radio" name="filter" value="secondyear" onchange="onFilterChange()"> Второгодники
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="search-bar">
|
||||
@@ -151,10 +157,12 @@
|
||||
function renderTable(students) {
|
||||
const tbody = document.querySelector('#students-table tbody');
|
||||
tbody.innerHTML = students.map((s, i) => {
|
||||
const hasMandatory = (s.mandatory_grades || 0) >= 2;
|
||||
const isSecondYear = (s.elective_count || 0) === 0 && s.exam_count === 1;
|
||||
const hasMandatory = isSecondYear ? (s.mandatory_grades || 0) >= 1 : (s.mandatory_grades || 0) >= 2;
|
||||
const rowClass = !hasMandatory ? 'row-warning' : '';
|
||||
const examType = (s.elective_count || 0) > 0 ? 'ОГЭ' : 'ГВЭ';
|
||||
const examTypeClass = (s.elective_count || 0) > 0 ? 'badge-gia' : 'badge-gve';
|
||||
const isOge = (s.elective_count || 0) > 0 || isSecondYear;
|
||||
const examType = isOge ? 'ОГЭ' : 'ГВЭ';
|
||||
const examTypeClass = isOge ? 'badge-gia' : 'badge-gve';
|
||||
return `
|
||||
<tr class="${rowClass}">
|
||||
<td>${i + 1}</td>
|
||||
|
||||
Reference in New Issue
Block a user