Add paper/print alternatives for off-screen learning
Build and Push Docker Image / build-and-push (push) Successful in 3m35s

- Spelling: "Blank test" print button per word list (numbered lines, no words shown) for dictated paper tests
- Spelling: "Record Paper Test" form in admin to log a paper test score to the gradebook
- Gradebook: "Record Paper Score" form to log a paper quiz/assignment score for any course item
- Flashcards: "Print" button per deck generates a cut-and-fold card sheet (fronts page + backs page)
- Crossword: print now includes a second page with a filled answer key
- Lessons + Assignments: 🖨 Print button in student view generates a clean printable page
- Word lists: new "CSV file" import tab — upload a .csv with word, sentence, definition columns

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 4.6 committed 2026-10-19 12:43:38 -04:00
1 parent 6baea15986
commit a92db291c7
5 files changed
+392 -3

No files matched your search

+48
View File
@@ -557,6 +557,54 @@ app.patch('/api/tests/:testId/correct', requirePin, (req, res) => {
res.json({ score: newScore, total: test.total });
});
// Record a paper spelling test (no per-word answers — score entered by parent)
app.post('/api/paper-spelling-test', requirePin, (req, res) => {
const { studentId, listId, score, total, itemId, date } = req.body;
const s = Number(score), t = Number(total);
if (!studentId || !listId || isNaN(s) || isNaN(t) || t <= 0 || s < 0 || s > t) {
return res.status(400).json({ error: 'studentId, listId, score (0–total) and total required' });
}
const testId = db.prepare(`INSERT INTO tests (student_id, list_id, score, total, item_id) VALUES (?, ?, ?, ?, ?)`)
.run(studentId, listId, s, t, itemId || null).lastInsertRowid;
if (itemId) {
db.prepare(`
INSERT INTO submissions (student_id, item_id, status, score, points_possible, completed_at, graded_at)
VALUES (?, ?, 'graded', ?, ?, datetime('now'), datetime('now'))
ON CONFLICT (student_id, item_id) DO UPDATE SET
status = 'graded', score = excluded.score, points_possible = excluded.points_possible,
completed_at = datetime('now'), graded_at = datetime('now')
`).run(studentId, itemId, s, t);
db.prepare(`INSERT INTO submission_history (student_id, item_id, score, points_possible) VALUES (?, ?, ?, ?)`)
.run(studentId, itemId, s, t);
markScheduleDone.run(studentId, itemId, isDateStr(date) ? date : today());
}
res.json({ testId, score: s, total: t });
});
// Record a paper score for any gradable course item (quiz, assignment, etc.)
app.post('/api/paper-score/:itemId', requirePin, (req, res) => {
const { studentId, score, total, date } = req.body;
const s = Number(score), t = Number(total);
if (!studentId || isNaN(s) || isNaN(t) || t <= 0 || s < 0 || s > t) {
return res.status(400).json({ error: 'studentId, score (0–total) and total required' });
}
const item = db.prepare(`SELECT id FROM items WHERE id = ?`).get(req.params.itemId);
if (!item) return res.status(404).json({ error: 'Item not found' });
db.prepare(`
INSERT INTO submissions (student_id, item_id, status, score, points_possible, completed_at, graded_at)
VALUES (?, ?, 'graded', ?, ?, datetime('now'), datetime('now'))
ON CONFLICT (student_id, item_id) DO UPDATE SET
status = 'graded', score = excluded.score, points_possible = excluded.points_possible,
completed_at = datetime('now'), graded_at = datetime('now')
`).run(studentId, item.id, s, t);
db.prepare(`INSERT INTO submission_history (student_id, item_id, score, points_possible) VALUES (?, ?, ?, ?)`)
.run(studentId, item.id, s, t);
markScheduleDone.run(studentId, item.id, isDateStr(date) ? date : today());
res.json({ score: s, total: t });
});
// ---------- spelling practice activity (parent view) ----------
app.get('/api/spelling-activity', requirePin, (req, res) => {