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>
66 lines
1.8 KiB
JavaScript
66 lines
1.8 KiB
JavaScript
'use strict';
|
|
const db = require('../db/db');
|
|
|
|
/* ── GET /api/knowledge-map?subject_slug=bio ──────────────────────────── */
|
|
function getMap(req, res) {
|
|
const { subject_slug } = req.query;
|
|
const uid = req.user.id;
|
|
|
|
let subjects;
|
|
if (subject_slug) {
|
|
subjects = db.prepare('SELECT id, slug, name, icon FROM subjects WHERE slug = ?').all(subject_slug);
|
|
} else {
|
|
subjects = db.prepare('SELECT id, slug, name, icon FROM subjects ORDER BY name').all();
|
|
}
|
|
|
|
const nodes = [];
|
|
const links = [];
|
|
|
|
for (const subj of subjects) {
|
|
nodes.push({
|
|
id: `subj_${subj.id}`,
|
|
type: 'subject',
|
|
label: subj.name,
|
|
icon: subj.icon,
|
|
slug: subj.slug,
|
|
mastery: null,
|
|
});
|
|
|
|
const topics = db.prepare(`
|
|
SELECT t.id, t.name,
|
|
COUNT(DISTINCT q.id) AS total_q,
|
|
COUNT(DISTINCT CASE WHEN ua.is_correct = 1 THEN ua.question_id END) AS correct_q
|
|
FROM topics t
|
|
LEFT JOIN questions q ON q.topic_id = t.id
|
|
LEFT JOIN user_answers ua ON ua.question_id = q.id
|
|
AND ua.session_id IN (
|
|
SELECT id FROM test_sessions WHERE user_id = ? AND status = 'completed'
|
|
)
|
|
WHERE t.subject_id = ?
|
|
GROUP BY t.id
|
|
ORDER BY t.order_index
|
|
`).all(uid, subj.id);
|
|
|
|
for (const topic of topics) {
|
|
const mastery = topic.total_q > 0
|
|
? Math.round((topic.correct_q / topic.total_q) * 100)
|
|
: null;
|
|
|
|
nodes.push({
|
|
id: `topic_${topic.id}`,
|
|
type: 'topic',
|
|
label: topic.name,
|
|
mastery,
|
|
totalQ: topic.total_q,
|
|
correctQ: topic.correct_q,
|
|
});
|
|
|
|
links.push({ source: `subj_${subj.id}`, target: `topic_${topic.id}` });
|
|
}
|
|
}
|
|
|
|
res.json({ nodes, links });
|
|
}
|
|
|
|
module.exports = { getMap };
|