236 lines
8.7 KiB
JavaScript
236 lines
8.7 KiB
JavaScript
const express = require('express');
|
||
const { dbGet, dbAll } = require('../db');
|
||
|
||
const router = express.Router();
|
||
|
||
const requireAuth = (req, res, next) => {
|
||
if (!req.session.user) return res.redirect('/login');
|
||
next();
|
||
};
|
||
|
||
router.get('/login', (req, res) => {
|
||
if (req.session.user) return res.redirect('/');
|
||
res.render('login');
|
||
});
|
||
|
||
router.get('/', requireAuth, (req, res) => {
|
||
res.render('index', { user: req.session.user });
|
||
});
|
||
|
||
router.get('/student/:id', requireAuth, (req, res) => {
|
||
const student = dbGet('SELECT * FROM students WHERE id = ?', [req.params.id]);
|
||
if (!student) return res.status(404).send('Ученик не найден');
|
||
|
||
const results = dbAll(`
|
||
SELECT er.*, s.name as subject_name, s.is_mandatory, s.code as subject_code
|
||
FROM exam_results er
|
||
JOIN subjects s ON s.code = er.subject_code
|
||
WHERE er.student_id = ?
|
||
ORDER BY s.is_mandatory DESC, s.name
|
||
`, [req.params.id]);
|
||
|
||
const mandatoryResults = results.filter(r => r.is_mandatory);
|
||
const electiveResults = results.filter(r => !r.is_mandatory);
|
||
const rusResult = mandatoryResults.find(r => r.subject_code === '01') || null;
|
||
const mathResult = mandatoryResults.find(r => r.subject_code === '02') || null;
|
||
|
||
res.render('student', {
|
||
student,
|
||
electiveResults,
|
||
rusResult,
|
||
mathResult,
|
||
user: req.session.user
|
||
});
|
||
});
|
||
|
||
router.get('/import', requireAuth, (req, res) => {
|
||
res.render('import', { user: req.session.user });
|
||
});
|
||
|
||
router.get('/certificate/:id', requireAuth, (req, res) => {
|
||
const { id } = req.params;
|
||
const type = req.query.type || 'gia';
|
||
const subjectCodes = req.query.subjects ? req.query.subjects.split(',') : [];
|
||
|
||
const student = dbGet('SELECT * FROM students WHERE id = ?', [id]);
|
||
if (!student) return res.status(404).send('Ученик не найден');
|
||
|
||
const rusResult = dbGet(`
|
||
SELECT er.*, s.name as subject_name
|
||
FROM exam_results er JOIN subjects s ON s.code = er.subject_code
|
||
WHERE er.student_id = ? AND er.subject_code = '01'
|
||
`, [id]);
|
||
|
||
const mathResult = dbGet(`
|
||
SELECT er.*, s.name as subject_name
|
||
FROM exam_results er JOIN subjects s ON s.code = er.subject_code
|
||
WHERE er.student_id = ? AND er.subject_code = '02'
|
||
`, [id]);
|
||
|
||
let electives = [];
|
||
if (type === 'gia' && subjectCodes.length > 0) {
|
||
const placeholders = subjectCodes.map(() => '?').join(',');
|
||
electives = dbAll(`
|
||
SELECT er.*, s.name as subject_name
|
||
FROM exam_results er JOIN subjects s ON s.code = er.subject_code
|
||
WHERE er.student_id = ? AND er.subject_code IN (${placeholders})
|
||
`, [id, ...subjectCodes]);
|
||
}
|
||
|
||
const today = new Date();
|
||
const certNumber = `${today.getFullYear()}${String(id).padStart(4, '0')}`;
|
||
const months = ['января', 'февраля', 'марта', 'апреля', 'мая', 'июня',
|
||
'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря'];
|
||
const dateStr = `«${String(today.getDate()).padStart(2, '0')}» ${months[today.getMonth()]} ${today.getFullYear()} года`;
|
||
|
||
const gender = detectGender(student.patronymic);
|
||
const fioDative = formatNameDative(student, gender);
|
||
const verbEnding = gender === 'm' ? 'обучался' : 'обучалась';
|
||
const verbEnding2 = gender === 'm' ? 'получил' : 'получила';
|
||
|
||
res.render('certificate', {
|
||
type,
|
||
student: { ...student, fioDative, gender },
|
||
certNumber,
|
||
dateStr,
|
||
verbEnding,
|
||
verbEnding2,
|
||
rusResult: rusResult || null,
|
||
mathResult: mathResult || null,
|
||
electives,
|
||
allSubjects: type === 'gia'
|
||
? [
|
||
rusResult || { subject_name: 'Русский язык', primary_score: null, grade: null },
|
||
mathResult || { subject_name: 'Математика', primary_score: null, grade: null },
|
||
...electives
|
||
]
|
||
: [
|
||
rusResult || { subject_name: 'Русский язык', grade: null },
|
||
mathResult || { subject_name: 'Математика', grade: null }
|
||
]
|
||
});
|
||
});
|
||
|
||
function detectGender(patronymic) {
|
||
if (!patronymic) return 'm';
|
||
const last = patronymic.toLowerCase();
|
||
if (/вна|чна|нична$/.test(last)) return 'f';
|
||
return 'm';
|
||
}
|
||
|
||
function formatNameDative(student, gender) {
|
||
const l = student.last_name || '';
|
||
const f = student.first_name || '';
|
||
const p = student.patronymic || '';
|
||
if (gender === 'f') {
|
||
return `${declineFemaleLast(l)} ${declineFemaleFirst(f)} ${declineFemalePatr(p)}`;
|
||
}
|
||
return `${declineMaleLast(l)} ${declineMaleFirst(f)} ${declineMalePatr(p)}`;
|
||
}
|
||
|
||
function declineFemaleLast(name) {
|
||
if (!name) return '';
|
||
if (name.endsWith('ая')) return name.slice(0, -2) + 'ой';
|
||
if (name.endsWith('яя')) return name.slice(0, -2) + 'ей';
|
||
if (name.endsWith('а')) return name.slice(0, -1) + 'ой';
|
||
if (name.endsWith('я')) return name.slice(0, -1) + 'е';
|
||
if (name.endsWith('ова') || name.endsWith('ева') || name.endsWith('ина')) return name.slice(0, -1) + 'ой';
|
||
return name + 'ой';
|
||
}
|
||
function declineFemaleFirst(name) {
|
||
if (!name) return '';
|
||
if (name.endsWith('а') || name.endsWith('я')) return name.slice(0, -1) + 'е';
|
||
return name + 'е';
|
||
}
|
||
function declineFemalePatr(name) {
|
||
if (!name) return '';
|
||
if (name.endsWith('на')) return name.slice(0, -1) + 'е';
|
||
return name + 'е';
|
||
}
|
||
|
||
function declineMaleLast(name) {
|
||
if (!name) return '';
|
||
if (name.endsWith('ов') || name.endsWith('ев') || name.endsWith('ин') || name.endsWith('ёв')) return name + 'у';
|
||
if (name.endsWith('ий') || name.endsWith('ый')) return name.slice(0, -2) + 'ому';
|
||
if (name.endsWith('ой')) return name.slice(0, -2) + 'ому';
|
||
return name + 'у';
|
||
}
|
||
function declineMaleFirst(name) {
|
||
if (!name) return '';
|
||
if (name.endsWith('й') || name.endsWith('ь')) return name.slice(0, -1) + 'ю';
|
||
if (name.endsWith('а') || name.endsWith('я')) return name.slice(0, -1) + 'е';
|
||
return name + 'у';
|
||
}
|
||
function declineMalePatr(name) {
|
||
if (!name) return '';
|
||
if (name.endsWith('ич')) return name + 'у';
|
||
if (name.endsWith('на')) return name.slice(0, -1) + 'е';
|
||
return name + 'у';
|
||
}
|
||
|
||
router.get('/batch-print', requireAuth, (req, res) => {
|
||
const { class: cls, parallel, type } = req.query;
|
||
|
||
let students;
|
||
if (cls) {
|
||
students = dbAll('SELECT * FROM students WHERE class = ? ORDER BY last_name, first_name', [cls]);
|
||
} else if (parallel) {
|
||
students = dbAll('SELECT * FROM students WHERE class LIKE ? ORDER BY class, last_name, first_name', [parallel + '%']);
|
||
} else {
|
||
students = dbAll('SELECT * FROM students ORDER BY class, last_name, first_name');
|
||
}
|
||
|
||
const today = new Date();
|
||
const months = ['января', 'февраля', 'марта', 'апреля', 'мая', 'июня',
|
||
'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря'];
|
||
const dateStr = `«${String(today.getDate()).padStart(2, '0')}» ${months[today.getMonth()]} ${today.getFullYear()} года`;
|
||
|
||
const studentData = students.map(st => {
|
||
const results = dbAll(`
|
||
SELECT er.*, s.name as subject_name, s.is_mandatory
|
||
FROM exam_results er JOIN subjects s ON s.code = er.subject_code
|
||
WHERE er.student_id = ?
|
||
`, [st.id]);
|
||
|
||
const rusResult = results.find(r => r.subject_code === '01') || null;
|
||
const mathResult = results.find(r => r.subject_code === '02') || null;
|
||
const electives = results.filter(r => !r.is_mandatory);
|
||
|
||
// Автоопределение: элективы есть → ОГЭ, 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);
|
||
const fioDative = formatNameDative(st, gender);
|
||
|
||
let allSubjects;
|
||
if (finalType === 'gia') {
|
||
allSubjects = [
|
||
rusResult || { subject_name: 'Русский язык', primary_score: null, grade: null },
|
||
mathResult || { subject_name: 'Математика', primary_score: null, grade: null },
|
||
...electives
|
||
];
|
||
} else {
|
||
allSubjects = [
|
||
rusResult || { subject_name: 'Русский язык', grade: null },
|
||
mathResult || { subject_name: 'Математика', grade: null }
|
||
];
|
||
}
|
||
|
||
return {
|
||
student: { ...st, fioDative, gender },
|
||
certNumber: `${today.getFullYear()}${String(st.id).padStart(4, '0')}`,
|
||
dateStr,
|
||
verbEnding: gender === 'm' ? 'обучался' : 'обучалась',
|
||
verbEnding2: gender === 'm' ? 'получил' : 'получила',
|
||
allSubjects,
|
||
type: finalType
|
||
};
|
||
});
|
||
|
||
res.render('batch-certificate', { students: studentData });
|
||
});
|
||
|
||
module.exports = router;
|