be4d43105e
Node.js/Express backend + vanilla JS frontend. Features: real-time collaborative whiteboard (SSE), multi-page support, LaTeX formulas, shapes/connectors, coordinate systems, number lines, compass, zoom/pan, Catmull-Rom pencil smoothing, ruler/protractor with rotation & resize controls, minimap navigation overlay, auto-measurements, multi-page thumbnails sidebar, PNG export, page templates. Student/teacher workflows: classes, assignments, library, dashboard. Mobile responsive. SQLite (better-sqlite3). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
81 lines
3.0 KiB
JavaScript
81 lines
3.0 KiB
JavaScript
const db = require('../db/db');
|
|
|
|
function teacherOverview(req, res) {
|
|
const user = req.user;
|
|
const classId = req.query.classId ? Number(req.query.classId) : null;
|
|
|
|
const classes = user.role === 'admin'
|
|
? db.prepare('SELECT id, name FROM classes ORDER BY name').all()
|
|
: db.prepare('SELECT id, name FROM classes WHERE teacher_id = ? ORDER BY name').all(user.id);
|
|
|
|
if (!classId) return res.json({ classes, data: null });
|
|
|
|
if (user.role !== 'admin') {
|
|
const cls = db.prepare('SELECT id FROM classes WHERE id = ? AND teacher_id = ?').get(classId, user.id);
|
|
if (!cls) return res.status(403).json({ error: 'Forbidden' });
|
|
}
|
|
|
|
const overview = db.prepare(`
|
|
SELECT
|
|
COUNT(DISTINCT cm.user_id) AS students,
|
|
COUNT(DISTINCT CASE WHEN ts.status='completed' THEN ts.id END) AS sessions,
|
|
ROUND(AVG(CASE WHEN ts.status='completed' AND ts.total>0
|
|
THEN ts.score*100.0/ts.total END), 1) AS avgScore
|
|
FROM class_members cm
|
|
LEFT JOIN test_sessions ts ON ts.user_id = cm.user_id
|
|
WHERE cm.class_id = ?
|
|
`).get(classId);
|
|
|
|
const scoreByWeek = db.prepare(`
|
|
SELECT
|
|
strftime('%Y-W%W', ts.finished_at) AS week,
|
|
ROUND(AVG(ts.score*100.0/ts.total), 1) AS avg,
|
|
COUNT(*) AS sessions
|
|
FROM test_sessions ts
|
|
JOIN class_members cm ON cm.user_id = ts.user_id AND cm.class_id = ?
|
|
WHERE ts.status='completed' AND ts.total>0
|
|
AND ts.finished_at >= datetime('now','-56 days')
|
|
GROUP BY week ORDER BY week
|
|
`).all(classId);
|
|
|
|
const hardQuestions = db.prepare(`
|
|
SELECT q.id, q.text, q.difficulty,
|
|
COUNT(ua.id) AS attempts,
|
|
ROUND(SUM(CASE WHEN ua.is_correct=0 THEN 1.0 ELSE 0 END)*100/COUNT(ua.id),1) AS errorRate,
|
|
t.name AS topic
|
|
FROM user_answers ua
|
|
JOIN questions q ON q.id = ua.question_id
|
|
JOIN test_sessions ts ON ts.id = ua.session_id
|
|
JOIN class_members cm ON cm.user_id = ts.user_id AND cm.class_id = ?
|
|
LEFT JOIN topics t ON t.id = q.topic_id
|
|
WHERE ts.status='completed'
|
|
GROUP BY q.id HAVING attempts >= 3
|
|
ORDER BY errorRate DESC LIMIT 10
|
|
`).all(classId);
|
|
|
|
const heatmap = db.prepare(`
|
|
SELECT date(ts.started_at) AS day,
|
|
COUNT(DISTINCT ts.user_id) AS students,
|
|
COUNT(ts.id) AS sessions
|
|
FROM test_sessions ts
|
|
JOIN class_members cm ON cm.user_id = ts.user_id AND cm.class_id = ?
|
|
WHERE ts.started_at >= datetime('now','-90 days')
|
|
GROUP BY day ORDER BY day
|
|
`).all(classId);
|
|
|
|
const assignments = db.prepare(`
|
|
SELECT a.id, a.title, a.deadline,
|
|
COUNT(DISTINCT cm.user_id) AS total,
|
|
COUNT(DISTINCT CASE WHEN ass.session_id IS NOT NULL THEN ass.user_id END) AS done
|
|
FROM assignments a
|
|
JOIN class_members cm ON cm.class_id = a.class_id
|
|
LEFT JOIN assignment_sessions ass ON ass.assignment_id=a.id AND ass.user_id=cm.user_id
|
|
WHERE a.class_id = ?
|
|
GROUP BY a.id ORDER BY a.created_at DESC LIMIT 10
|
|
`).all(classId);
|
|
|
|
res.json({ classes, overview, scoreByWeek, hardQuestions, heatmap, assignments });
|
|
}
|
|
|
|
module.exports = { teacherOverview };
|