fix(import): set owner on auto-created lessons + nest lesson_path under started lesson
Two bugs surfaced by Excel import on a lesson with a lesson_path column: 1. resolveLesson created lessons without owner_id, so after the ownership model (sub-project B) they never appeared in getLessonTree — import reported success but nothing was visible. 2. lesson_path was resolved at the root; cards landed in new root lessons instead of under the lesson the import was started from. Now: auto-created lessons get ownerId + visibility 'private'; lesson_path is resolved relative to the started lesson (each segment a sublesson). Also drop the stale eager card_progress insert (progress is per-user and lazy since B).
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { and, eq, inArray, isNull } from 'drizzle-orm';
|
||||
import { and, eq, inArray } from 'drizzle-orm';
|
||||
import * as XLSX from 'xlsx';
|
||||
import type { Db } from '../db/client.js';
|
||||
import { cardProgress, cards, lessons } from '../db/schema.js';
|
||||
import { cards, lessons } from '../db/schema.js';
|
||||
import { getDescendantLessonIds } from './lessons.js';
|
||||
import { canEditLesson, canReadLesson } from './permissions.js';
|
||||
import { ApiError } from '../lib/errors.js';
|
||||
@@ -36,6 +36,7 @@ function nowSec() { return Math.floor(Date.now() / 1000); }
|
||||
|
||||
async function resolveLesson(
|
||||
db: Db,
|
||||
userId: number,
|
||||
defaultLessonId: number,
|
||||
lessonPath: string | undefined,
|
||||
createMissing: boolean
|
||||
@@ -43,14 +44,19 @@ async function resolveLesson(
|
||||
if (!lessonPath || lessonPath.trim() === '') return defaultLessonId;
|
||||
const parts = lessonPath.split('/').map((s) => s.trim()).filter(Boolean);
|
||||
if (parts.length === 0) return defaultLessonId;
|
||||
let parentId: number | null = null;
|
||||
// lesson_path is resolved RELATIVE to the lesson the import was started from:
|
||||
// each segment becomes a sublesson under it (created on demand, owned by the user).
|
||||
let parentId: number = defaultLessonId;
|
||||
for (const name of parts) {
|
||||
const found: typeof lessons.$inferSelect | undefined = parentId === null
|
||||
? db.select().from(lessons).where(and(eq(lessons.name, name), isNull(lessons.parentId))).get()
|
||||
: db.select().from(lessons).where(and(eq(lessons.name, name), eq(lessons.parentId, parentId))).get();
|
||||
const found = db.select().from(lessons)
|
||||
.where(and(eq(lessons.name, name), eq(lessons.parentId, parentId), eq(lessons.ownerId, userId)))
|
||||
.get();
|
||||
if (found) { parentId = found.id; continue; }
|
||||
if (!createMissing) return null;
|
||||
const [row] = db.insert(lessons).values({ name, parentId, position: 0 }).returning().all();
|
||||
const [row] = db.insert(lessons).values({
|
||||
name, parentId, position: 0,
|
||||
ownerId: userId, visibility: 'private', isCurated: false,
|
||||
}).returning().all();
|
||||
parentId = row!.id;
|
||||
}
|
||||
return parentId;
|
||||
@@ -73,7 +79,7 @@ export async function importCardsFromBuffer(
|
||||
result.errors.push({ row: rowNum, message: 'question and answer are required' });
|
||||
continue;
|
||||
}
|
||||
const targetLessonId = await resolveLesson(db, defaultLessonId, row.lesson_path, opts.createMissingLessons);
|
||||
const targetLessonId = await resolveLesson(db, userId, defaultLessonId, row.lesson_path, opts.createMissingLessons);
|
||||
if (targetLessonId === null) {
|
||||
result.errors.push({ row: rowNum, message: `lesson_path not found: ${row.lesson_path}` });
|
||||
continue;
|
||||
@@ -87,12 +93,7 @@ export async function importCardsFromBuffer(
|
||||
} else {
|
||||
const positions = db.select({ pos: cards.position }).from(cards).where(eq(cards.lessonId, targetLessonId)).all();
|
||||
const position = positions.length === 0 ? 0 : Math.max(...positions.map((p) => p.pos)) + 1;
|
||||
const [inserted] = db.insert(cards).values({ lessonId: targetLessonId, question: q, answer: a, hint, position }).returning().all();
|
||||
db.insert(cardProgress).values({ cardId: inserted!.id, direction: 'forward', box: 1, nextDueAt: 0 }).run();
|
||||
const lesson = db.select().from(lessons).where(eq(lessons.id, targetLessonId)).get();
|
||||
if (lesson?.bidirectional) {
|
||||
db.insert(cardProgress).values({ cardId: inserted!.id, direction: 'backward', box: 1, nextDueAt: 0 }).run();
|
||||
}
|
||||
db.insert(cards).values({ lessonId: targetLessonId, question: q, answer: a, hint, position }).run();
|
||||
result.inserted += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user