@things-factory/learning 10.1.262 → 10.1.263

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist-client/pages/learning-course-list.js +27 -5
  2. package/dist-client/pages/learning-course-list.js.map +1 -1
  3. package/dist-client/pages/learning-enrollment-status.js +12 -2
  4. package/dist-client/pages/learning-enrollment-status.js.map +1 -1
  5. package/dist-client/pages/learning-view.d.ts +10 -0
  6. package/dist-client/pages/learning-view.js +59 -3
  7. package/dist-client/pages/learning-view.js.map +1 -1
  8. package/dist-client/tsconfig.tsbuildinfo +1 -1
  9. package/dist-server/ai-reads.d.ts +1 -0
  10. package/dist-server/ai-reads.js +26 -2
  11. package/dist-server/ai-reads.js.map +1 -1
  12. package/dist-server/service/enrollment/enrollment-mutation.d.ts +2 -0
  13. package/dist-server/service/enrollment/enrollment-mutation.js +18 -0
  14. package/dist-server/service/enrollment/enrollment-mutation.js.map +1 -1
  15. package/dist-server/service/enrollment/enrollment-query.d.ts +2 -0
  16. package/dist-server/service/enrollment/enrollment-query.js +15 -0
  17. package/dist-server/service/enrollment/enrollment-query.js.map +1 -1
  18. package/dist-server/service/enrollment/enrollment-service.d.ts +4 -0
  19. package/dist-server/service/enrollment/enrollment-service.js +1 -0
  20. package/dist-server/service/enrollment/enrollment-service.js.map +1 -1
  21. package/dist-server/service/enrollment/index.d.ts +3 -1
  22. package/dist-server/service/enrollment/index.js +3 -1
  23. package/dist-server/service/enrollment/index.js.map +1 -1
  24. package/dist-server/service/enrollment/lesson-note-service.d.ts +15 -0
  25. package/dist-server/service/enrollment/lesson-note-service.js +86 -0
  26. package/dist-server/service/enrollment/lesson-note-service.js.map +1 -0
  27. package/dist-server/service/enrollment/lesson-note.d.ts +35 -0
  28. package/dist-server/service/enrollment/lesson-note.js +107 -0
  29. package/dist-server/service/enrollment/lesson-note.js.map +1 -0
  30. package/dist-server/service/index.d.ts +2 -1
  31. package/dist-server/service/index.js +1 -0
  32. package/dist-server/service/index.js.map +1 -1
  33. package/dist-server/tsconfig.tsbuildinfo +1 -1
  34. package/package.json +14 -14
  35. package/translations/en.json +22 -16
  36. package/translations/es.json +7 -1
  37. package/translations/ja.json +7 -1
  38. package/translations/ko.json +23 -17
  39. package/translations/ms.json +7 -1
  40. package/translations/zh.json +7 -1
@@ -1 +1 @@
1
- {"version":3,"file":"enrollment-service.js","sourceRoot":"","sources":["../../../server/service/enrollment/enrollment-service.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * What the doors of enrolments do with the database (ADR-0194 decisions 5-8): enrolling people for whoever asks and\n * withdrawing them, the to-do of each, the time the server counts from ticks, the quiz the server scores, the\n * acknowledgement, and completing an enrolment with its evidence in the same transaction as the handler of whoever\n * asked. Nothing here deletes an enrolment or its progress.\n */\nimport { EntityManager, In, IsNull } from 'typeorm'\n\nimport { User } from '@things-factory/auth-base'\nimport { audienceTargetRefusal, audienceUserIds, departmentTreeOf, domainRolesOf } from '@things-factory/organization'\nimport { logger } from '@things-factory/env'\nimport { translatorFor } from '@things-factory/i18n-base/translator'\nimport { calendarDay, getRepository, Refusal } from '@things-factory/shell'\nimport { abort, Activity, ActivityThread, complete, handOut } from '@things-factory/worklist'\n\nimport { LEARNING_COURSE_ACTIVITY } from '../../activities'\nimport { Course } from '../course/course'\nimport { CourseQuestion } from '../course/course-question'\nimport { CourseUnit, CourseUnitKind } from '../course/course-unit'\nimport { CourseVersion } from '../course/course-version'\nimport { courseOf, learningManagerOf, publishedVersionOf, questionsOf, unitsOf } from '../course/course-service'\nimport { Enrollment, EnrollmentStatus, OPEN_ENROLLMENT_STATUSES } from './enrollment'\nimport { learningOutcomeOf } from './learning-outcome'\nimport { counts, drawQuestions, missingOf, Missing, scoreOf, tickVerdict, unitDone } from './progress-rules'\nimport { QuizAttempt } from './quiz-attempt'\nimport { UnitProgress } from './unit-progress'\n\n/** Who asked for an enrolment - a module's kind of record and the record. A manager's own is `learning.direct`. */\nexport interface LearningSource {\n kind: string\n id?: string | null\n}\n\nexport const DIRECT_SOURCE_KIND = 'learning.direct'\n\n/** The learner's screen of one enrolment - where the to-do leads. */\nexport const learningAddressOf = (enrollmentId: string) => `learning-view?id=${enrollmentId}`\n\nexport const todayIn = (domain: { timezone?: string | null }, now: Date = new Date()) => calendarDay(domain?.timezone, now)\n\nconst notFound = () => new Refusal('learning-not-found', {}, 'the record was not found. reload and try again.')\nconst isOpen = (status: string) => (OPEN_ENROLLMENT_STATUSES as string[]).includes(status)\n\n/* ---------------------------------------------------------------- the to-do (decision 5) */\n\n/** The context worklist closes a to-do under - `by` ends or aborts it; worklist speaks its refusals through `t`. */\nfunction todoContextOf(tx: EntityManager, domain: any, user: User) {\n const t = translatorFor(user.locale || 'ko')\n return { state: { domain, user, tx }, t: (key: string, options?: any) => t(key, options) } as any\n}\n\n/**\n * Hands out 「<과정> 수강」 to the person, with the due day - nothing where the domain has not installed the to-do. worklist's\n * handOut: the person's 할 일 and nobody's 상신함, also when the manager assigning it is the person (2026-11-08). The one\n * who asked (`context`) stands as its issuer.\n */\nasync function issueTodo(context: any, enrollment: Enrollment, course: Course, person: User): Promise<string | null> {\n const { domain, tx } = context.state\n const activity = await tx.getRepository(Activity).findOne({ where: { domain: { id: domain.id }, name: LEARNING_COURSE_ACTIVITY } as any })\n if (!activity) {\n logger.warn(`[learning] ${domain.subdomain}: the activity \"${LEARNING_COURSE_ACTIVITY}\" is not installed - \"${course.name}\" not handed out`)\n return null\n }\n const t = translatorFor(person.locale || 'ko')\n const name = t('text.learning-todo-name', { course: course.name })\n const headline = enrollment.dueOn ? t('text.learning-todo-headline', { dueOn: enrollment.dueOn }) : t('text.learning-todo-headline-no-due')\n const dueAt = enrollment.dueOn ? dueAtOf(enrollment.dueOn, domain.timezone) : undefined\n const input = { enrollmentId: enrollment.id, headline, address: learningAddressOf(enrollment.id) }\n const instance = await handOut({ activityId: activity.id, name, description: headline, input, assignee: person, dueAt }, context)\n return instance.id\n}\n\n/** The end of the due day in the domain's time zone. */\nfunction dueAtOf(dueOn: string, timezone?: string | null): Date {\n const end = new Date(`${dueOn}T23:59:59Z`)\n const zone = timezone || 'UTC'\n const asLocal = new Date(end.toLocaleString('en-US', { timeZone: zone }))\n const asUtc = new Date(end.toLocaleString('en-US', { timeZone: 'UTC' }))\n return new Date(end.getTime() - (asLocal.getTime() - asUtc.getTime()))\n}\n\nasync function todoOpen(tx: EntityManager, instanceId: string): Promise<boolean> {\n return (await tx.getRepository(ActivityThread).count({ where: { activityInstance: { id: instanceId }, state: In(['assigned', 'started', 'unassigned']) } as any })) > 0\n}\n\n/** Ends the to-do as done once the course is completed. */\nasync function completeTodo(tx: EntityManager, domain: any, enrollment: Enrollment, by: User): Promise<void> {\n if (!enrollment.todoInstanceId || !(await todoOpen(tx, enrollment.todoInstanceId))) return\n await complete({ id: enrollment.todoInstanceId, reason: 'course completed' }, todoContextOf(tx, domain, by))\n}\n\n/** Aborts the to-do of an enrolment withdrawn or cancelled. */\nasync function abortTodo(tx: EntityManager, domain: any, enrollment: Enrollment, by: User, reason: string): Promise<void> {\n if (!enrollment.todoInstanceId || !(await todoOpen(tx, enrollment.todoInstanceId))) return\n await abort({ id: enrollment.todoInstanceId, reason } as any, todoContextOf(tx, domain, by))\n}\n\n/* ---------------------------------------------------------------- enrolling and withdrawing (decision 7) */\n\n/**\n * Enrols people in a course for whoever asks (`source`), to be completed by `dueOn` - the server function a using module\n * calls with the people it chose. One open enrolment per person, course and source: a person who has one is left with\n * it. Each new enrolment hands out its to-do. Answers the open enrolments of everyone given.\n */\nexport async function enroll(source: LearningSource, userIds: string[], courseId: string, dueOn: string | null | undefined, context: any): Promise<Enrollment[]> {\n const { domain, user, tx } = context.state\n if (!source?.kind) throw new Refusal('learning-source-required', {}, 'an enrolment names who asked for it')\n if (dueOn && !/^\\d{4}-\\d{2}-\\d{2}$/.test(dueOn)) throw new Refusal('learning-due-invalid', {}, 'the due day is YYYY-MM-DD')\n const course = await courseOf(tx, domain, courseId)\n if (!(await publishedVersionOf(tx, domain, course.id))) throw new Refusal('learning-course-not-published', { course: course.name }, `${course.name} has no published version`)\n const ids = [...new Set(userIds)].filter(Boolean)\n if (!ids.length) return []\n const people = await (tx as EntityManager).getRepository(User).find({ where: { id: In(ids) }, relations: ['domains'] })\n const sourceId = source.id ?? ''\n const repository = (tx as EntityManager).getRepository(Enrollment)\n const out: Enrollment[] = []\n for (const id of ids) {\n const person = people.find(one => one.id === id)\n if (!person || !(person.domains ?? []).some(one => one.id === domain.id)) throw new Refusal('learning-learner-unknown', {}, 'a learner must be a user of this domain')\n const open = await repository.findOne({ where: { domain: { id: domain.id }, user: { id }, course: { id: course.id }, sourceKind: source.kind, sourceId, status: In(OPEN_ENROLLMENT_STATUSES) } as any })\n if (open) {\n out.push(open)\n continue\n }\n const saved = await repository.save({ domain, user: person, course, dueOn: dueOn || null, sourceKind: source.kind, sourceId, status: EnrollmentStatus.ASSIGNED, learnedSeconds: 0, attempts: 0, creator: user, updater: user } as any)\n const todo = await issueTodo(context, saved, course, person)\n if (todo) await repository.update(saved.id, { todoInstanceId: todo } as any)\n out.push((await repository.findOneBy({ id: saved.id }))!)\n }\n return out\n}\n\n/**\n * Withdraws the open enrolments a source asked for - the record they were for was taken back. Each is cancelled with\n * the reason and its to-do aborted; completed ones stay. Answers how many were withdrawn.\n */\nexport async function withdraw(source: LearningSource, reason: string, context: any): Promise<number> {\n const { domain, user, tx } = context.state\n const rows = await (tx as EntityManager).getRepository(Enrollment).find({ where: { domain: { id: domain.id }, sourceKind: source.kind, sourceId: source.id ?? '', status: In(OPEN_ENROLLMENT_STATUSES) } as any })\n let count = 0\n for (const row of rows) if (await cancelOpen(tx, domain, row, user, reason)) count++\n return count\n}\n\n/** Cancels an open enrolment - claimed by an update only an open one matches, so a completion at the same time wins once. */\nasync function cancelOpen(tx: EntityManager, domain: any, row: Enrollment, by: User, reason: string): Promise<boolean> {\n const claimed = await tx.getRepository(Enrollment).update({ id: row.id, status: In(OPEN_ENROLLMENT_STATUSES) } as any, { status: EnrollmentStatus.CANCELLED, cancelledAt: new Date(), cancelReason: reason, updater: by } as any)\n if (!claimed.affected) return false\n await abortTodo(tx, domain, row, by, reason)\n return true\n}\n\n/**\n * A manager cancels enrolments with one reason (decision 8) - together: one refused, none is cancelled. An open one is\n * withdrawn; a completed one stays on record as cancelled - never deleted - and whoever asked for it is told in the same\n * transaction.\n */\nexport async function cancelEnrollments(ids: string[], reason: string, context: any): Promise<Enrollment[]> {\n if (!reason?.trim()) throw new Refusal('learning-cancel-reason-required', {}, 'say why it is cancelled')\n const unique = [...new Set(ids ?? [])]\n if (!unique.length) throw new Refusal('learning-cancel-none', {}, 'pick the enrolments to cancel')\n const out: Enrollment[] = []\n for (const id of unique) out.push(await cancelOne(id, reason.trim(), context))\n return out\n}\n\nasync function cancelOne(id: string, reason: string, context: any): Promise<Enrollment> {\n const { domain, user, tx } = context.state\n const row = await enrollmentOf(tx, domain, id)\n if (isOpen(row.status)) {\n if (!(await cancelOpen(tx, domain, row, user, reason))) throw new Refusal('learning-enrollment-moved', {}, 'the enrolment changed meanwhile - reload and try again')\n } else if (row.status === EnrollmentStatus.COMPLETED) {\n const claimed = await tx.getRepository(Enrollment).update({ id: row.id, status: EnrollmentStatus.COMPLETED } as any, { status: EnrollmentStatus.CANCELLED, cancelledAt: new Date(), cancelReason: reason, updater: user } as any)\n if (!claimed.affected) throw new Refusal('learning-enrollment-moved', {}, 'the enrolment changed meanwhile - reload and try again')\n await learningOutcomeOf(row.sourceKind)?.cancelled?.((await enrollmentOf(tx, domain, id))!, reason, context)\n } else throw new Refusal('learning-enrollment-cancelled', {}, 'the enrolment is cancelled already')\n return await enrollmentOf(tx, domain, id)\n}\n\n/**\n * The people an audience names now - the domain, departments with those under them, roles and people, by organization's\n * one rule (ADR-0163). Each target must be of this domain.\n */\nexport async function learnersOf(audiences: { scope: string; targetId: string; targetName?: string }[], context: any): Promise<string[]> {\n const { domain, tx } = context.state\n if (!audiences?.length) throw new Refusal('learning-audience-required', {}, 'pick who takes the course')\n const departmentIds = [...(await departmentTreeOf(domain, tx)).keys()]\n const roleIds = (await domainRolesOf(domain, undefined, tx)).map(one => one.id)\n for (const target of audiences) {\n const why = audienceTargetRefusal(target as any, { domainId: domain.id, departmentIds, roleIds })\n if (why) throw new Refusal('learning-audience-invalid', { target: target.targetName ?? target.targetId }, `${target.targetName ?? target.targetId} is not of this domain (${why})`)\n }\n return await audienceUserIds(domain, audiences as any, tx)\n}\n\n/* ---------------------------------------------------------------- reading */\n\nexport async function enrollmentOf(tx: EntityManager, domain: { id: string }, id: string): Promise<Enrollment> {\n const row = id && /^[0-9a-f-]{36}$/i.test(id) ? await tx.getRepository(Enrollment).findOne({ where: { id, domain: { id: domain.id } } as any }) : null\n if (!row) throw notFound()\n return row\n}\n\n/** The request's own enrolment - one answer for \"none\" and \"not yours\". */\nexport async function myEnrollmentOf(context: any, id: string, tx: EntityManager = learningManagerOf(context)): Promise<Enrollment> {\n const row = await enrollmentOf(tx, context.state.domain, id)\n if (row.userId !== context.state.user.id) throw notFound()\n return row\n}\n\n/** The version an enrolment shows: its own once started, the published one before. */\nexport async function shownVersionOf(tx: EntityManager, domain: { id: string }, enrollment: Enrollment): Promise<CourseVersion | null> {\n if (enrollment.versionId) return await tx.getRepository(CourseVersion).findOne({ where: { id: enrollment.versionId } as any, withDeleted: true })\n return await publishedVersionOf(tx, domain, enrollment.courseId!)\n}\n\nexport async function progressesOf(tx: EntityManager, enrollmentId: string): Promise<UnitProgress[]> {\n return await tx.getRepository(UnitProgress).find({ where: { enrollment: { id: enrollmentId } } as any })\n}\n\n/** What still stands between an enrolment and its completion - nothing for a completed one. */\nexport async function missingFor(tx: EntityManager, domain: { id: string }, enrollment: Enrollment): Promise<Missing[]> {\n if (enrollment.status === EnrollmentStatus.COMPLETED) return []\n const version = await shownVersionOf(tx, domain, enrollment)\n if (!version) return ['units']\n const units = (await unitsOf(tx, version.id)).filter(counts)\n const progress = await progressesOf(tx, enrollment.id)\n const questions = version.passScore != null ? await tx.getRepository(CourseQuestion).count({ where: { version: { id: version.id } } as any }) : 0\n return missingOf({\n units: units.length,\n unitsDone: units.filter(unit => progress.some(one => one.unitId === unit.id && one.doneAt)).length,\n learnedSeconds: enrollment.learnedSeconds,\n minSeconds: version.minSeconds ?? 0,\n quiz: version.passScore != null && questions > 0,\n passed: enrollment.score != null && enrollment.score >= (version.passScore ?? 101),\n acknowledgement: !!version.acknowledgement,\n acknowledged: !!enrollment.acknowledgedAt\n })\n}\n\n/* ---------------------------------------------------------------- learning (decision 6) */\n\n/** The enrolment, started if it was not: its version fixed to the one published now, in progress from now on. */\nasync function started(tx: EntityManager, domain: any, enrollment: Enrollment, user: User): Promise<Enrollment> {\n if (enrollment.status === EnrollmentStatus.CANCELLED) throw new Refusal('learning-enrollment-cancelled', {}, 'the enrolment is cancelled')\n if (enrollment.versionId) {\n if (enrollment.status === EnrollmentStatus.ASSIGNED) await tx.getRepository(Enrollment).update({ id: enrollment.id, status: EnrollmentStatus.ASSIGNED } as any, { status: EnrollmentStatus.IN_PROGRESS, startedAt: new Date() } as any)\n return await enrollmentOf(tx, domain, enrollment.id)\n }\n const version = await publishedVersionOf(tx, domain, enrollment.courseId!)\n if (!version) throw new Refusal('learning-course-not-published', {}, 'the course has no published version')\n await tx.getRepository(Enrollment).update({ id: enrollment.id, status: In(OPEN_ENROLLMENT_STATUSES) } as any, { version, status: EnrollmentStatus.IN_PROGRESS, startedAt: enrollment.startedAt ?? new Date(), updater: user } as any)\n return await enrollmentOf(tx, domain, enrollment.id)\n}\n\nasync function unitOfVersion(tx: EntityManager, version: { id: string }, unitId: string): Promise<CourseUnit> {\n const unit = await tx.getRepository(CourseUnit).findOne({ where: { id: unitId, version: { id: version.id } } as any })\n if (!unit) throw notFound()\n return unit\n}\n\nexport interface TickResult {\n enrollment: Enrollment\n progress: UnitProgress\n}\n\n/**\n * One tick of the learner's screen on a unit (decision 6): the server adds the gap since the unit's last tick, at most\n * 15 seconds and at most what the screen says, and refuses a video position ahead of what was watched. A total the\n * screen sends is never taken. A unit that reaches its condition is done, and the enrolment is completed when nothing is\n * missing any more.\n */\nexport async function recordTick(enrollmentId: string, unitId: string, seconds: number, position: number, context: any, now: Date = new Date()): Promise<TickResult> {\n const { domain, user, tx } = context.state\n let enrollment = await myEnrollmentOf(context, enrollmentId, tx)\n enrollment = await started(tx, domain, enrollment, user)\n const unit = await unitOfVersion(tx, { id: enrollment.versionId! }, unitId)\n if (unit.kind === CourseUnitKind.LINK) throw new Refusal('learning-link-not-counted', {}, 'a link is not counted')\n const repository = (tx as EntityManager).getRepository(UnitProgress)\n const progress =\n (await repository.findOne({ where: { enrollment: { id: enrollment.id }, unit: { id: unit.id } } as any })) ??\n (await repository.save({ domain, enrollment, unit, learnedSeconds: 0, watchedSeconds: 0, positionSeconds: 0, pagesSeen: 0, pagesRead: 0, pageSeconds: 0, pageAt: 0, creator: user, updater: user } as any))\n if (enrollment.status === EnrollmentStatus.COMPLETED) return { enrollment, progress }\n\n const verdict = tickVerdict(progress, { seconds, position }, now, unit)\n if ('refusal' in verdict) {\n if (verdict.refusal === 'page-ahead') throw new Refusal('learning-page-ahead', { allowed: verdict.allowed }, `a page cannot be ahead of the one after the last read (${verdict.allowed})`)\n throw new Refusal('learning-position-ahead', { allowed: verdict.allowed }, `the video cannot be ahead of what was watched (${verdict.allowed} s)`)\n }\n progress.learnedSeconds += verdict.add\n progress.watchedSeconds = verdict.watchedSeconds\n progress.positionSeconds = verdict.positionSeconds\n progress.pagesSeen = verdict.pagesSeen\n progress.pagesRead = verdict.pagesRead\n progress.pageSeconds = verdict.pageSeconds\n progress.pageAt = verdict.pageAt\n progress.lastTickAt = now\n if (!progress.doneAt && unitDone(unit, progress)) progress.doneAt = now\n progress.updater = user\n await repository.save(progress)\n if (verdict.add) await tx.getRepository(Enrollment).increment({ id: enrollment.id } as any, 'learnedSeconds', verdict.add)\n /* Where the learner was - the course opens on this unit again (2026-11-08). */\n if (enrollment.lastUnitId !== unit.id) await tx.getRepository(Enrollment).update({ id: enrollment.id } as any, { lastUnitId: unit.id } as any)\n enrollment = await completeIfMet(tx, domain, await enrollmentOf(tx, domain, enrollment.id), context)\n return { enrollment, progress }\n}\n\n/** The learner confirms the version's acknowledgement - recorded once, when they did. */\nexport async function acknowledgeEnrollment(enrollmentId: string, context: any): Promise<Enrollment> {\n const { domain, user, tx } = context.state\n let enrollment = await started(tx, domain, await myEnrollmentOf(context, enrollmentId, tx), user)\n const version = await shownVersionOf(tx, domain, enrollment)\n if (!version?.acknowledgement) throw new Refusal('learning-no-acknowledgement', {}, 'the course asks for no acknowledgement')\n if (!enrollment.acknowledgedAt) await tx.getRepository(Enrollment).update({ id: enrollment.id } as any, { acknowledgedAt: new Date(), updater: user } as any)\n return await completeIfMet(tx, domain, await enrollmentOf(tx, domain, enrollment.id), context)\n}\n\n/* ---------------------------------------------------------------- the quiz (decision 4) */\n\nexport interface DrawnForLearner {\n attempt: QuizAttempt\n questions: { id: string; kind: string; text: string; choices: { id: string; text: string }[] }[]\n}\n\n/** The questions of an attempt as the learner is drawn them - the choices in the order shown, never whether one is right. */\nexport function forLearner(attempt: QuizAttempt, questions: CourseQuestion[]): DrawnForLearner {\n return {\n attempt,\n questions: attempt.drawn.map(drawn => {\n const question = questions.find(one => one.id === drawn.questionId)!\n return {\n id: question.id,\n kind: question.kind,\n text: question.text,\n choices: drawn.choiceIds.map(id => question.choices.find(one => one.id === id)!).map(one => ({ id: one.id, text: one.text }))\n }\n })\n }\n}\n\n/**\n * Starts an attempt at the quiz: the questions drawn and shuffled, their choices shuffled. An attempt not sent yet is\n * answered again; the version's limit on attempts holds.\n */\n/**\n * Holds the enrolment for the rest of the request's transaction - an update takes its row lock on every driver, so two\n * attempts of one enrolment are started, counted and sent one after the other. Two starts at once made two unsent\n * attempts, and a limit of one let both be sent (2026-11-08).\n */\nasync function holdEnrollment(tx: EntityManager, domain: any, enrollment: Enrollment, user: User): Promise<Enrollment> {\n await tx.getRepository(Enrollment).update({ id: enrollment.id } as any, { updater: user } as any)\n return await enrollmentOf(tx, domain, enrollment.id)\n}\n\nexport async function startQuizAttempt(enrollmentId: string, context: any): Promise<DrawnForLearner> {\n const { domain, user, tx } = context.state\n const held = await holdEnrollment(tx, domain, await myEnrollmentOf(context, enrollmentId, tx), user)\n const enrollment = await started(tx, domain, held, user)\n if (enrollment.status === EnrollmentStatus.COMPLETED) throw new Refusal('learning-enrollment-completed', {}, 'the course is completed')\n const version = (await shownVersionOf(tx, domain, enrollment))!\n const questions = await questionsOf(tx, version.id)\n if (version.passScore == null || !questions.length) throw new Refusal('learning-no-quiz', {}, 'the course has no quiz')\n const repository = (tx as EntityManager).getRepository(QuizAttempt)\n const pending = await repository.findOne({ where: { enrollment: { id: enrollment.id }, submittedAt: IsNull() } as any })\n if (pending) return forLearner(pending, questions)\n if (version.attemptLimit != null && enrollment.attempts >= version.attemptLimit) throw new Refusal('learning-attempts-used', { limit: version.attemptLimit }, `all ${version.attemptLimit} attempts are used`)\n const attempt = await repository.save({ domain, enrollment, drawn: drawQuestions(questions, version.questionCount), creator: user, updater: user } as any)\n return forLearner(attempt, questions)\n}\n\nexport interface QuizResult {\n enrollment: Enrollment\n score: number\n passed: boolean\n attemptsLeft: number | null\n}\n\n/**\n * Scores an attempt's answers on the server against what it drew (decision 4) - only the score and whether it passed go\n * back, never the right answers. The best score stays on the enrolment; passing may complete it.\n */\nexport async function submitQuizAttempt(attemptId: string, answers: Record<string, string[]>, context: any): Promise<QuizResult> {\n const { domain, user, tx } = context.state\n const attempt = await (tx as EntityManager).getRepository(QuizAttempt).findOne({ where: { id: attemptId, domain: { id: domain.id } } as any })\n if (!attempt) throw notFound()\n const enrollment = await holdEnrollment(tx, domain, await myEnrollmentOf(context, attempt.enrollmentId!, tx), user)\n const version = (await shownVersionOf(tx, domain, enrollment))!\n if (version.attemptLimit != null && enrollment.attempts >= version.attemptLimit) throw new Refusal('learning-attempts-used', { limit: version.attemptLimit }, `all ${version.attemptLimit} attempts are used`)\n const questions = await questionsOf(tx, version.id)\n const drawn = attempt.drawn.map(one => questions.find(question => question.id === one.questionId)).filter(Boolean) as CourseQuestion[]\n const kept: Record<string, string[]> = Object.fromEntries(drawn.map(question => [question.id, (answers?.[question.id] ?? []).map(String)]))\n const score = scoreOf(drawn, kept)\n const passed = score >= (version.passScore ?? 101)\n const claimed = await tx.getRepository(QuizAttempt).update({ id: attempt.id, submittedAt: IsNull() } as any, { answers: kept, score, passed, submittedAt: new Date(), updater: user } as any)\n if (!claimed.affected) throw new Refusal('learning-attempt-sent', {}, 'the attempt was sent already')\n await tx.getRepository(Enrollment).increment({ id: enrollment.id } as any, 'attempts', 1)\n await tx.getRepository(Enrollment).update({ id: enrollment.id } as any, { score: Math.max(enrollment.score ?? 0, score), updater: user } as any)\n const after = await completeIfMet(tx, domain, await enrollmentOf(tx, domain, enrollment.id), context)\n return { enrollment: after, score, passed, attemptsLeft: version.attemptLimit != null ? Math.max(0, version.attemptLimit - after.attempts) : null }\n}\n\n/* ---------------------------------------------------------------- completing (decision 8) */\n\n/** A certificate number: the day completed and the enrolment's own id - one per completion, never reused. */\nexport const certificateNoOf = (enrollment: { id: string }, day: string) => `LMS-${day.replace(/-/g, '')}-${enrollment.id.replace(/-/g, '').slice(0, 8).toUpperCase()}`\n\n/**\n * Completes an open enrolment when nothing is missing - every unit done, the least time, the quiz passed, the\n * acknowledgement confirmed. The evidence stays on it; whoever asked for it is told in this same transaction (a handler\n * that fails takes the completion back), and the to-do ends as done.\n */\nexport async function completeIfMet(tx: EntityManager, domain: any, enrollment: Enrollment, context: any): Promise<Enrollment> {\n if (!isOpen(enrollment.status) || !enrollment.versionId) return enrollment\n if ((await missingFor(tx, domain, enrollment)).length) return enrollment\n const now = new Date()\n const claimed = await tx\n .getRepository(Enrollment)\n .update({ id: enrollment.id, status: In(OPEN_ENROLLMENT_STATUSES) } as any, { status: EnrollmentStatus.COMPLETED, completedAt: now, certificateNo: certificateNoOf(enrollment, todayIn(domain, now)), updater: context.state.user } as any)\n if (!claimed.affected) return await enrollmentOf(tx, domain, enrollment.id)\n const completed = await enrollmentOf(tx, domain, enrollment.id)\n await learningOutcomeOf(completed.sourceKind)?.completed(completed, context)\n await completeTodo(tx, domain, completed, context.state.user)\n return completed\n}\n\n/* ---------------------------------------------------------------- whom an enrolment is for */\n\n/** The open and completed enrolments of the request's person, the open first and the soonest due first. */\nexport async function myEnrollments(context: any): Promise<Enrollment[]> {\n const { domain, user } = context.state\n const rows = await learningManagerOf(context)\n .getRepository(Enrollment)\n .find({ where: { domain: { id: domain.id }, user: { id: user.id }, status: In([...OPEN_ENROLLMENT_STATUSES, EnrollmentStatus.COMPLETED]) } as any })\n const rank = (one: Enrollment) => (isOpen(one.status) ? 0 : 1)\n return rows.sort((a, b) => rank(a) - rank(b) || String(a.dueOn ?? '9999').localeCompare(String(b.dueOn ?? '9999')) || +new Date(b.createdAt!) - +new Date(a.createdAt!))\n}\n\nexport { isOpen }\n"]}
1
+ {"version":3,"file":"enrollment-service.js","sourceRoot":"","sources":["../../../server/service/enrollment/enrollment-service.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * What the doors of enrolments do with the database (ADR-0194 decisions 5-8): enrolling people for whoever asks and\n * withdrawing them, the to-do of each, the time the server counts from ticks, the quiz the server scores, the\n * acknowledgement, and completing an enrolment with its evidence in the same transaction as the handler of whoever\n * asked. Nothing here deletes an enrolment or its progress.\n */\nimport { EntityManager, In, IsNull } from 'typeorm'\n\nimport { User } from '@things-factory/auth-base'\nimport { audienceTargetRefusal, audienceUserIds, departmentTreeOf, domainRolesOf } from '@things-factory/organization'\nimport { logger } from '@things-factory/env'\nimport { translatorFor } from '@things-factory/i18n-base/translator'\nimport { calendarDay, getRepository, Refusal } from '@things-factory/shell'\nimport { abort, Activity, ActivityThread, complete, handOut } from '@things-factory/worklist'\n\nimport { LEARNING_COURSE_ACTIVITY } from '../../activities'\nimport { Course } from '../course/course'\nimport { CourseQuestion } from '../course/course-question'\nimport { CourseUnit, CourseUnitKind } from '../course/course-unit'\nimport { CourseVersion } from '../course/course-version'\nimport { courseOf, learningManagerOf, publishedVersionOf, questionsOf, unitsOf } from '../course/course-service'\nimport { Enrollment, EnrollmentStatus, OPEN_ENROLLMENT_STATUSES } from './enrollment'\nimport { learningOutcomeOf } from './learning-outcome'\nimport { counts, drawQuestions, missingOf, Missing, scoreOf, tickVerdict, unitDone } from './progress-rules'\nimport { QuizAttempt } from './quiz-attempt'\nimport { UnitProgress } from './unit-progress'\n\n/** Who asked for an enrolment - a module's kind of record and the record. A manager's own is `learning.direct`. */\nexport interface LearningSource {\n kind: string\n id?: string | null\n}\n\nexport const DIRECT_SOURCE_KIND = 'learning.direct'\n\n/** The learner's screen of one enrolment - where the to-do leads. */\nexport const learningAddressOf = (enrollmentId: string) => `learning-view?id=${enrollmentId}`\n\nexport const todayIn = (domain: { timezone?: string | null }, now: Date = new Date()) => calendarDay(domain?.timezone, now)\n\nconst notFound = () => new Refusal('learning-not-found', {}, 'the record was not found. reload and try again.')\nconst isOpen = (status: string) => (OPEN_ENROLLMENT_STATUSES as string[]).includes(status)\n\n/* ---------------------------------------------------------------- the to-do (decision 5) */\n\n/** The context worklist closes a to-do under - `by` ends or aborts it; worklist speaks its refusals through `t`. */\nfunction todoContextOf(tx: EntityManager, domain: any, user: User) {\n const t = translatorFor(user.locale || 'ko')\n return { state: { domain, user, tx }, t: (key: string, options?: any) => t(key, options) } as any\n}\n\n/**\n * Hands out 「<과정> 수강」 to the person, with the due day - nothing where the domain has not installed the to-do. worklist's\n * handOut: the person's 할 일 and nobody's 상신함, also when the manager assigning it is the person (2026-11-08). The one\n * who asked (`context`) stands as its issuer.\n */\nasync function issueTodo(context: any, enrollment: Enrollment, course: Course, person: User): Promise<string | null> {\n const { domain, tx } = context.state\n const activity = await tx.getRepository(Activity).findOne({ where: { domain: { id: domain.id }, name: LEARNING_COURSE_ACTIVITY } as any })\n if (!activity) {\n logger.warn(`[learning] ${domain.subdomain}: the activity \"${LEARNING_COURSE_ACTIVITY}\" is not installed - \"${course.name}\" not handed out`)\n return null\n }\n const t = translatorFor(person.locale || 'ko')\n const name = t('text.learning-todo-name', { course: course.name })\n const headline = enrollment.dueOn ? t('text.learning-todo-headline', { dueOn: enrollment.dueOn }) : t('text.learning-todo-headline-no-due')\n const dueAt = enrollment.dueOn ? dueAtOf(enrollment.dueOn, domain.timezone) : undefined\n const input = { enrollmentId: enrollment.id, headline, address: learningAddressOf(enrollment.id) }\n const instance = await handOut({ activityId: activity.id, name, description: headline, input, assignee: person, dueAt }, context)\n return instance.id\n}\n\n/** The end of the due day in the domain's time zone. */\nfunction dueAtOf(dueOn: string, timezone?: string | null): Date {\n const end = new Date(`${dueOn}T23:59:59Z`)\n const zone = timezone || 'UTC'\n const asLocal = new Date(end.toLocaleString('en-US', { timeZone: zone }))\n const asUtc = new Date(end.toLocaleString('en-US', { timeZone: 'UTC' }))\n return new Date(end.getTime() - (asLocal.getTime() - asUtc.getTime()))\n}\n\nasync function todoOpen(tx: EntityManager, instanceId: string): Promise<boolean> {\n return (await tx.getRepository(ActivityThread).count({ where: { activityInstance: { id: instanceId }, state: In(['assigned', 'started', 'unassigned']) } as any })) > 0\n}\n\n/** Ends the to-do as done once the course is completed. */\nasync function completeTodo(tx: EntityManager, domain: any, enrollment: Enrollment, by: User): Promise<void> {\n if (!enrollment.todoInstanceId || !(await todoOpen(tx, enrollment.todoInstanceId))) return\n await complete({ id: enrollment.todoInstanceId, reason: 'course completed' }, todoContextOf(tx, domain, by))\n}\n\n/** Aborts the to-do of an enrolment withdrawn or cancelled. */\nasync function abortTodo(tx: EntityManager, domain: any, enrollment: Enrollment, by: User, reason: string): Promise<void> {\n if (!enrollment.todoInstanceId || !(await todoOpen(tx, enrollment.todoInstanceId))) return\n await abort({ id: enrollment.todoInstanceId, reason } as any, todoContextOf(tx, domain, by))\n}\n\n/* ---------------------------------------------------------------- enrolling and withdrawing (decision 7) */\n\n/**\n * Enrols people in a course for whoever asks (`source`), to be completed by `dueOn` - the server function a using module\n * calls with the people it chose. One open enrolment per person, course and source: a person who has one is left with\n * it. Each new enrolment hands out its to-do. Answers the open enrolments of everyone given.\n */\nexport async function enroll(source: LearningSource, userIds: string[], courseId: string, dueOn: string | null | undefined, context: any): Promise<Enrollment[]> {\n const { domain, user, tx } = context.state\n if (!source?.kind) throw new Refusal('learning-source-required', {}, 'an enrolment names who asked for it')\n if (dueOn && !/^\\d{4}-\\d{2}-\\d{2}$/.test(dueOn)) throw new Refusal('learning-due-invalid', {}, 'the due day is YYYY-MM-DD')\n const course = await courseOf(tx, domain, courseId)\n if (!(await publishedVersionOf(tx, domain, course.id))) throw new Refusal('learning-course-not-published', { course: course.name }, `${course.name} has no published version`)\n const ids = [...new Set(userIds)].filter(Boolean)\n if (!ids.length) return []\n const people = await (tx as EntityManager).getRepository(User).find({ where: { id: In(ids) }, relations: ['domains'] })\n const sourceId = source.id ?? ''\n const repository = (tx as EntityManager).getRepository(Enrollment)\n const out: Enrollment[] = []\n for (const id of ids) {\n const person = people.find(one => one.id === id)\n if (!person || !(person.domains ?? []).some(one => one.id === domain.id)) throw new Refusal('learning-learner-unknown', {}, 'a learner must be a user of this domain')\n const open = await repository.findOne({ where: { domain: { id: domain.id }, user: { id }, course: { id: course.id }, sourceKind: source.kind, sourceId, status: In(OPEN_ENROLLMENT_STATUSES) } as any })\n if (open) {\n out.push(open)\n continue\n }\n const saved = await repository.save({ domain, user: person, course, dueOn: dueOn || null, sourceKind: source.kind, sourceId, status: EnrollmentStatus.ASSIGNED, learnedSeconds: 0, attempts: 0, creator: user, updater: user } as any)\n const todo = await issueTodo(context, saved, course, person)\n if (todo) await repository.update(saved.id, { todoInstanceId: todo } as any)\n out.push((await repository.findOneBy({ id: saved.id }))!)\n }\n return out\n}\n\n/**\n * Withdraws the open enrolments a source asked for - the record they were for was taken back. Each is cancelled with\n * the reason and its to-do aborted; completed ones stay. Answers how many were withdrawn.\n */\nexport async function withdraw(source: LearningSource, reason: string, context: any): Promise<number> {\n const { domain, user, tx } = context.state\n const rows = await (tx as EntityManager).getRepository(Enrollment).find({ where: { domain: { id: domain.id }, sourceKind: source.kind, sourceId: source.id ?? '', status: In(OPEN_ENROLLMENT_STATUSES) } as any })\n let count = 0\n for (const row of rows) if (await cancelOpen(tx, domain, row, user, reason)) count++\n return count\n}\n\n/** Cancels an open enrolment - claimed by an update only an open one matches, so a completion at the same time wins once. */\nasync function cancelOpen(tx: EntityManager, domain: any, row: Enrollment, by: User, reason: string): Promise<boolean> {\n const claimed = await tx.getRepository(Enrollment).update({ id: row.id, status: In(OPEN_ENROLLMENT_STATUSES) } as any, { status: EnrollmentStatus.CANCELLED, cancelledAt: new Date(), cancelReason: reason, updater: by } as any)\n if (!claimed.affected) return false\n await abortTodo(tx, domain, row, by, reason)\n return true\n}\n\n/**\n * A manager cancels enrolments with one reason (decision 8) - together: one refused, none is cancelled. An open one is\n * withdrawn; a completed one stays on record as cancelled - never deleted - and whoever asked for it is told in the same\n * transaction.\n */\nexport async function cancelEnrollments(ids: string[], reason: string, context: any): Promise<Enrollment[]> {\n if (!reason?.trim()) throw new Refusal('learning-cancel-reason-required', {}, 'say why it is cancelled')\n const unique = [...new Set(ids ?? [])]\n if (!unique.length) throw new Refusal('learning-cancel-none', {}, 'pick the enrolments to cancel')\n const out: Enrollment[] = []\n for (const id of unique) out.push(await cancelOne(id, reason.trim(), context))\n return out\n}\n\nasync function cancelOne(id: string, reason: string, context: any): Promise<Enrollment> {\n const { domain, user, tx } = context.state\n const row = await enrollmentOf(tx, domain, id)\n if (isOpen(row.status)) {\n if (!(await cancelOpen(tx, domain, row, user, reason))) throw new Refusal('learning-enrollment-moved', {}, 'the enrolment changed meanwhile - reload and try again')\n } else if (row.status === EnrollmentStatus.COMPLETED) {\n const claimed = await tx.getRepository(Enrollment).update({ id: row.id, status: EnrollmentStatus.COMPLETED } as any, { status: EnrollmentStatus.CANCELLED, cancelledAt: new Date(), cancelReason: reason, updater: user } as any)\n if (!claimed.affected) throw new Refusal('learning-enrollment-moved', {}, 'the enrolment changed meanwhile - reload and try again')\n await learningOutcomeOf(row.sourceKind)?.cancelled?.((await enrollmentOf(tx, domain, id))!, reason, context)\n } else throw new Refusal('learning-enrollment-cancelled', {}, 'the enrolment is cancelled already')\n return await enrollmentOf(tx, domain, id)\n}\n\n/**\n * The people an audience names now - the domain, departments with those under them, roles and people, by organization's\n * one rule (ADR-0163). Each target must be of this domain.\n */\nexport async function learnersOf(audiences: { scope: string; targetId: string; targetName?: string }[], context: any): Promise<string[]> {\n const { domain, tx } = context.state\n if (!audiences?.length) throw new Refusal('learning-audience-required', {}, 'pick who takes the course')\n const departmentIds = [...(await departmentTreeOf(domain, tx)).keys()]\n const roleIds = (await domainRolesOf(domain, undefined, tx)).map(one => one.id)\n for (const target of audiences) {\n const why = audienceTargetRefusal(target as any, { domainId: domain.id, departmentIds, roleIds })\n if (why) throw new Refusal('learning-audience-invalid', { target: target.targetName ?? target.targetId }, `${target.targetName ?? target.targetId} is not of this domain (${why})`)\n }\n return await audienceUserIds(domain, audiences as any, tx)\n}\n\n/* ---------------------------------------------------------------- reading */\n\nexport async function enrollmentOf(tx: EntityManager, domain: { id: string }, id: string): Promise<Enrollment> {\n const row = id && /^[0-9a-f-]{36}$/i.test(id) ? await tx.getRepository(Enrollment).findOne({ where: { id, domain: { id: domain.id } } as any }) : null\n if (!row) throw notFound()\n return row\n}\n\n/** The request's own enrolment - one answer for \"none\" and \"not yours\". */\nexport async function myEnrollmentOf(context: any, id: string, tx: EntityManager = learningManagerOf(context)): Promise<Enrollment> {\n const row = await enrollmentOf(tx, context.state.domain, id)\n if (row.userId !== context.state.user.id) throw notFound()\n return row\n}\n\n/** The version an enrolment shows: its own once started, the published one before. */\nexport async function shownVersionOf(tx: EntityManager, domain: { id: string }, enrollment: Enrollment): Promise<CourseVersion | null> {\n if (enrollment.versionId) return await tx.getRepository(CourseVersion).findOne({ where: { id: enrollment.versionId } as any, withDeleted: true })\n return await publishedVersionOf(tx, domain, enrollment.courseId!)\n}\n\nexport async function progressesOf(tx: EntityManager, enrollmentId: string): Promise<UnitProgress[]> {\n return await tx.getRepository(UnitProgress).find({ where: { enrollment: { id: enrollmentId } } as any })\n}\n\n/** What still stands between an enrolment and its completion - nothing for a completed one. */\nexport async function missingFor(tx: EntityManager, domain: { id: string }, enrollment: Enrollment): Promise<Missing[]> {\n if (enrollment.status === EnrollmentStatus.COMPLETED) return []\n const version = await shownVersionOf(tx, domain, enrollment)\n if (!version) return ['units']\n const units = (await unitsOf(tx, version.id)).filter(counts)\n const progress = await progressesOf(tx, enrollment.id)\n const questions = version.passScore != null ? await tx.getRepository(CourseQuestion).count({ where: { version: { id: version.id } } as any }) : 0\n return missingOf({\n units: units.length,\n unitsDone: units.filter(unit => progress.some(one => one.unitId === unit.id && one.doneAt)).length,\n learnedSeconds: enrollment.learnedSeconds,\n minSeconds: version.minSeconds ?? 0,\n quiz: version.passScore != null && questions > 0,\n passed: enrollment.score != null && enrollment.score >= (version.passScore ?? 101),\n acknowledgement: !!version.acknowledgement,\n acknowledged: !!enrollment.acknowledgedAt\n })\n}\n\n/* ---------------------------------------------------------------- learning (decision 6) */\n\n/** The enrolment, started if it was not: its version fixed to the one published now, in progress from now on. */\nasync function started(tx: EntityManager, domain: any, enrollment: Enrollment, user: User): Promise<Enrollment> {\n if (enrollment.status === EnrollmentStatus.CANCELLED) throw new Refusal('learning-enrollment-cancelled', {}, 'the enrolment is cancelled')\n if (enrollment.versionId) {\n if (enrollment.status === EnrollmentStatus.ASSIGNED) await tx.getRepository(Enrollment).update({ id: enrollment.id, status: EnrollmentStatus.ASSIGNED } as any, { status: EnrollmentStatus.IN_PROGRESS, startedAt: new Date() } as any)\n return await enrollmentOf(tx, domain, enrollment.id)\n }\n const version = await publishedVersionOf(tx, domain, enrollment.courseId!)\n if (!version) throw new Refusal('learning-course-not-published', {}, 'the course has no published version')\n await tx.getRepository(Enrollment).update({ id: enrollment.id, status: In(OPEN_ENROLLMENT_STATUSES) } as any, { version, status: EnrollmentStatus.IN_PROGRESS, startedAt: enrollment.startedAt ?? new Date(), updater: user } as any)\n return await enrollmentOf(tx, domain, enrollment.id)\n}\n\nexport async function unitOfVersion(tx: EntityManager, version: { id: string }, unitId: string): Promise<CourseUnit> {\n const unit = await tx.getRepository(CourseUnit).findOne({ where: { id: unitId, version: { id: version.id } } as any })\n if (!unit) throw notFound()\n return unit\n}\n\nexport interface TickResult {\n enrollment: Enrollment\n progress: UnitProgress\n}\n\n/**\n * One tick of the learner's screen on a unit (decision 6): the server adds the gap since the unit's last tick, at most\n * 15 seconds and at most what the screen says, and refuses a video position ahead of what was watched. A total the\n * screen sends is never taken. A unit that reaches its condition is done, and the enrolment is completed when nothing is\n * missing any more.\n */\nexport async function recordTick(enrollmentId: string, unitId: string, seconds: number, position: number, context: any, now: Date = new Date()): Promise<TickResult> {\n const { domain, user, tx } = context.state\n let enrollment = await myEnrollmentOf(context, enrollmentId, tx)\n enrollment = await started(tx, domain, enrollment, user)\n const unit = await unitOfVersion(tx, { id: enrollment.versionId! }, unitId)\n if (unit.kind === CourseUnitKind.LINK) throw new Refusal('learning-link-not-counted', {}, 'a link is not counted')\n const repository = (tx as EntityManager).getRepository(UnitProgress)\n const progress =\n (await repository.findOne({ where: { enrollment: { id: enrollment.id }, unit: { id: unit.id } } as any })) ??\n (await repository.save({ domain, enrollment, unit, learnedSeconds: 0, watchedSeconds: 0, positionSeconds: 0, pagesSeen: 0, pagesRead: 0, pageSeconds: 0, pageAt: 0, creator: user, updater: user } as any))\n if (enrollment.status === EnrollmentStatus.COMPLETED) return { enrollment, progress }\n\n const verdict = tickVerdict(progress, { seconds, position }, now, unit)\n if ('refusal' in verdict) {\n if (verdict.refusal === 'page-ahead') throw new Refusal('learning-page-ahead', { allowed: verdict.allowed }, `a page cannot be ahead of the one after the last read (${verdict.allowed})`)\n throw new Refusal('learning-position-ahead', { allowed: verdict.allowed }, `the video cannot be ahead of what was watched (${verdict.allowed} s)`)\n }\n progress.learnedSeconds += verdict.add\n progress.watchedSeconds = verdict.watchedSeconds\n progress.positionSeconds = verdict.positionSeconds\n progress.pagesSeen = verdict.pagesSeen\n progress.pagesRead = verdict.pagesRead\n progress.pageSeconds = verdict.pageSeconds\n progress.pageAt = verdict.pageAt\n progress.lastTickAt = now\n if (!progress.doneAt && unitDone(unit, progress)) progress.doneAt = now\n progress.updater = user\n await repository.save(progress)\n if (verdict.add) await tx.getRepository(Enrollment).increment({ id: enrollment.id } as any, 'learnedSeconds', verdict.add)\n /* Where the learner was - the course opens on this unit again (2026-11-08). */\n if (enrollment.lastUnitId !== unit.id) await tx.getRepository(Enrollment).update({ id: enrollment.id } as any, { lastUnitId: unit.id } as any)\n enrollment = await completeIfMet(tx, domain, await enrollmentOf(tx, domain, enrollment.id), context)\n return { enrollment, progress }\n}\n\n/** The learner confirms the version's acknowledgement - recorded once, when they did. */\nexport async function acknowledgeEnrollment(enrollmentId: string, context: any): Promise<Enrollment> {\n const { domain, user, tx } = context.state\n let enrollment = await started(tx, domain, await myEnrollmentOf(context, enrollmentId, tx), user)\n const version = await shownVersionOf(tx, domain, enrollment)\n if (!version?.acknowledgement) throw new Refusal('learning-no-acknowledgement', {}, 'the course asks for no acknowledgement')\n if (!enrollment.acknowledgedAt) await tx.getRepository(Enrollment).update({ id: enrollment.id } as any, { acknowledgedAt: new Date(), updater: user } as any)\n return await completeIfMet(tx, domain, await enrollmentOf(tx, domain, enrollment.id), context)\n}\n\n/* ---------------------------------------------------------------- the quiz (decision 4) */\n\nexport interface DrawnForLearner {\n attempt: QuizAttempt\n questions: { id: string; kind: string; text: string; choices: { id: string; text: string }[] }[]\n}\n\n/** The questions of an attempt as the learner is drawn them - the choices in the order shown, never whether one is right. */\nexport function forLearner(attempt: QuizAttempt, questions: CourseQuestion[]): DrawnForLearner {\n return {\n attempt,\n questions: attempt.drawn.map(drawn => {\n const question = questions.find(one => one.id === drawn.questionId)!\n return {\n id: question.id,\n kind: question.kind,\n text: question.text,\n choices: drawn.choiceIds.map(id => question.choices.find(one => one.id === id)!).map(one => ({ id: one.id, text: one.text }))\n }\n })\n }\n}\n\n/**\n * Starts an attempt at the quiz: the questions drawn and shuffled, their choices shuffled. An attempt not sent yet is\n * answered again; the version's limit on attempts holds.\n */\n/**\n * Holds the enrolment for the rest of the request's transaction - an update takes its row lock on every driver, so two\n * attempts of one enrolment are started, counted and sent one after the other. Two starts at once made two unsent\n * attempts, and a limit of one let both be sent (2026-11-08).\n */\nasync function holdEnrollment(tx: EntityManager, domain: any, enrollment: Enrollment, user: User): Promise<Enrollment> {\n await tx.getRepository(Enrollment).update({ id: enrollment.id } as any, { updater: user } as any)\n return await enrollmentOf(tx, domain, enrollment.id)\n}\n\nexport async function startQuizAttempt(enrollmentId: string, context: any): Promise<DrawnForLearner> {\n const { domain, user, tx } = context.state\n const held = await holdEnrollment(tx, domain, await myEnrollmentOf(context, enrollmentId, tx), user)\n const enrollment = await started(tx, domain, held, user)\n if (enrollment.status === EnrollmentStatus.COMPLETED) throw new Refusal('learning-enrollment-completed', {}, 'the course is completed')\n const version = (await shownVersionOf(tx, domain, enrollment))!\n const questions = await questionsOf(tx, version.id)\n if (version.passScore == null || !questions.length) throw new Refusal('learning-no-quiz', {}, 'the course has no quiz')\n const repository = (tx as EntityManager).getRepository(QuizAttempt)\n const pending = await repository.findOne({ where: { enrollment: { id: enrollment.id }, submittedAt: IsNull() } as any })\n if (pending) return forLearner(pending, questions)\n if (version.attemptLimit != null && enrollment.attempts >= version.attemptLimit) throw new Refusal('learning-attempts-used', { limit: version.attemptLimit }, `all ${version.attemptLimit} attempts are used`)\n const attempt = await repository.save({ domain, enrollment, drawn: drawQuestions(questions, version.questionCount), creator: user, updater: user } as any)\n return forLearner(attempt, questions)\n}\n\nexport interface QuizResult {\n enrollment: Enrollment\n score: number\n passed: boolean\n attemptsLeft: number | null\n}\n\n/**\n * Scores an attempt's answers on the server against what it drew (decision 4) - only the score and whether it passed go\n * back, never the right answers. The best score stays on the enrolment; passing may complete it.\n */\nexport async function submitQuizAttempt(attemptId: string, answers: Record<string, string[]>, context: any): Promise<QuizResult> {\n const { domain, user, tx } = context.state\n const attempt = await (tx as EntityManager).getRepository(QuizAttempt).findOne({ where: { id: attemptId, domain: { id: domain.id } } as any })\n if (!attempt) throw notFound()\n const enrollment = await holdEnrollment(tx, domain, await myEnrollmentOf(context, attempt.enrollmentId!, tx), user)\n const version = (await shownVersionOf(tx, domain, enrollment))!\n if (version.attemptLimit != null && enrollment.attempts >= version.attemptLimit) throw new Refusal('learning-attempts-used', { limit: version.attemptLimit }, `all ${version.attemptLimit} attempts are used`)\n const questions = await questionsOf(tx, version.id)\n const drawn = attempt.drawn.map(one => questions.find(question => question.id === one.questionId)).filter(Boolean) as CourseQuestion[]\n const kept: Record<string, string[]> = Object.fromEntries(drawn.map(question => [question.id, (answers?.[question.id] ?? []).map(String)]))\n const score = scoreOf(drawn, kept)\n const passed = score >= (version.passScore ?? 101)\n const claimed = await tx.getRepository(QuizAttempt).update({ id: attempt.id, submittedAt: IsNull() } as any, { answers: kept, score, passed, submittedAt: new Date(), updater: user } as any)\n if (!claimed.affected) throw new Refusal('learning-attempt-sent', {}, 'the attempt was sent already')\n await tx.getRepository(Enrollment).increment({ id: enrollment.id } as any, 'attempts', 1)\n await tx.getRepository(Enrollment).update({ id: enrollment.id } as any, { score: Math.max(enrollment.score ?? 0, score), updater: user } as any)\n const after = await completeIfMet(tx, domain, await enrollmentOf(tx, domain, enrollment.id), context)\n return { enrollment: after, score, passed, attemptsLeft: version.attemptLimit != null ? Math.max(0, version.attemptLimit - after.attempts) : null }\n}\n\n/* ---------------------------------------------------------------- completing (decision 8) */\n\n/** A certificate number: the day completed and the enrolment's own id - one per completion, never reused. */\nexport const certificateNoOf = (enrollment: { id: string }, day: string) => `LMS-${day.replace(/-/g, '')}-${enrollment.id.replace(/-/g, '').slice(0, 8).toUpperCase()}`\n\n/**\n * Completes an open enrolment when nothing is missing - every unit done, the least time, the quiz passed, the\n * acknowledgement confirmed. The evidence stays on it; whoever asked for it is told in this same transaction (a handler\n * that fails takes the completion back), and the to-do ends as done.\n */\nexport async function completeIfMet(tx: EntityManager, domain: any, enrollment: Enrollment, context: any): Promise<Enrollment> {\n if (!isOpen(enrollment.status) || !enrollment.versionId) return enrollment\n if ((await missingFor(tx, domain, enrollment)).length) return enrollment\n const now = new Date()\n const claimed = await tx\n .getRepository(Enrollment)\n .update({ id: enrollment.id, status: In(OPEN_ENROLLMENT_STATUSES) } as any, { status: EnrollmentStatus.COMPLETED, completedAt: now, certificateNo: certificateNoOf(enrollment, todayIn(domain, now)), updater: context.state.user } as any)\n if (!claimed.affected) return await enrollmentOf(tx, domain, enrollment.id)\n const completed = await enrollmentOf(tx, domain, enrollment.id)\n await learningOutcomeOf(completed.sourceKind)?.completed(completed, context)\n await completeTodo(tx, domain, completed, context.state.user)\n return completed\n}\n\n/* ---------------------------------------------------------------- whom an enrolment is for */\n\n/** The open and completed enrolments of the request's person, the open first and the soonest due first. */\nexport async function myEnrollments(context: any): Promise<Enrollment[]> {\n const { domain, user } = context.state\n const rows = await learningManagerOf(context)\n .getRepository(Enrollment)\n .find({ where: { domain: { id: domain.id }, user: { id: user.id }, status: In([...OPEN_ENROLLMENT_STATUSES, EnrollmentStatus.COMPLETED]) } as any })\n const rank = (one: Enrollment) => (isOpen(one.status) ? 0 : 1)\n return rows.sort((a, b) => rank(a) - rank(b) || String(a.dueOn ?? '9999').localeCompare(String(b.dueOn ?? '9999')) || +new Date(b.createdAt!) - +new Date(a.createdAt!))\n}\n\nexport { isOpen }\n"]}
@@ -1,15 +1,17 @@
1
1
  import { Enrollment } from './enrollment';
2
2
  import { QuizAttempt } from './quiz-attempt';
3
3
  import { UnitProgress } from './unit-progress';
4
+ import { LessonNote } from './lesson-note';
4
5
  import { EnrollmentMutation } from './enrollment-mutation';
5
6
  import { CourseEnrollmentFields, EnrollmentQuery } from './enrollment-query';
6
7
  export * from './enrollment';
7
8
  export * from './quiz-attempt';
8
9
  export * from './unit-progress';
10
+ export * from './lesson-note';
9
11
  export * from './learning-outcome';
10
12
  export { cancelEnrollments, DIRECT_SOURCE_KIND, enroll, learningAddressOf, withdraw } from './enrollment-service';
11
13
  export type { LearningSource } from './enrollment-service';
12
14
  export { LEARNING_CERTIFICATE_KIND, LEARNING_REGISTER_KIND } from './learning-documents';
13
- export declare const entities: (typeof Enrollment | typeof QuizAttempt | typeof UnitProgress)[];
15
+ export declare const entities: (typeof Enrollment | typeof QuizAttempt | typeof UnitProgress | typeof LessonNote)[];
14
16
  export declare const resolvers: (typeof EnrollmentMutation | typeof EnrollmentQuery | typeof CourseEnrollmentFields)[];
15
17
  export declare const subscribers: any[];
@@ -5,11 +5,13 @@ const tslib_1 = require("tslib");
5
5
  const enrollment_1 = require("./enrollment");
6
6
  const quiz_attempt_1 = require("./quiz-attempt");
7
7
  const unit_progress_1 = require("./unit-progress");
8
+ const lesson_note_1 = require("./lesson-note");
8
9
  const enrollment_mutation_1 = require("./enrollment-mutation");
9
10
  const enrollment_query_1 = require("./enrollment-query");
10
11
  tslib_1.__exportStar(require("./enrollment"), exports);
11
12
  tslib_1.__exportStar(require("./quiz-attempt"), exports);
12
13
  tslib_1.__exportStar(require("./unit-progress"), exports);
14
+ tslib_1.__exportStar(require("./lesson-note"), exports);
13
15
  tslib_1.__exportStar(require("./learning-outcome"), exports);
14
16
  var enrollment_service_1 = require("./enrollment-service");
15
17
  Object.defineProperty(exports, "cancelEnrollments", { enumerable: true, get: function () { return enrollment_service_1.cancelEnrollments; } });
@@ -20,7 +22,7 @@ Object.defineProperty(exports, "withdraw", { enumerable: true, get: function ()
20
22
  var learning_documents_1 = require("./learning-documents");
21
23
  Object.defineProperty(exports, "LEARNING_CERTIFICATE_KIND", { enumerable: true, get: function () { return learning_documents_1.LEARNING_CERTIFICATE_KIND; } });
22
24
  Object.defineProperty(exports, "LEARNING_REGISTER_KIND", { enumerable: true, get: function () { return learning_documents_1.LEARNING_REGISTER_KIND; } });
23
- exports.entities = [enrollment_1.Enrollment, unit_progress_1.UnitProgress, quiz_attempt_1.QuizAttempt];
25
+ exports.entities = [enrollment_1.Enrollment, unit_progress_1.UnitProgress, quiz_attempt_1.QuizAttempt, lesson_note_1.LessonNote];
24
26
  exports.resolvers = [enrollment_query_1.EnrollmentQuery, enrollment_query_1.CourseEnrollmentFields, enrollment_mutation_1.EnrollmentMutation];
25
27
  exports.subscribers = [];
26
28
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/service/enrollment/index.ts"],"names":[],"mappings":";;;;AAAA,6CAAyC;AACzC,iDAA4C;AAC5C,mDAA8C;AAC9C,+DAA0D;AAC1D,yDAA4E;AAE5E,uDAA4B;AAC5B,yDAA8B;AAC9B,0DAA+B;AAC/B,6DAAkC;AAClC,2DAAiH;AAAxG,uHAAA,iBAAiB,OAAA;AAAE,wHAAA,kBAAkB,OAAA;AAAE,4GAAA,MAAM,OAAA;AAAE,uHAAA,iBAAiB,OAAA;AAAE,8GAAA,QAAQ,OAAA;AAEnF,2DAAwF;AAA/E,+HAAA,yBAAyB,OAAA;AAAE,4HAAA,sBAAsB,OAAA;AAE7C,QAAA,QAAQ,GAAG,CAAC,uBAAU,EAAE,4BAAY,EAAE,0BAAW,CAAC,CAAA;AAClD,QAAA,SAAS,GAAG,CAAC,kCAAe,EAAE,yCAAsB,EAAE,wCAAkB,CAAC,CAAA;AACzE,QAAA,WAAW,GAAG,EAAE,CAAA","sourcesContent":["import { Enrollment } from './enrollment'\nimport { QuizAttempt } from './quiz-attempt'\nimport { UnitProgress } from './unit-progress'\nimport { EnrollmentMutation } from './enrollment-mutation'\nimport { CourseEnrollmentFields, EnrollmentQuery } from './enrollment-query'\n\nexport * from './enrollment'\nexport * from './quiz-attempt'\nexport * from './unit-progress'\nexport * from './learning-outcome'\nexport { cancelEnrollments, DIRECT_SOURCE_KIND, enroll, learningAddressOf, withdraw } from './enrollment-service'\nexport type { LearningSource } from './enrollment-service'\nexport { LEARNING_CERTIFICATE_KIND, LEARNING_REGISTER_KIND } from './learning-documents'\n\nexport const entities = [Enrollment, UnitProgress, QuizAttempt]\nexport const resolvers = [EnrollmentQuery, CourseEnrollmentFields, EnrollmentMutation]\nexport const subscribers = []\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/service/enrollment/index.ts"],"names":[],"mappings":";;;;AAAA,6CAAyC;AACzC,iDAA4C;AAC5C,mDAA8C;AAC9C,+CAA0C;AAC1C,+DAA0D;AAC1D,yDAA4E;AAE5E,uDAA4B;AAC5B,yDAA8B;AAC9B,0DAA+B;AAC/B,wDAA6B;AAC7B,6DAAkC;AAClC,2DAAiH;AAAxG,uHAAA,iBAAiB,OAAA;AAAE,wHAAA,kBAAkB,OAAA;AAAE,4GAAA,MAAM,OAAA;AAAE,uHAAA,iBAAiB,OAAA;AAAE,8GAAA,QAAQ,OAAA;AAEnF,2DAAwF;AAA/E,+HAAA,yBAAyB,OAAA;AAAE,4HAAA,sBAAsB,OAAA;AAE7C,QAAA,QAAQ,GAAG,CAAC,uBAAU,EAAE,4BAAY,EAAE,0BAAW,EAAE,wBAAU,CAAC,CAAA;AAC9D,QAAA,SAAS,GAAG,CAAC,kCAAe,EAAE,yCAAsB,EAAE,wCAAkB,CAAC,CAAA;AACzE,QAAA,WAAW,GAAG,EAAE,CAAA","sourcesContent":["import { Enrollment } from './enrollment'\nimport { QuizAttempt } from './quiz-attempt'\nimport { UnitProgress } from './unit-progress'\nimport { LessonNote } from './lesson-note'\nimport { EnrollmentMutation } from './enrollment-mutation'\nimport { CourseEnrollmentFields, EnrollmentQuery } from './enrollment-query'\n\nexport * from './enrollment'\nexport * from './quiz-attempt'\nexport * from './unit-progress'\nexport * from './lesson-note'\nexport * from './learning-outcome'\nexport { cancelEnrollments, DIRECT_SOURCE_KIND, enroll, learningAddressOf, withdraw } from './enrollment-service'\nexport type { LearningSource } from './enrollment-service'\nexport { LEARNING_CERTIFICATE_KIND, LEARNING_REGISTER_KIND } from './learning-documents'\n\nexport const entities = [Enrollment, UnitProgress, QuizAttempt, LessonNote]\nexport const resolvers = [EnrollmentQuery, CourseEnrollmentFields, EnrollmentMutation]\nexport const subscribers = []\n"]}
@@ -0,0 +1,15 @@
1
+ import { LessonNote, LessonNoteStroke } from './lesson-note';
2
+ /** What one page's note may hold (ADR-0194 amendment 2-4). */
3
+ export declare const NOTE_LIMITS: {
4
+ strokes: number;
5
+ points: number;
6
+ };
7
+ /** The strokes as they are kept - each checked; anything else is refused, never mended. */
8
+ export declare function noteStrokesOf(strokes: unknown): LessonNoteStroke[];
9
+ /** The learner's notes on a unit of their own enrolment, by page - none before it started. */
10
+ export declare function myLessonNotes(enrollmentId: string, unitId: string, context: any): Promise<LessonNote[]>;
11
+ /**
12
+ * Keeps a page's strokes - the whole page each time, as the screen has it after a stroke. The page is one of the unit's;
13
+ * the enrolment the learner's own and not cancelled. A completed enrolment still takes notes (amendment 2-7).
14
+ */
15
+ export declare function saveLessonNote(enrollmentId: string, unitId: string, page: number, strokes: unknown, context: any): Promise<LessonNote>;
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NOTE_LIMITS = void 0;
4
+ exports.noteStrokesOf = noteStrokesOf;
5
+ exports.myLessonNotes = myLessonNotes;
6
+ exports.saveLessonNote = saveLessonNote;
7
+ const shell_1 = require("@things-factory/shell");
8
+ const course_service_1 = require("../course/course-service");
9
+ const course_unit_1 = require("../course/course-unit");
10
+ const enrollment_1 = require("./enrollment");
11
+ const enrollment_service_1 = require("./enrollment-service");
12
+ const lesson_note_1 = require("./lesson-note");
13
+ /** What one page's note may hold (ADR-0194 amendment 2-4). */
14
+ exports.NOTE_LIMITS = { strokes: 1000, points: 50000 };
15
+ const TOOLS = ['pen', 'highlighter'];
16
+ const COLOR = /^#[0-9a-f]{6}$/i;
17
+ /** The strokes as they are kept - each checked; anything else is refused, never mended. */
18
+ function noteStrokesOf(strokes) {
19
+ if (!Array.isArray(strokes))
20
+ throw new shell_1.Refusal('learning-note-invalid', {}, 'the strokes are not a list');
21
+ if (strokes.length > exports.NOTE_LIMITS.strokes)
22
+ throw new shell_1.Refusal('learning-note-too-large', exports.NOTE_LIMITS, `more than ${exports.NOTE_LIMITS.strokes} strokes on a page`);
23
+ let points = 0;
24
+ const kept = strokes.map((one) => {
25
+ const fine = one &&
26
+ TOOLS.includes(one.tool) &&
27
+ typeof one.color === 'string' &&
28
+ COLOR.test(one.color) &&
29
+ typeof one.width === 'number' &&
30
+ one.width > 0 &&
31
+ one.width <= 0.05 &&
32
+ Array.isArray(one.points) &&
33
+ one.points.length >= 2 &&
34
+ one.points.length % 2 === 0 &&
35
+ one.points.every((value) => typeof value === 'number' && value >= 0 && value <= 1);
36
+ if (!fine)
37
+ throw new shell_1.Refusal('learning-note-invalid', {}, 'a stroke is not one the page writes');
38
+ points += one.points.length / 2;
39
+ return { tool: one.tool, color: one.color, width: one.width, points: one.points };
40
+ });
41
+ if (points > exports.NOTE_LIMITS.points)
42
+ throw new shell_1.Refusal('learning-note-too-large', exports.NOTE_LIMITS, `more than ${exports.NOTE_LIMITS.points} points on a page`);
43
+ return kept;
44
+ }
45
+ /** The PDF unit of the learner's own enrolment, its version's - a cancelled enrolment takes no notes. */
46
+ async function notedUnitOf(tx, context, enrollmentId, unitId) {
47
+ const enrollment = await (0, enrollment_service_1.myEnrollmentOf)(context, enrollmentId, tx);
48
+ if (!enrollment.versionId)
49
+ throw new shell_1.Refusal('learning-note-not-started', {}, 'the enrolment has not started');
50
+ const unit = await (0, enrollment_service_1.unitOfVersion)(tx, { id: enrollment.versionId }, unitId);
51
+ if (unit.kind !== course_unit_1.CourseUnitKind.PDF)
52
+ throw new shell_1.Refusal('learning-note-pdf-only', {}, 'notes are written over the pages of a PDF');
53
+ return { enrollment, unit };
54
+ }
55
+ /** The learner's notes on a unit of their own enrolment, by page - none before it started. */
56
+ async function myLessonNotes(enrollmentId, unitId, context) {
57
+ const tx = (0, course_service_1.learningManagerOf)(context);
58
+ /* Not started yet - the course opens and starts it right after this read: nothing is written yet (2026-11-08). */
59
+ if (!(await (0, enrollment_service_1.myEnrollmentOf)(context, enrollmentId, tx)).versionId)
60
+ return [];
61
+ const { enrollment, unit } = await notedUnitOf(tx, context, enrollmentId, unitId);
62
+ return await tx
63
+ .getRepository(lesson_note_1.LessonNote)
64
+ .find({ where: { enrollment: { id: enrollment.id }, unit: { id: unit.id } }, order: { page: 'ASC' } });
65
+ }
66
+ /**
67
+ * Keeps a page's strokes - the whole page each time, as the screen has it after a stroke. The page is one of the unit's;
68
+ * the enrolment the learner's own and not cancelled. A completed enrolment still takes notes (amendment 2-7).
69
+ */
70
+ async function saveLessonNote(enrollmentId, unitId, page, strokes, context) {
71
+ const { domain, user, tx } = context.state;
72
+ const { enrollment, unit } = await notedUnitOf(tx, context, enrollmentId, unitId);
73
+ if (enrollment.status === enrollment_1.EnrollmentStatus.CANCELLED)
74
+ throw new shell_1.Refusal('learning-enrollment-cancelled', {}, 'the enrolment is cancelled');
75
+ if (!Number.isInteger(page) || page < 1 || (unit.pageCount && page > unit.pageCount))
76
+ throw new shell_1.Refusal('learning-note-page', { pages: unit.pageCount ?? 0 }, `no page ${page} in the document`);
77
+ const kept = noteStrokesOf(strokes);
78
+ const repository = tx.getRepository(lesson_note_1.LessonNote);
79
+ const row = await repository.findOne({
80
+ where: { enrollment: { id: enrollment.id }, unit: { id: unit.id }, page }
81
+ });
82
+ if (row)
83
+ return await repository.save({ ...row, strokes: kept, updater: user });
84
+ return await repository.save({ domain, enrollment, unit, page, strokes: kept, creator: user, updater: user });
85
+ }
86
+ //# sourceMappingURL=lesson-note-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lesson-note-service.js","sourceRoot":"","sources":["../../../server/service/enrollment/lesson-note-service.ts"],"names":[],"mappings":"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","sourcesContent":["import { EntityManager } from 'typeorm'\n\nimport { Refusal } from '@things-factory/shell'\n\nimport { learningManagerOf } from '../course/course-service'\nimport { CourseUnitKind } from '../course/course-unit'\nimport { EnrollmentStatus } from './enrollment'\nimport { myEnrollmentOf, unitOfVersion } from './enrollment-service'\nimport { LessonNote, LessonNoteStroke } from './lesson-note'\n\n/** What one page's note may hold (ADR-0194 amendment 2-4). */\nexport const NOTE_LIMITS = { strokes: 1000, points: 50000 }\n\nconst TOOLS = ['pen', 'highlighter']\nconst COLOR = /^#[0-9a-f]{6}$/i\n\n/** The strokes as they are kept - each checked; anything else is refused, never mended. */\nexport function noteStrokesOf(strokes: unknown): LessonNoteStroke[] {\n if (!Array.isArray(strokes)) throw new Refusal('learning-note-invalid', {}, 'the strokes are not a list')\n if (strokes.length > NOTE_LIMITS.strokes)\n throw new Refusal('learning-note-too-large', NOTE_LIMITS, `more than ${NOTE_LIMITS.strokes} strokes on a page`)\n let points = 0\n const kept = strokes.map((one: any) => {\n const fine =\n one &&\n TOOLS.includes(one.tool) &&\n typeof one.color === 'string' &&\n COLOR.test(one.color) &&\n typeof one.width === 'number' &&\n one.width > 0 &&\n one.width <= 0.05 &&\n Array.isArray(one.points) &&\n one.points.length >= 2 &&\n one.points.length % 2 === 0 &&\n one.points.every((value: unknown) => typeof value === 'number' && value >= 0 && value <= 1)\n if (!fine) throw new Refusal('learning-note-invalid', {}, 'a stroke is not one the page writes')\n points += one.points.length / 2\n return { tool: one.tool, color: one.color, width: one.width, points: one.points }\n })\n if (points > NOTE_LIMITS.points)\n throw new Refusal('learning-note-too-large', NOTE_LIMITS, `more than ${NOTE_LIMITS.points} points on a page`)\n return kept\n}\n\n/** The PDF unit of the learner's own enrolment, its version's - a cancelled enrolment takes no notes. */\nasync function notedUnitOf(tx: EntityManager, context: any, enrollmentId: string, unitId: string) {\n const enrollment = await myEnrollmentOf(context, enrollmentId, tx)\n if (!enrollment.versionId) throw new Refusal('learning-note-not-started', {}, 'the enrolment has not started')\n const unit = await unitOfVersion(tx, { id: enrollment.versionId }, unitId)\n if (unit.kind !== CourseUnitKind.PDF)\n throw new Refusal('learning-note-pdf-only', {}, 'notes are written over the pages of a PDF')\n return { enrollment, unit }\n}\n\n/** The learner's notes on a unit of their own enrolment, by page - none before it started. */\nexport async function myLessonNotes(enrollmentId: string, unitId: string, context: any): Promise<LessonNote[]> {\n const tx = learningManagerOf(context)\n /* Not started yet - the course opens and starts it right after this read: nothing is written yet (2026-11-08). */\n if (!(await myEnrollmentOf(context, enrollmentId, tx)).versionId) return []\n const { enrollment, unit } = await notedUnitOf(tx, context, enrollmentId, unitId)\n return await tx\n .getRepository(LessonNote)\n .find({ where: { enrollment: { id: enrollment.id }, unit: { id: unit.id } } as any, order: { page: 'ASC' } })\n}\n\n/**\n * Keeps a page's strokes - the whole page each time, as the screen has it after a stroke. The page is one of the unit's;\n * the enrolment the learner's own and not cancelled. A completed enrolment still takes notes (amendment 2-7).\n */\nexport async function saveLessonNote(\n enrollmentId: string,\n unitId: string,\n page: number,\n strokes: unknown,\n context: any\n): Promise<LessonNote> {\n const { domain, user, tx } = context.state\n const { enrollment, unit } = await notedUnitOf(tx, context, enrollmentId, unitId)\n if (enrollment.status === EnrollmentStatus.CANCELLED)\n throw new Refusal('learning-enrollment-cancelled', {}, 'the enrolment is cancelled')\n if (!Number.isInteger(page) || page < 1 || (unit.pageCount && page > unit.pageCount))\n throw new Refusal('learning-note-page', { pages: unit.pageCount ?? 0 }, `no page ${page} in the document`)\n const kept = noteStrokesOf(strokes)\n const repository = (tx as EntityManager).getRepository(LessonNote)\n const row = await repository.findOne({\n where: { enrollment: { id: enrollment.id }, unit: { id: unit.id }, page } as any\n })\n if (row) return await repository.save({ ...row, strokes: kept, updater: user } as any)\n return await repository.save({ domain, enrollment, unit, page, strokes: kept, creator: user, updater: user } as any)\n}\n"]}
@@ -0,0 +1,35 @@
1
+ import { User } from '@things-factory/auth-base';
2
+ import { Domain } from '@things-factory/shell';
3
+ import { CourseUnit } from '../course/course-unit';
4
+ import { Enrollment } from './enrollment';
5
+ /**
6
+ * 노트 (ADR-0194 amendment 2-3~7) - what a learner wrote over one page of a PDF unit in one enrolment: pen and
7
+ * highlighter strokes, their points as fractions of the page. One per enrolment, unit and page. The learner's own -
8
+ * read and written by them alone, never by a manager or a department head, and no part of a completion or its evidence.
9
+ * An enrolment takes one version, so a note stays on the page it was written on; a new version is a new enrolment.
10
+ */
11
+ export declare class LessonNote {
12
+ readonly id: string;
13
+ domain?: Domain;
14
+ domainId?: string;
15
+ enrollment?: Enrollment;
16
+ enrollmentId?: string;
17
+ unit?: CourseUnit;
18
+ unitId?: string;
19
+ page: number;
20
+ strokes: LessonNoteStroke[];
21
+ createdAt?: Date;
22
+ updatedAt?: Date;
23
+ deletedAt?: Date;
24
+ creator?: User;
25
+ creatorId?: string;
26
+ updater?: User;
27
+ updaterId?: string;
28
+ }
29
+ /** One stroke of a note - `@operato/learning`'s InkStroke. */
30
+ export interface LessonNoteStroke {
31
+ tool: 'pen' | 'highlighter';
32
+ color: string;
33
+ width: number;
34
+ points: number[];
35
+ }
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LessonNote = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const type_graphql_1 = require("type-graphql");
6
+ const typeorm_1 = require("typeorm");
7
+ const auth_base_1 = require("@things-factory/auth-base");
8
+ const shell_1 = require("@things-factory/shell");
9
+ const course_unit_1 = require("../course/course-unit");
10
+ const enrollment_1 = require("./enrollment");
11
+ /**
12
+ * 노트 (ADR-0194 amendment 2-3~7) - what a learner wrote over one page of a PDF unit in one enrolment: pen and
13
+ * highlighter strokes, their points as fractions of the page. One per enrolment, unit and page. The learner's own -
14
+ * read and written by them alone, never by a manager or a department head, and no part of a completion or its evidence.
15
+ * An enrolment takes one version, so a note stays on the page it was written on; a new version is a new enrolment.
16
+ */
17
+ let LessonNote = class LessonNote {
18
+ };
19
+ exports.LessonNote = LessonNote;
20
+ tslib_1.__decorate([
21
+ (0, typeorm_1.PrimaryGeneratedColumn)('uuid'),
22
+ (0, type_graphql_1.Field)(type => type_graphql_1.ID, { description: 'Note id.' }),
23
+ tslib_1.__metadata("design:type", String)
24
+ ], LessonNote.prototype, "id", void 0);
25
+ tslib_1.__decorate([
26
+ (0, typeorm_1.ManyToOne)(type => shell_1.Domain),
27
+ (0, type_graphql_1.Field)(type => shell_1.Domain, { nullable: true, description: 'The domain.' }),
28
+ tslib_1.__metadata("design:type", shell_1.Domain)
29
+ ], LessonNote.prototype, "domain", void 0);
30
+ tslib_1.__decorate([
31
+ (0, typeorm_1.RelationId)((row) => row.domain),
32
+ tslib_1.__metadata("design:type", String)
33
+ ], LessonNote.prototype, "domainId", void 0);
34
+ tslib_1.__decorate([
35
+ (0, typeorm_1.ManyToOne)(type => enrollment_1.Enrollment),
36
+ (0, type_graphql_1.Field)(type => enrollment_1.Enrollment, { nullable: true, description: 'The enrolment.' }),
37
+ tslib_1.__metadata("design:type", enrollment_1.Enrollment)
38
+ ], LessonNote.prototype, "enrollment", void 0);
39
+ tslib_1.__decorate([
40
+ (0, typeorm_1.RelationId)((row) => row.enrollment),
41
+ tslib_1.__metadata("design:type", String)
42
+ ], LessonNote.prototype, "enrollmentId", void 0);
43
+ tslib_1.__decorate([
44
+ (0, typeorm_1.ManyToOne)(type => course_unit_1.CourseUnit),
45
+ (0, type_graphql_1.Field)(type => course_unit_1.CourseUnit, { nullable: true, description: 'The unit.' }),
46
+ tslib_1.__metadata("design:type", course_unit_1.CourseUnit)
47
+ ], LessonNote.prototype, "unit", void 0);
48
+ tslib_1.__decorate([
49
+ (0, typeorm_1.RelationId)((row) => row.unit),
50
+ tslib_1.__metadata("design:type", String)
51
+ ], LessonNote.prototype, "unitId", void 0);
52
+ tslib_1.__decorate([
53
+ (0, typeorm_1.Column)({ type: 'int' }),
54
+ (0, type_graphql_1.Field)(type => type_graphql_1.Int, { description: 'The page of the PDF.' }),
55
+ tslib_1.__metadata("design:type", Number)
56
+ ], LessonNote.prototype, "page", void 0);
57
+ tslib_1.__decorate([
58
+ (0, typeorm_1.Column)({ type: 'simple-json' }),
59
+ (0, type_graphql_1.Field)(type => shell_1.ScalarObject, {
60
+ description: 'The strokes - each { tool: pen | highlighter, color, width, points: [x, y, ...] }, as fractions of the page.'
61
+ }),
62
+ tslib_1.__metadata("design:type", Array)
63
+ ], LessonNote.prototype, "strokes", void 0);
64
+ tslib_1.__decorate([
65
+ (0, typeorm_1.CreateDateColumn)(),
66
+ (0, type_graphql_1.Field)({ nullable: true, description: 'Creation timestamp.' }),
67
+ tslib_1.__metadata("design:type", Date)
68
+ ], LessonNote.prototype, "createdAt", void 0);
69
+ tslib_1.__decorate([
70
+ (0, typeorm_1.UpdateDateColumn)(),
71
+ (0, type_graphql_1.Field)({ nullable: true, description: 'Last update timestamp.' }),
72
+ tslib_1.__metadata("design:type", Date)
73
+ ], LessonNote.prototype, "updatedAt", void 0);
74
+ tslib_1.__decorate([
75
+ (0, typeorm_1.DeleteDateColumn)(),
76
+ (0, type_graphql_1.Field)({ nullable: true, description: 'Soft delete timestamp.' }),
77
+ tslib_1.__metadata("design:type", Date)
78
+ ], LessonNote.prototype, "deletedAt", void 0);
79
+ tslib_1.__decorate([
80
+ (0, typeorm_1.ManyToOne)(type => auth_base_1.User, { nullable: true }),
81
+ (0, type_graphql_1.Field)(type => auth_base_1.User, { nullable: true, description: 'Who made it.' }),
82
+ tslib_1.__metadata("design:type", auth_base_1.User)
83
+ ], LessonNote.prototype, "creator", void 0);
84
+ tslib_1.__decorate([
85
+ (0, typeorm_1.RelationId)((row) => row.creator),
86
+ tslib_1.__metadata("design:type", String)
87
+ ], LessonNote.prototype, "creatorId", void 0);
88
+ tslib_1.__decorate([
89
+ (0, typeorm_1.ManyToOne)(type => auth_base_1.User, { nullable: true }),
90
+ (0, type_graphql_1.Field)(type => auth_base_1.User, { nullable: true, description: 'Who changed it last.' }),
91
+ tslib_1.__metadata("design:type", auth_base_1.User)
92
+ ], LessonNote.prototype, "updater", void 0);
93
+ tslib_1.__decorate([
94
+ (0, typeorm_1.RelationId)((row) => row.updater),
95
+ tslib_1.__metadata("design:type", String)
96
+ ], LessonNote.prototype, "updaterId", void 0);
97
+ exports.LessonNote = LessonNote = tslib_1.__decorate([
98
+ (0, typeorm_1.Entity)({ name: 'learning_lesson_notes' }),
99
+ (0, typeorm_1.Index)('ix_learning_lesson_note_0', (row) => [row.domain, row.enrollment, row.unit, row.page], {
100
+ unique: true,
101
+ where: '"deleted_at" IS NULL'
102
+ }),
103
+ (0, type_graphql_1.ObjectType)({
104
+ description: "A learner's notes over one page of a PDF unit - pen and highlighter strokes, theirs alone."
105
+ })
106
+ ], LessonNote);
107
+ //# sourceMappingURL=lesson-note.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lesson-note.js","sourceRoot":"","sources":["../../../server/service/enrollment/lesson-note.ts"],"names":[],"mappings":"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","sourcesContent":["import { Field, ID, Int, ObjectType } from 'type-graphql'\nimport {\n Column,\n CreateDateColumn,\n DeleteDateColumn,\n Entity,\n Index,\n ManyToOne,\n PrimaryGeneratedColumn,\n RelationId,\n UpdateDateColumn\n} from 'typeorm'\n\nimport { User } from '@things-factory/auth-base'\nimport { Domain, ScalarObject } from '@things-factory/shell'\n\nimport { CourseUnit } from '../course/course-unit'\nimport { Enrollment } from './enrollment'\n\n/**\n * 노트 (ADR-0194 amendment 2-3~7) - what a learner wrote over one page of a PDF unit in one enrolment: pen and\n * highlighter strokes, their points as fractions of the page. One per enrolment, unit and page. The learner's own -\n * read and written by them alone, never by a manager or a department head, and no part of a completion or its evidence.\n * An enrolment takes one version, so a note stays on the page it was written on; a new version is a new enrolment.\n */\n@Entity({ name: 'learning_lesson_notes' })\n@Index('ix_learning_lesson_note_0', (row: LessonNote) => [row.domain, row.enrollment, row.unit, row.page], {\n unique: true,\n where: '\"deleted_at\" IS NULL'\n})\n@ObjectType({\n description: \"A learner's notes over one page of a PDF unit - pen and highlighter strokes, theirs alone.\"\n})\nexport class LessonNote {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID, { description: 'Note id.' })\n readonly id: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { nullable: true, description: 'The domain.' })\n domain?: Domain\n\n @RelationId((row: LessonNote) => row.domain)\n domainId?: string\n\n @ManyToOne(type => Enrollment)\n @Field(type => Enrollment, { nullable: true, description: 'The enrolment.' })\n enrollment?: Enrollment\n\n @RelationId((row: LessonNote) => row.enrollment)\n enrollmentId?: string\n\n @ManyToOne(type => CourseUnit)\n @Field(type => CourseUnit, { nullable: true, description: 'The unit.' })\n unit?: CourseUnit\n\n @RelationId((row: LessonNote) => row.unit)\n unitId?: string\n\n @Column({ type: 'int' })\n @Field(type => Int, { description: 'The page of the PDF.' })\n page: number\n\n @Column({ type: 'simple-json' })\n @Field(type => ScalarObject, {\n description:\n 'The strokes - each { tool: pen | highlighter, color, width, points: [x, y, ...] }, as fractions of the page.'\n })\n strokes: LessonNoteStroke[]\n\n @CreateDateColumn()\n @Field({ nullable: true, description: 'Creation timestamp.' })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true, description: 'Last update timestamp.' })\n updatedAt?: Date\n\n @DeleteDateColumn()\n @Field({ nullable: true, description: 'Soft delete timestamp.' })\n deletedAt?: Date\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'Who made it.' })\n creator?: User\n\n @RelationId((row: LessonNote) => row.creator)\n creatorId?: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'Who changed it last.' })\n updater?: User\n\n @RelationId((row: LessonNote) => row.updater)\n updaterId?: string\n}\n\n/** One stroke of a note - `@operato/learning`'s InkStroke. */\nexport interface LessonNoteStroke {\n tool: 'pen' | 'highlighter'\n color: string\n width: number\n points: number[]\n}\n"]}
@@ -6,9 +6,10 @@ export { COURSE_UNIT_REF_TYPE, publishedVersionOf, unitsOf, questionsOf, addDraf
6
6
  export * from './enrollment/enrollment';
7
7
  export * from './enrollment/quiz-attempt';
8
8
  export * from './enrollment/unit-progress';
9
+ export * from './enrollment/lesson-note';
9
10
  export * from './enrollment/learning-outcome';
10
11
  export { cancelEnrollments, DIRECT_SOURCE_KIND, enroll, learningAddressOf, withdraw, type LearningSource, LEARNING_CERTIFICATE_KIND, LEARNING_REGISTER_KIND } from './enrollment';
11
- export declare const entities: (typeof import("./course").Course | typeof import("./course").CourseVersion | typeof import("./course").CourseUnit | typeof import("./course").CourseQuestion | typeof import("./enrollment").Enrollment | typeof import("./enrollment").QuizAttempt | typeof import("./enrollment").UnitProgress)[];
12
+ export declare const entities: (typeof import("./course").Course | typeof import("./course").CourseVersion | typeof import("./course").CourseUnit | typeof import("./course").CourseQuestion | typeof import("./enrollment").Enrollment | typeof import("./enrollment").QuizAttempt | typeof import("./enrollment").UnitProgress | typeof import("./enrollment").LessonNote)[];
12
13
  export declare const schema: {
13
14
  resolverClasses: (typeof import("./course/material-draft-mutation").MaterialDraftMutation | typeof import("./course/course-mutation").CourseMutation | typeof import("./course/course-query").CourseQuery | typeof import("./course/course-query").CourseVersionFields | typeof import("./course/course-query").CourseUnitFields | typeof import("./course/course-query").CourseQuestionFields | typeof import("./enrollment/enrollment-mutation").EnrollmentMutation | typeof import("./enrollment/enrollment-query").EnrollmentQuery | typeof import("./enrollment/enrollment-query").CourseEnrollmentFields)[];
14
15
  };