const Database = require('better-sqlite3'); const path = require('path'); const fs = require('fs'); const DATA_DIR = path.join(__dirname, '..', 'data'); if (!fs.existsSync(DATA_DIR)) fs.mkdirSync(DATA_DIR, { recursive: true }); const DB_PATH = path.join(DATA_DIR, 'journal.db'); const db = new Database(DB_PATH); db.pragma('journal_mode = WAL'); db.pragma('foreign_keys = ON'); db.exec(` CREATE TABLE IF NOT EXISTS classes ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE NOT NULL ); CREATE TABLE IF NOT EXISTS students ( id INTEGER PRIMARY KEY AUTOINCREMENT, full_name TEXT NOT NULL, class_id INTEGER NOT NULL, UNIQUE(full_name, class_id), FOREIGN KEY (class_id) REFERENCES classes(id) ); CREATE TABLE IF NOT EXISTS subjects ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE NOT NULL ); CREATE TABLE IF NOT EXISTS teachers ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE NOT NULL, subject_id INTEGER, FOREIGN KEY (subject_id) REFERENCES subjects(id) ); CREATE TABLE IF NOT EXISTS grades ( id INTEGER PRIMARY KEY AUTOINCREMENT, student_id INTEGER NOT NULL, subject_id INTEGER NOT NULL, teacher_id INTEGER, quarter TEXT NOT NULL, grade INTEGER, UNIQUE(student_id, subject_id, quarter), FOREIGN KEY (student_id) REFERENCES students(id), FOREIGN KEY (subject_id) REFERENCES subjects(id), FOREIGN KEY (teacher_id) REFERENCES teachers(id) ); CREATE TABLE IF NOT EXISTS daily_grades ( id INTEGER PRIMARY KEY AUTOINCREMENT, student_id INTEGER NOT NULL, subject_id INTEGER NOT NULL, teacher_id INTEGER, grade_date TEXT NOT NULL, grade INTEGER, symbol TEXT, FOREIGN KEY (student_id) REFERENCES students(id), FOREIGN KEY (subject_id) REFERENCES subjects(id), FOREIGN KEY (teacher_id) REFERENCES teachers(id) ); CREATE INDEX IF NOT EXISTS idx_daily_student ON daily_grades(student_id); CREATE INDEX IF NOT EXISTS idx_daily_subject ON daily_grades(subject_id); CREATE INDEX IF NOT EXISTS idx_daily_teacher ON daily_grades(teacher_id); CREATE INDEX IF NOT EXISTS idx_daily_date ON daily_grades(grade_date); CREATE TABLE IF NOT EXISTS lesson_plans ( id INTEGER PRIMARY KEY AUTOINCREMENT, subject_id INTEGER NOT NULL, teacher_id INTEGER, lesson_date TEXT NOT NULL, topic TEXT, homework TEXT, UNIQUE(subject_id, lesson_date), FOREIGN KEY (subject_id) REFERENCES subjects(id), FOREIGN KEY (teacher_id) REFERENCES teachers(id) ); CREATE INDEX IF NOT EXISTS idx_lp_subject ON lesson_plans(subject_id); CREATE INDEX IF NOT EXISTS idx_lp_teacher ON lesson_plans(teacher_id); CREATE INDEX IF NOT EXISTS idx_lp_date ON lesson_plans(lesson_date); `); const getClass = db.prepare(`SELECT id FROM classes WHERE name = ?`); const insertClass = db.prepare(`INSERT OR IGNORE INTO classes (name) VALUES (?)`); const getStudent = db.prepare(`SELECT id FROM students WHERE full_name = ? AND class_id = ?`); const insertStudent = db.prepare(`INSERT OR IGNORE INTO students (full_name, class_id) VALUES (?, ?)`); const getSubject = db.prepare(`SELECT id FROM subjects WHERE name = ?`); const insertSubject = db.prepare(`INSERT OR IGNORE INTO subjects (name) VALUES (?)`); const getTeacher = db.prepare(`SELECT id FROM teachers WHERE name = ? AND subject_id = ?`); const getTeacherNull = db.prepare(`SELECT id FROM teachers WHERE name = ? AND subject_id IS NULL`); const insertTeacher = db.prepare(`INSERT OR IGNORE INTO teachers (name, subject_id) VALUES (?, ?)`); const upsertGrade = db.prepare(` INSERT INTO grades (student_id, subject_id, teacher_id, quarter, grade) VALUES (?, ?, ?, ?, ?) ON CONFLICT(student_id, subject_id, quarter) DO UPDATE SET teacher_id = excluded.teacher_id, grade = excluded.grade `); function upsertClass(name) { insertClass.run(name); return getClass.get(name).id; } function upsertStudent(fullName, classId) { insertStudent.run(fullName, classId); return getStudent.get(fullName, classId).id; } function upsertSubject(name) { insertSubject.run(name); return getSubject.get(name).id; } function upsertTeacher(name, subjectId) { insertTeacher.run(name, subjectId || null); const row = subjectId ? getTeacher.get(name, subjectId) : getTeacherNull.get(name); return row ? row.id : null; } function upsertGradeValue(studentId, subjectId, teacherId, quarter, grade) { upsertGrade.run(studentId, subjectId, teacherId || null, quarter, grade === 'н/а' || grade === 'н' ? null : Number(grade) || null); } const insertDailyGrade = db.prepare(` INSERT INTO daily_grades (student_id, subject_id, teacher_id, grade_date, grade, symbol) VALUES (?, ?, ?, ?, ?, ?) `); function insertDailyGradeValue(studentId, subjectId, teacherId, gradeDate, gradeVal) { const num = /^[2-5]$/.test(gradeVal) ? parseInt(gradeVal) : null; const sym = /^[НБУнбу]$/.test(gradeVal) ? gradeVal.toUpperCase() : null; insertDailyGrade.run(studentId, subjectId, teacherId || null, gradeDate, num, sym); } const insertLessonPlan = db.prepare(` INSERT OR REPLACE INTO lesson_plans (subject_id, teacher_id, lesson_date, topic, homework) VALUES (?, ?, ?, ?, ?) `); function insertLessonPlanValue(subjectId, teacherId, lessonDate, topic, homework) { insertLessonPlan.run(subjectId, teacherId || null, lessonDate, topic || null, homework || null); } function clearAll() { db.exec('DELETE FROM lesson_plans; DELETE FROM daily_grades; DELETE FROM grades; DELETE FROM teachers; DELETE FROM students; DELETE FROM subjects; DELETE FROM classes;'); } module.exports = { db, upsertClass, upsertStudent, upsertSubject, upsertTeacher, upsertGradeValue, insertDailyGradeValue, insertLessonPlanValue, clearAll, DATA_DIR };