diff --git a/backend/src/routes/exam-prep.js b/backend/src/routes/exam-prep.js index 578c0ab..112c8c8 100644 --- a/backend/src/routes/exam-prep.js +++ b/backend/src/routes/exam-prep.js @@ -630,13 +630,15 @@ function pickRandomByDifficulty(examKey, count, excludeSlugs) { ? `AND (subtopic IS NULL OR subtopic NOT IN (${exParams.map(() => '?').join(',')}))` : ''; + const COLS = `id, task_idx, variant, task_type, text_html, figure_html, opts_json, + answer, solution_html, topic, subtopic, difficulty, textbook_slug, textbook_paragraph`; const out = []; + const seen = new Set(); for (let d = 1; d <= 5; d++) { const limit = dist[d - 1]; if (limit === 0) continue; const sql = ` - SELECT id, task_idx, variant, task_type, text_html, figure_html, opts_json, - answer, solution_html, topic, subtopic, difficulty, textbook_slug, textbook_paragraph + SELECT ${COLS} FROM exam_tasks WHERE exam_key = ? AND task_type IN ('mc','open') AND difficulty = ? @@ -646,8 +648,25 @@ function pickRandomByDifficulty(examKey, count, excludeSlugs) { const args = exParams ? [examKey, d, ...exParams, limit] : [examKey, d, limit]; + for (const r of db.prepare(sql).all(...args)) { if (!seen.has(r.id)) { seen.add(r.id); out.push(r); } } + } + // Backfill to `count` from any difficulty — covers tracks whose tasks don't + // span all 5 difficulty levels (otherwise empty levels would shrink the batch). + if (out.length < count) { + const ids = [...seen]; + const notIn = ids.length ? `AND id NOT IN (${ids.map(() => '?').join(',')})` : ''; + const sql = ` + SELECT ${COLS} + FROM exam_tasks + WHERE exam_key = ? AND task_type IN ('mc','open') + ${exClause} + ${notIn} + ORDER BY RANDOM() + LIMIT ?`; + const args = [examKey, ...(exParams || []), ...ids, count - out.length]; out.push(...db.prepare(sql).all(...args)); } + out.sort((a, b) => (a.difficulty || 0) - (b.difficulty || 0)); return out; } diff --git a/frontend/lesson.html b/frontend/lesson.html index 4aae0ff..3a4f13d 100644 --- a/frontend/lesson.html +++ b/frontend/lesson.html @@ -276,7 +276,7 @@ .lesson-nav-btn-prev { justify-content: flex-start; } .lesson-nav-btn-next { justify-content: flex-end; margin-left: auto; } .lesson-nav-btn-label { font-size: 0.7rem; font-weight: 600; color: var(--text-3); display: block; } - .lesson-nav-btn-title { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 160px; } + .lesson-nav-btn-title { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 160px; } /* ── complete button ── */ .lesson-complete-wrap {