@things-factory/learning 10.1.260 → 10.1.261
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.
- package/dist-client/components/learning-material-form.d.ts +41 -0
- package/dist-client/components/learning-material-form.js +202 -0
- package/dist-client/components/learning-material-form.js.map +1 -0
- package/dist-client/components/learning-unit-form.d.ts +3 -0
- package/dist-client/components/learning-unit-form.js +10 -2
- package/dist-client/components/learning-unit-form.js.map +1 -1
- package/dist-client/pages/learning-course-editor.d.ts +1 -0
- package/dist-client/pages/learning-course-editor.js +19 -6
- package/dist-client/pages/learning-course-editor.js.map +1 -1
- package/dist-client/pages/learning-course-list.d.ts +1 -1
- package/dist-client/pages/learning-course-list.js +6 -2
- package/dist-client/pages/learning-course-list.js.map +1 -1
- package/dist-client/pages/learning-enrollment-status.d.ts +28 -16
- package/dist-client/pages/learning-enrollment-status.js +95 -112
- package/dist-client/pages/learning-enrollment-status.js.map +1 -1
- package/dist-client/pages/learning-view.d.ts +11 -0
- package/dist-client/pages/learning-view.js +94 -19
- package/dist-client/pages/learning-view.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/utils/learning-api.d.ts +48 -3
- package/dist-client/utils/learning-api.js +51 -2
- package/dist-client/utils/learning-api.js.map +1 -1
- package/dist-client/utils/material-source.d.ts +12 -0
- package/dist-client/utils/material-source.js +77 -0
- package/dist-client/utils/material-source.js.map +1 -0
- package/dist-server/ai-reads.js +1 -1
- package/dist-server/ai-reads.js.map +1 -1
- package/dist-server/service/course/course-mutation.d.ts +1 -1
- package/dist-server/service/course/course-mutation.js +7 -6
- package/dist-server/service/course/course-mutation.js.map +1 -1
- package/dist-server/service/course/course-query.d.ts +1 -0
- package/dist-server/service/course/course-query.js +14 -3
- package/dist-server/service/course/course-query.js.map +1 -1
- package/dist-server/service/course/course-rules.d.ts +3 -1
- package/dist-server/service/course/course-rules.js +5 -1
- package/dist-server/service/course/course-rules.js.map +1 -1
- package/dist-server/service/course/course-service.d.ts +5 -4
- package/dist-server/service/course/course-service.js +10 -14
- package/dist-server/service/course/course-service.js.map +1 -1
- package/dist-server/service/course/course-type.d.ts +1 -0
- package/dist-server/service/course/course-type.js +4 -0
- package/dist-server/service/course/course-type.js.map +1 -1
- package/dist-server/service/course/course-unit.d.ts +3 -2
- package/dist-server/service/course/course-unit.js +7 -2
- package/dist-server/service/course/course-unit.js.map +1 -1
- package/dist-server/service/course/index.d.ts +2 -1
- package/dist-server/service/course/index.js +2 -1
- package/dist-server/service/course/index.js.map +1 -1
- package/dist-server/service/course/material-audio.d.ts +1 -0
- package/dist-server/service/course/material-audio.js +43 -0
- package/dist-server/service/course/material-audio.js.map +1 -0
- package/dist-server/service/course/material-draft-mutation.d.ts +13 -0
- package/dist-server/service/course/material-draft-mutation.js +55 -0
- package/dist-server/service/course/material-draft-mutation.js.map +1 -0
- package/dist-server/service/course/material-draft-rules.d.ts +38 -0
- package/dist-server/service/course/material-draft-rules.js +83 -0
- package/dist-server/service/course/material-draft-rules.js.map +1 -0
- package/dist-server/service/course/material-draft-service.d.ts +15 -0
- package/dist-server/service/course/material-draft-service.js +162 -0
- package/dist-server/service/course/material-draft-service.js.map +1 -0
- package/dist-server/service/enrollment/enrollment-query.d.ts +5 -4
- package/dist-server/service/enrollment/enrollment-query.js +26 -35
- package/dist-server/service/enrollment/enrollment-query.js.map +1 -1
- package/dist-server/service/enrollment/enrollment-service.js +13 -3
- package/dist-server/service/enrollment/enrollment-service.js.map +1 -1
- package/dist-server/service/enrollment/enrollment-status.d.ts +31 -18
- package/dist-server/service/enrollment/enrollment-status.js +101 -42
- package/dist-server/service/enrollment/enrollment-status.js.map +1 -1
- package/dist-server/service/enrollment/enrollment-type.d.ts +1 -14
- package/dist-server/service/enrollment/enrollment-type.js +6 -54
- package/dist-server/service/enrollment/enrollment-type.js.map +1 -1
- package/dist-server/service/enrollment/enrollment.d.ts +1 -0
- package/dist-server/service/enrollment/enrollment.js +5 -0
- package/dist-server/service/enrollment/enrollment.js.map +1 -1
- package/dist-server/service/enrollment/learning-ai.d.ts +9 -2
- package/dist-server/service/enrollment/learning-ai.js +25 -28
- package/dist-server/service/enrollment/learning-ai.js.map +1 -1
- package/dist-server/service/enrollment/learning-documents.js +3 -4
- package/dist-server/service/enrollment/learning-documents.js.map +1 -1
- package/dist-server/service/enrollment/progress-rules.d.ts +36 -11
- package/dist-server/service/enrollment/progress-rules.js +47 -12
- package/dist-server/service/enrollment/progress-rules.js.map +1 -1
- package/dist-server/service/enrollment/unit-progress.d.ts +4 -0
- package/dist-server/service/enrollment/unit-progress.js +20 -0
- package/dist-server/service/enrollment/unit-progress.js.map +1 -1
- package/dist-server/service/index.d.ts +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +14 -13
- package/translations/en.json +30 -7
- package/translations/es.json +30 -7
- package/translations/ja.json +30 -7
- package/translations/ko.json +30 -7
- package/translations/ms.json +30 -7
- package/translations/zh.json +30 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"enrollment-query.js","sourceRoot":"","sources":["../../../server/service/enrollment/enrollment-query.ts"],"names":[],"mappings":"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","sourcesContent":["import { Arg, Args, Ctx, Directive, FieldResolver, Int, Query, Resolver, Root, UseMiddleware } from 'type-graphql'\n\nimport { User } from '@things-factory/auth-base'\nimport { getRepository, ListParam, SpeakRefusals } from '@things-factory/shell'\n\nimport { Course } from '../course/course'\nimport { CourseQuestion } from '../course/course-question'\nimport { CourseVersion } from '../course/course-version'\nimport { holds, learningManagerOf, unitsOf } from '../course/course-service'\nimport { Enrollment } from './enrollment'\nimport { missingFor, myEnrollmentOf, myEnrollments, progressesOf, shownVersionOf, todayIn } from './enrollment-service'\nimport { completionRegister, enrollmentStatus, StatusRow, teamEnrollmentStatus } from './enrollment-status'\nimport { EnrollmentStatusFilter, EnrollmentStatusList, EnrollmentStatusRow, LearningContent, LearningEnrollmentList, LearningUnit } from './enrollment-type'\nimport { courseContent, readableEnrollments } from './learning-ai'\nimport { counts, overdue } from './progress-rules'\n\n\nconst rowOut = (row: StatusRow): EnrollmentStatusRow => ({ id: row.enrollment.id, ...row })\n\nfunction listOf(rows: StatusRow[]): EnrollmentStatusList {\n const live = rows.filter(one => one.state !== 'CANCELLED')\n return {\n items: rows.map(rowOut),\n summary: {\n total: live.length,\n assigned: live.filter(one => one.enrollment.status === 'ASSIGNED').length,\n inProgress: live.filter(one => one.enrollment.status === 'IN_PROGRESS').length,\n completed: live.filter(one => one.state === 'COMPLETED').length,\n overdue: live.filter(one => one.state === 'OVERDUE').length\n }\n }\n}\n\n/** Whether the request reads how an enrolment went - its learner or a manager. */\nasync function readsInside(enrollment: Enrollment, context: any): Promise<boolean> {\n return enrollment.userId === context.state.user.id || (await holds(context, 'course', 'query'))\n}\n\n/*\n * The reads of enrolments (ADR-0194 decisions 5-11): one's own (`learning` query, the task `learning.taking`); every\n * enrolment and the completion register for a manager (`course` query); a department head's people, status only.\n */\n@Resolver(Enrollment)\n@UseMiddleware(SpeakRefusals)\nexport class EnrollmentQuery {\n @Directive('@privilege(category: \"learning\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => [Enrollment], { description: \"The request's own enrolments - open ones first, the soonest due first, then the completed.\" })\n async myEnrollments(@Ctx() context: ResolverContext): Promise<Enrollment[]> {\n return await myEnrollments(context)\n }\n\n @Directive('@privilege(category: \"learning\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => Enrollment, { description: \"One of the request's own enrolments.\" })\n async myEnrollment(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<Enrollment> {\n return await myEnrollmentOf(context, id)\n }\n\n @Directive('@privilege(category: \"learning\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => EnrollmentStatusList, { description: \"Where the people of the request's departments stand - for a department head, status and days only.\" })\n async teamEnrollmentStatus(@Arg('filter', type => EnrollmentStatusFilter, { nullable: true }) filter: EnrollmentStatusFilter, @Ctx() context: ResolverContext): Promise<EnrollmentStatusList> {\n return listOf(await teamEnrollmentStatus(filter ?? {}, context))\n }\n\n @Directive('@privilege(category: \"course\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => EnrollmentStatusList, { description: 'Every enrolment of a course or a source, with its progress and evidence.' })\n async enrollmentStatus(@Arg('filter', type => EnrollmentStatusFilter, { nullable: true }) filter: EnrollmentStatusFilter, @Ctx() context: ResolverContext): Promise<EnrollmentStatusList> {\n return listOf(await enrollmentStatus(filter ?? {}, context))\n }\n\n @Directive('@privilege(category: \"course\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => [EnrollmentStatusRow], { description: 'The completion register of a course - every completion by its day.' })\n async completionRegister(@Arg('courseId') courseId: string, @Ctx() context: ResolverContext): Promise<EnrollmentStatusRow[]> {\n return (await completionRegister(courseId, context)).map(rowOut)\n }\n\n @Directive('@privilege(category: \"learning\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => LearningEnrollmentList, { description: 'The enrolments the request may read - a manager every one, a department head their people (status only), anyone else their own - for the assistant (ADR-0194 decision 12).' })\n async learningEnrollments(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<LearningEnrollmentList> {\n let rows = await readableEnrollments(context)\n for (const filter of params?.filters ?? []) {\n const values = Array.isArray(filter.value) ? filter.value.map(String) : [String(filter.value)]\n if (filter.name === 'state') rows = rows.filter(row => values.includes(row.state) !== (filter.operator === 'notin' || filter.operator === 'noteq'))\n }\n const search = String(params?.search ?? params?.filters?.find(one => one.operator === 'search')?.value ?? '').replace(/%/g, '').trim()\n if (search) rows = rows.filter(row => `${row.learner} ${row.departmentName ?? ''} ${row.course}`.includes(search))\n const { page = 1, limit = 0 } = (params?.pagination ?? {}) as any\n return { items: limit > 0 ? rows.slice((page - 1) * limit, page * limit) : rows, total: rows.length }\n }\n\n @Directive('@privilege(category: \"learning\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => LearningContent, { description: \"The units of one course version for the assistant - a learner's own enrolment's (not while a quiz attempt is open), or a version for a manager (ADR-0194 decision 12).\" })\n async learningContent(@Arg('enrollmentId', { nullable: true }) enrollmentId: string, @Arg('versionId', { nullable: true }) versionId: string, @Ctx() context: ResolverContext): Promise<LearningContent> {\n return await courseContent({ enrollmentId, versionId }, context)\n }\n\n /* ---------------------------------------------------------------- fields */\n\n @FieldResolver(type => Course, { nullable: true, description: 'The course.' })\n async course(@Root() enrollment: Enrollment): Promise<Course | null> {\n return enrollment.courseId ? await getRepository(Course).findOneBy({ id: enrollment.courseId }) : null\n }\n\n @FieldResolver(type => User, { nullable: true, description: 'Who takes it.' })\n async user(@Root() enrollment: Enrollment): Promise<User | null> {\n return enrollment.userId ? await getRepository(User).findOneBy({ id: enrollment.userId }) : null\n }\n\n @FieldResolver(type => CourseVersion, { nullable: true, description: 'The version it shows - its own once started, the published one before.' })\n async version(@Root() enrollment: Enrollment, @Ctx() context: ResolverContext): Promise<CourseVersion | null> {\n return await shownVersionOf(learningManagerOf(context), context.state.domain, enrollment)\n }\n\n @FieldResolver(type => String, { description: 'Where it stands today - OVERDUE for an open one past its due day.' })\n state(@Root() enrollment: Enrollment, @Ctx() context: ResolverContext): string {\n return overdue(enrollment, todayIn(context.state.domain)) ? 'OVERDUE' : enrollment.status\n }\n\n @FieldResolver(type => [LearningUnit], { description: 'Its units with how far the learner went - for its learner and managers.' })\n async units(@Root() enrollment: Enrollment, @Ctx() context: ResolverContext): Promise<LearningUnit[]> {\n if (!(await readsInside(enrollment, context))) return []\n const tx = learningManagerOf(context)\n const version = await shownVersionOf(tx, context.state.domain, enrollment)\n if (!version) return []\n const progress = await progressesOf(tx, enrollment.id)\n return (await unitsOf(tx, version.id)).map(unit => ({ unit, progress: progress.find(one => one.unitId === unit.id) ?? null, counts: counts(unit) }))\n }\n\n @FieldResolver(type => [String], { description: 'What still stands before completion - units, time, quiz, acknowledgement.' })\n async missing(@Root() enrollment: Enrollment, @Ctx() context: ResolverContext): Promise<string[]> {\n if (!(await readsInside(enrollment, context))) return []\n return await missingFor(learningManagerOf(context), context.state.domain, enrollment)\n }\n\n @FieldResolver(type => Int, { nullable: true, description: 'The quiz attempts left - no limit, or no quiz, when empty.' })\n async attemptsLeft(@Root() enrollment: Enrollment, @Ctx() context: ResolverContext): Promise<number | null> {\n if (!(await readsInside(enrollment, context))) return null\n const version = await shownVersionOf(learningManagerOf(context), context.state.domain, enrollment)\n return version?.attemptLimit != null ? Math.max(0, version.attemptLimit - (enrollment.attempts ?? 0)) : null\n }\n\n @FieldResolver(type => Int, { description: 'How many questions its quiz draws - 0 when it has none.' })\n async quizSize(@Root() enrollment: Enrollment, @Ctx() context: ResolverContext): Promise<number> {\n const tx = learningManagerOf(context)\n const version = await shownVersionOf(tx, context.state.domain, enrollment)\n if (!version || version.passScore == null) return 0\n const all = await tx.getRepository(CourseQuestion).count({ where: { version: { id: version.id } } as any })\n return version.questionCount ? Math.min(version.questionCount, all) : all\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"enrollment-query.js","sourceRoot":"","sources":["../../../server/service/enrollment/enrollment-query.ts"],"names":[],"mappings":"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","sourcesContent":["import { Arg, Args, Ctx, Directive, FieldResolver, Int, Query, Resolver, Root, UseMiddleware } from 'type-graphql'\n\nimport { requestHolds, User } from '@things-factory/auth-base'\nimport { getRepository, ListParam, ListValueCount, SpeakRefusals } from '@things-factory/shell'\n\nimport { Course } from '../course/course'\nimport { CourseQuestion } from '../course/course-question'\nimport { CourseVersion } from '../course/course-version'\nimport { learningManagerOf, unitsOf } from '../course/course-service'\nimport { Enrollment } from './enrollment'\nimport { missingFor, myEnrollmentOf, myEnrollments, progressesOf, shownVersionOf, todayIn } from './enrollment-service'\nimport { completionRegister, enrollmentPage, enrollmentStateCounts, StatusRow, teamReachOf } from './enrollment-status'\nimport { EnrollmentStatusList, EnrollmentStatusRow, LearningContent, LearningEnrollmentList, LearningUnit } from './enrollment-type'\nimport { courseContent, readableEnrollments } from './learning-ai'\nimport { counts, overdue } from './progress-rules'\n\n\nconst rowOut = (row: StatusRow): EnrollmentStatusRow => ({ id: row.enrollment.id, ...row })\n\nconst pageOf = ({ rows, total }: { rows: StatusRow[]; total: number }): EnrollmentStatusList => ({ items: rows.map(rowOut), total })\n\n/** Whether the request reads how an enrolment went - its learner or a manager. */\nasync function readsInside(enrollment: Enrollment, context: any): Promise<boolean> {\n return enrollment.userId === context.state.user.id || (await requestHolds(context, 'course', 'query'))\n}\n\n/*\n * The reads of enrolments (ADR-0194 decisions 5-11): one's own (`learning` query, the task `learning.taking`); every\n * enrolment and the completion register for a manager (`course` query); a department head's people, status only.\n */\n@Resolver(Enrollment)\n@UseMiddleware(SpeakRefusals)\nexport class EnrollmentQuery {\n @Directive('@privilege(category: \"learning\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => [Enrollment], { description: \"The request's own enrolments - open ones first, the soonest due first, then the completed.\" })\n async myEnrollments(@Ctx() context: ResolverContext): Promise<Enrollment[]> {\n return await myEnrollments(context)\n }\n\n @Directive('@privilege(category: \"learning\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => Enrollment, { description: \"One of the request's own enrolments.\" })\n async myEnrollment(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<Enrollment> {\n return await myEnrollmentOf(context, id)\n }\n\n @Directive('@privilege(category: \"learning\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => EnrollmentStatusList, { description: \"Where the people of the request's departments stand - for a department head, status and days only. Filters: course, state, sourceKind, sourceId; search: the learner's name or the course's.\" })\n async teamEnrollmentStatus(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<EnrollmentStatusList> {\n return pageOf(await enrollmentPage(params, await teamReachOf(context), context))\n }\n\n @Directive('@privilege(category: \"course\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => EnrollmentStatusList, { description: \"Every enrolment, with its progress and evidence. Filters: course, state, sourceKind, sourceId; search: the learner's name or the course's.\" })\n async enrollmentStatus(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<EnrollmentStatusList> {\n return pageOf(await enrollmentPage(params, { kind: 'all' }, context))\n }\n\n @Directive('@privilege(category: \"learning\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => [ListValueCount], { description: \"How many enrolments stand in each state under the list's other conditions, every state in order - of everyone for a manager, else of a department head's people (refused to anyone heading none), as the status list reads them.\" })\n async enrollmentStateCounts(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<ListValueCount[]> {\n const reach = (await requestHolds(context, 'course', 'query')) ? ({ kind: 'all' } as const) : await teamReachOf(context)\n return await enrollmentStateCounts(params, reach, context)\n }\n\n @Directive('@privilege(category: \"course\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => [EnrollmentStatusRow], { description: 'The completion register of a course - every completion by its day.' })\n async completionRegister(@Arg('courseId') courseId: string, @Ctx() context: ResolverContext): Promise<EnrollmentStatusRow[]> {\n return (await completionRegister(courseId, context)).map(rowOut)\n }\n\n @Directive('@privilege(category: \"learning\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => LearningEnrollmentList, { description: 'The enrolments the request may read - a manager every one, a department head their people (status only), anyone else their own - for the assistant (ADR-0194 decision 12).' })\n async learningEnrollments(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<LearningEnrollmentList> {\n return await readableEnrollments(params, context)\n }\n\n @Directive('@privilege(category: \"learning\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => LearningContent, { description: \"The units of one course version for the assistant - a learner's own enrolment's (not while a quiz attempt is open), or a version for a manager (ADR-0194 decision 12).\" })\n async learningContent(@Arg('enrollmentId', { nullable: true }) enrollmentId: string, @Arg('versionId', { nullable: true }) versionId: string, @Ctx() context: ResolverContext): Promise<LearningContent> {\n return await courseContent({ enrollmentId, versionId }, context)\n }\n\n /* ---------------------------------------------------------------- fields */\n\n @FieldResolver(type => Course, { nullable: true, description: 'The course.' })\n async course(@Root() enrollment: Enrollment): Promise<Course | null> {\n return enrollment.courseId ? await getRepository(Course).findOneBy({ id: enrollment.courseId }) : null\n }\n\n @FieldResolver(type => User, { nullable: true, description: 'Who takes it.' })\n async user(@Root() enrollment: Enrollment): Promise<User | null> {\n return enrollment.userId ? await getRepository(User).findOneBy({ id: enrollment.userId }) : null\n }\n\n @FieldResolver(type => CourseVersion, { nullable: true, description: 'The version it shows - its own once started, the published one before.' })\n async version(@Root() enrollment: Enrollment, @Ctx() context: ResolverContext): Promise<CourseVersion | null> {\n return await shownVersionOf(learningManagerOf(context), context.state.domain, enrollment)\n }\n\n @FieldResolver(type => String, { description: 'Where it stands today - OVERDUE for an open one past its due day.' })\n state(@Root() enrollment: Enrollment, @Ctx() context: ResolverContext): string {\n return overdue(enrollment, todayIn(context.state.domain)) ? 'OVERDUE' : enrollment.status\n }\n\n @FieldResolver(type => [LearningUnit], { description: 'Its units with how far the learner went - for its learner and managers.' })\n async units(@Root() enrollment: Enrollment, @Ctx() context: ResolverContext): Promise<LearningUnit[]> {\n if (!(await readsInside(enrollment, context))) return []\n const tx = learningManagerOf(context)\n const version = await shownVersionOf(tx, context.state.domain, enrollment)\n if (!version) return []\n const progress = await progressesOf(tx, enrollment.id)\n return (await unitsOf(tx, version.id)).map(unit => ({ unit, progress: progress.find(one => one.unitId === unit.id) ?? null, counts: counts(unit) }))\n }\n\n @FieldResolver(type => [String], { description: 'What still stands before completion - units, time, quiz, acknowledgement.' })\n async missing(@Root() enrollment: Enrollment, @Ctx() context: ResolverContext): Promise<string[]> {\n if (!(await readsInside(enrollment, context))) return []\n return await missingFor(learningManagerOf(context), context.state.domain, enrollment)\n }\n\n @FieldResolver(type => Int, { nullable: true, description: 'The quiz attempts left - no limit, or no quiz, when empty.' })\n async attemptsLeft(@Root() enrollment: Enrollment, @Ctx() context: ResolverContext): Promise<number | null> {\n if (!(await readsInside(enrollment, context))) return null\n const version = await shownVersionOf(learningManagerOf(context), context.state.domain, enrollment)\n return version?.attemptLimit != null ? Math.max(0, version.attemptLimit - (enrollment.attempts ?? 0)) : null\n }\n\n @FieldResolver(type => Int, { description: 'How many questions its quiz draws - 0 when it has none.' })\n async quizSize(@Root() enrollment: Enrollment, @Ctx() context: ResolverContext): Promise<number> {\n const tx = learningManagerOf(context)\n const version = await shownVersionOf(tx, context.state.domain, enrollment)\n if (!version || version.passScore == null) return 0\n const all = await tx.getRepository(CourseQuestion).count({ where: { version: { id: version.id } } as any })\n return version.questionCount ? Math.min(version.questionCount, all) : all\n }\n}\n"]}
|
|
@@ -276,15 +276,22 @@ async function recordTick(enrollmentId, unitId, seconds, position, context, now
|
|
|
276
276
|
throw new shell_1.Refusal('learning-link-not-counted', {}, 'a link is not counted');
|
|
277
277
|
const repository = tx.getRepository(unit_progress_1.UnitProgress);
|
|
278
278
|
const progress = (await repository.findOne({ where: { enrollment: { id: enrollment.id }, unit: { id: unit.id } } })) ??
|
|
279
|
-
(await repository.save({ domain, enrollment, unit, learnedSeconds: 0, watchedSeconds: 0, positionSeconds: 0, creator: user, updater: user }));
|
|
279
|
+
(await repository.save({ domain, enrollment, unit, learnedSeconds: 0, watchedSeconds: 0, positionSeconds: 0, pagesSeen: 0, pagesRead: 0, pageSeconds: 0, pageAt: 0, creator: user, updater: user }));
|
|
280
280
|
if (enrollment.status === enrollment_1.EnrollmentStatus.COMPLETED)
|
|
281
281
|
return { enrollment, progress };
|
|
282
|
-
const verdict = (0, progress_rules_1.tickVerdict)(progress, { seconds, position }, now, unit
|
|
283
|
-
if ('refusal' in verdict)
|
|
282
|
+
const verdict = (0, progress_rules_1.tickVerdict)(progress, { seconds, position }, now, unit);
|
|
283
|
+
if ('refusal' in verdict) {
|
|
284
|
+
if (verdict.refusal === 'page-ahead')
|
|
285
|
+
throw new shell_1.Refusal('learning-page-ahead', { allowed: verdict.allowed }, `a page cannot be ahead of the one after the last read (${verdict.allowed})`);
|
|
284
286
|
throw new shell_1.Refusal('learning-position-ahead', { allowed: verdict.allowed }, `the video cannot be ahead of what was watched (${verdict.allowed} s)`);
|
|
287
|
+
}
|
|
285
288
|
progress.learnedSeconds += verdict.add;
|
|
286
289
|
progress.watchedSeconds = verdict.watchedSeconds;
|
|
287
290
|
progress.positionSeconds = verdict.positionSeconds;
|
|
291
|
+
progress.pagesSeen = verdict.pagesSeen;
|
|
292
|
+
progress.pagesRead = verdict.pagesRead;
|
|
293
|
+
progress.pageSeconds = verdict.pageSeconds;
|
|
294
|
+
progress.pageAt = verdict.pageAt;
|
|
288
295
|
progress.lastTickAt = now;
|
|
289
296
|
if (!progress.doneAt && (0, progress_rules_1.unitDone)(unit, progress))
|
|
290
297
|
progress.doneAt = now;
|
|
@@ -292,6 +299,9 @@ async function recordTick(enrollmentId, unitId, seconds, position, context, now
|
|
|
292
299
|
await repository.save(progress);
|
|
293
300
|
if (verdict.add)
|
|
294
301
|
await tx.getRepository(enrollment_1.Enrollment).increment({ id: enrollment.id }, 'learnedSeconds', verdict.add);
|
|
302
|
+
/* Where the learner was - the course opens on this unit again (2026-11-08). */
|
|
303
|
+
if (enrollment.lastUnitId !== unit.id)
|
|
304
|
+
await tx.getRepository(enrollment_1.Enrollment).update({ id: enrollment.id }, { lastUnitId: unit.id });
|
|
295
305
|
enrollment = await completeIfMet(tx, domain, await enrollmentOf(tx, domain, enrollment.id), context);
|
|
296
306
|
return { enrollment, progress };
|
|
297
307
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"enrollment-service.js","sourceRoot":"","sources":["../../../server/service/enrollment/enrollment-service.ts"],"names":[],"mappings":";;;AAqGA,wBA0BC;AAMD,4BAMC;AAcD,4CAYC;AAMD,gCAUC;AAID,oCAIC;AAGD,wCAIC;AAGD,wCAGC;AAED,oCAEC;AAGD,gCAiBC;AAkCD,gCAwBC;AAGD,sDAOC;AAUD,gCAaC;AAgBD,4CAcC;AAaD,8CAkBC;AAYD,sCAYC;AAKD,sCAOC;AA9ZD;;;;;GAKG;AACH,qCAAmD;AAEnD,yDAAgD;AAChD,+DAAsH;AACtH,6CAA4C;AAC5C,qEAAoE;AACpE,iDAA2E;AAC3E,uDAAmG;AAEnG,iDAA2D;AAE3D,+DAA0D;AAC1D,uDAAkE;AAClE,6DAAwD;AACxD,6DAAgH;AAChH,6CAAqF;AACrF,yDAAsD;AACtD,qDAA4G;AAC5G,iDAA4C;AAC5C,mDAA8C;AAQjC,QAAA,kBAAkB,GAAG,iBAAiB,CAAA;AAEnD,qEAAqE;AAC9D,MAAM,iBAAiB,GAAG,CAAC,YAAoB,EAAE,EAAE,CAAC,oBAAoB,YAAY,EAAE,CAAA;AAAhF,QAAA,iBAAiB,qBAA+D;AAEtF,MAAM,OAAO,GAAG,CAAC,MAAoC,EAAE,MAAY,IAAI,IAAI,EAAE,EAAE,EAAE,CAAC,IAAA,mBAAW,EAAC,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAA;AAA9G,QAAA,OAAO,WAAuG;AAE3H,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAC,IAAI,eAAO,CAAC,oBAAoB,EAAE,EAAE,EAAE,iDAAiD,CAAC,CAAA;AAC/G,MAAM,MAAM,GAAG,CAAC,MAAc,EAAE,EAAE,CAAE,qCAAqC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;AAuXjF,wBAAM;AArXf,6FAA6F;AAE7F,mHAAmH;AACnH,SAAS,aAAa,CAAC,EAAiB,EAAE,MAAW,EAAE,IAAU;IAC/D,MAAM,CAAC,GAAG,IAAA,0BAAa,EAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,CAAA;IAC5C,OAAO,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,GAAW,EAAE,OAAa,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,OAAO,CAAC,EAAS,CAAA;AACnG,CAAC;AAED,kHAAkH;AAClH,KAAK,UAAU,SAAS,CAAC,EAAiB,EAAE,MAAW,EAAE,UAAsB,EAAE,MAAc,EAAE,MAAY;IAC3G,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,aAAa,CAAC,mBAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,qCAAwB,EAAS,EAAE,CAAC,CAAA;IAC1I,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,YAAM,CAAC,IAAI,CAAC,cAAc,MAAM,CAAC,SAAS,mBAAmB,qCAAwB,yBAAyB,MAAM,CAAC,IAAI,kBAAkB,CAAC,CAAA;QAC5I,OAAO,IAAI,CAAA;IACb,CAAC;IACD,MAAM,CAAC,GAAG,IAAA,0BAAa,EAAC,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC,CAAA;IAC9C,MAAM,IAAI,GAAG,CAAC,CAAC,yBAAyB,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAA;IAClE,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,6BAA6B,EAAE,EAAE,KAAK,EAAE,UAAU,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,oCAAoC,CAAC,CAAA;IAC3I,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IACvF,MAAM,OAAO,GAAG,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IACjD,MAAM,KAAK,GAAG,EAAE,YAAY,EAAE,UAAU,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAA,yBAAiB,EAAC,UAAU,CAAC,EAAE,CAAC,EAAE,CAAA;IAClG,MAAM,QAAQ,GAAG,MAAM,IAAA,gBAAK,EAAC,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAS,EAAE,OAAO,CAAC,CAAA;IAClJ,MAAM,IAAA,iBAAM,EAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,OAAO,CAAC,CAAA;IAC1F,OAAO,QAAQ,CAAC,EAAE,CAAA;AACpB,CAAC;AAED,wDAAwD;AACxD,SAAS,OAAO,CAAC,KAAa,EAAE,QAAwB;IACtD,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,GAAG,KAAK,YAAY,CAAC,CAAA;IAC1C,MAAM,IAAI,GAAG,QAAQ,IAAI,KAAK,CAAA;IAC9B,MAAM,OAAO,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;IACzE,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC,CAAA;IACxE,OAAO,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAA;AACxE,CAAC;AAED,KAAK,UAAU,QAAQ,CAAC,EAAiB,EAAE,UAAkB;IAC3D,OAAO,CAAC,MAAM,EAAE,CAAC,aAAa,CAAC,yBAAc,CAAC,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,IAAA,YAAE,EAAC,CAAC,UAAU,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC,EAAS,EAAE,CAAC,CAAC,GAAG,CAAC,CAAA;AACzK,CAAC;AAED,2DAA2D;AAC3D,KAAK,UAAU,YAAY,CAAC,EAAiB,EAAE,MAAW,EAAE,UAAsB,EAAE,EAAQ;IAC1F,IAAI,CAAC,UAAU,CAAC,cAAc,IAAI,CAAC,CAAC,MAAM,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,cAAc,CAAC,CAAC;QAAE,OAAM;IAC1F,MAAM,IAAA,mBAAQ,EAAC,EAAE,EAAE,EAAE,UAAU,CAAC,cAAc,EAAE,MAAM,EAAE,kBAAkB,EAAE,EAAE,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAA;AAC9G,CAAC;AAED,+DAA+D;AAC/D,KAAK,UAAU,SAAS,CAAC,EAAiB,EAAE,MAAW,EAAE,UAAsB,EAAE,EAAQ,EAAE,MAAc;IACvG,IAAI,CAAC,UAAU,CAAC,cAAc,IAAI,CAAC,CAAC,MAAM,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,cAAc,CAAC,CAAC;QAAE,OAAM;IAC1F,MAAM,IAAA,gBAAK,EAAC,EAAE,EAAE,EAAE,UAAU,CAAC,cAAc,EAAE,MAAM,EAAS,EAAE,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAA;AAC9F,CAAC;AAED,6GAA6G;AAE7G;;;;GAIG;AACI,KAAK,UAAU,MAAM,CAAC,MAAsB,EAAE,OAAiB,EAAE,QAAgB,EAAE,KAAgC,EAAE,OAAY;IACtI,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;IAC1C,IAAI,CAAC,MAAM,EAAE,IAAI;QAAE,MAAM,IAAI,eAAO,CAAC,0BAA0B,EAAE,EAAE,EAAE,qCAAqC,CAAC,CAAA;IAC3G,IAAI,KAAK,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,eAAO,CAAC,sBAAsB,EAAE,EAAE,EAAE,2BAA2B,CAAC,CAAA;IAC3H,MAAM,MAAM,GAAG,MAAM,IAAA,yBAAQ,EAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAA;IACnD,IAAI,CAAC,CAAC,MAAM,IAAA,mCAAkB,EAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;QAAE,MAAM,IAAI,eAAO,CAAC,+BAA+B,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,GAAG,MAAM,CAAC,IAAI,2BAA2B,CAAC,CAAA;IAC9K,MAAM,GAAG,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;IACjD,IAAI,CAAC,GAAG,CAAC,MAAM;QAAE,OAAO,EAAE,CAAA;IAC1B,MAAM,MAAM,GAAG,MAAO,EAAoB,CAAC,aAAa,CAAC,gBAAI,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,IAAA,YAAE,EAAC,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAA;IACvH,MAAM,QAAQ,GAAG,MAAM,CAAC,EAAE,IAAI,EAAE,CAAA;IAChC,MAAM,UAAU,GAAI,EAAoB,CAAC,aAAa,CAAC,uBAAU,CAAC,CAAA;IAClE,MAAM,GAAG,GAAiB,EAAE,CAAA;IAC5B,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;QACrB,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;QAChD,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,MAAM,CAAC,EAAE,CAAC;YAAE,MAAM,IAAI,eAAO,CAAC,0BAA0B,EAAE,EAAE,EAAE,yCAAyC,CAAC,CAAA;QACtK,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAA,YAAE,EAAC,qCAAwB,CAAC,EAAS,EAAE,CAAC,CAAA;QACxM,IAAI,IAAI,EAAE,CAAC;YACT,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YACd,SAAQ;QACV,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,IAAI,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,6BAAgB,CAAC,QAAQ,EAAE,cAAc,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAS,CAAC,CAAA;QACtO,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;QAC/D,IAAI,IAAI;YAAE,MAAM,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,cAAc,EAAE,IAAI,EAAS,CAAC,CAAA;QAC5E,GAAG,CAAC,IAAI,CAAC,CAAC,MAAM,UAAU,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAE,CAAC,CAAA;IAC3D,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED;;;GAGG;AACI,KAAK,UAAU,QAAQ,CAAC,MAAsB,EAAE,MAAc,EAAE,OAAY;IACjF,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;IAC1C,MAAM,IAAI,GAAG,MAAO,EAAoB,CAAC,aAAa,CAAC,uBAAU,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,IAAA,YAAE,EAAC,qCAAwB,CAAC,EAAS,EAAE,CAAC,CAAA;IAClN,IAAI,KAAK,GAAG,CAAC,CAAA;IACb,KAAK,MAAM,GAAG,IAAI,IAAI;QAAE,IAAI,MAAM,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC;YAAE,KAAK,EAAE,CAAA;IACpF,OAAO,KAAK,CAAA;AACd,CAAC;AAED,6HAA6H;AAC7H,KAAK,UAAU,UAAU,CAAC,EAAiB,EAAE,MAAW,EAAE,GAAe,EAAE,EAAQ,EAAE,MAAc;IACjG,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,aAAa,CAAC,uBAAU,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,IAAA,YAAE,EAAC,qCAAwB,CAAC,EAAS,EAAE,EAAE,MAAM,EAAE,6BAAgB,CAAC,SAAS,EAAE,WAAW,EAAE,IAAI,IAAI,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAS,CAAC,CAAA;IACjO,IAAI,CAAC,OAAO,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAA;IACnC,MAAM,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,CAAC,CAAA;IAC5C,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;;GAGG;AACI,KAAK,UAAU,gBAAgB,CAAC,EAAU,EAAE,MAAc,EAAE,OAAY;IAC7E,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;IAC1C,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE;QAAE,MAAM,IAAI,eAAO,CAAC,iCAAiC,EAAE,EAAE,EAAE,yBAAyB,CAAC,CAAA;IACxG,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAA;IAC9C,IAAI,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QACvB,IAAI,CAAC,CAAC,MAAM,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;YAAE,MAAM,IAAI,eAAO,CAAC,2BAA2B,EAAE,EAAE,EAAE,wDAAwD,CAAC,CAAA;IAC7K,CAAC;SAAM,IAAI,GAAG,CAAC,MAAM,KAAK,6BAAgB,CAAC,SAAS,EAAE,CAAC;QACrD,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,aAAa,CAAC,uBAAU,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,6BAAgB,CAAC,SAAS,EAAS,EAAE,EAAE,MAAM,EAAE,6BAAgB,CAAC,SAAS,EAAE,WAAW,EAAE,IAAI,IAAI,EAAE,EAAE,YAAY,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAS,CAAC,CAAA;QACxO,IAAI,CAAC,OAAO,CAAC,QAAQ;YAAE,MAAM,IAAI,eAAO,CAAC,2BAA2B,EAAE,EAAE,EAAE,wDAAwD,CAAC,CAAA;QACnI,MAAM,IAAA,oCAAiB,EAAC,GAAG,CAAC,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,MAAM,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAE,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,OAAO,CAAC,CAAA;IACrH,CAAC;;QAAM,MAAM,IAAI,eAAO,CAAC,+BAA+B,EAAE,EAAE,EAAE,oCAAoC,CAAC,CAAA;IACnG,OAAO,MAAM,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAA;AAC3C,CAAC;AAED;;;GAGG;AACI,KAAK,UAAU,UAAU,CAAC,SAAqE,EAAE,OAAY;IAClH,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;IACpC,IAAI,CAAC,SAAS,EAAE,MAAM;QAAE,MAAM,IAAI,eAAO,CAAC,4BAA4B,EAAE,EAAE,EAAE,2BAA2B,CAAC,CAAA;IACxG,MAAM,aAAa,GAAG,CAAC,GAAG,CAAC,MAAM,IAAA,+BAAgB,EAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAA;IACtE,MAAM,OAAO,GAAG,CAAC,MAAM,IAAA,4BAAa,EAAC,MAAM,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IAC/E,KAAK,MAAM,MAAM,IAAI,SAAS,EAAE,CAAC;QAC/B,MAAM,GAAG,GAAG,IAAA,oCAAqB,EAAC,MAAa,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,CAAA;QACjG,IAAI,GAAG;YAAE,MAAM,IAAI,eAAO,CAAC,2BAA2B,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,QAAQ,EAAE,EAAE,GAAG,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,QAAQ,2BAA2B,GAAG,GAAG,CAAC,CAAA;IACrL,CAAC;IACD,OAAO,MAAM,IAAA,8BAAe,EAAC,MAAM,EAAE,SAAgB,EAAE,EAAE,CAAC,CAAA;AAC5D,CAAC;AAED,8EAA8E;AAEvE,KAAK,UAAU,YAAY,CAAC,EAAiB,EAAE,MAAsB,EAAE,EAAU;IACtF,MAAM,GAAG,GAAG,EAAE,IAAI,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,aAAa,CAAC,uBAAU,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAS,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IACtJ,IAAI,CAAC,GAAG;QAAE,MAAM,QAAQ,EAAE,CAAA;IAC1B,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,2EAA2E;AACpE,KAAK,UAAU,cAAc,CAAC,OAAY,EAAE,EAAU,EAAE,KAAoB,IAAA,kCAAiB,EAAC,OAAO,CAAC;IAC3G,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,EAAE,EAAE,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;IAC5D,IAAI,GAAG,CAAC,MAAM,KAAK,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE;QAAE,MAAM,QAAQ,EAAE,CAAA;IAC1D,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,sFAAsF;AAC/E,KAAK,UAAU,cAAc,CAAC,EAAiB,EAAE,MAAsB,EAAE,UAAsB;IACpG,IAAI,UAAU,CAAC,SAAS;QAAE,OAAO,MAAM,EAAE,CAAC,aAAa,CAAC,8BAAa,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,UAAU,CAAC,SAAS,EAAS,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAA;IACjJ,OAAO,MAAM,IAAA,mCAAkB,EAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,QAAS,CAAC,CAAA;AACnE,CAAC;AAEM,KAAK,UAAU,YAAY,CAAC,EAAiB,EAAE,YAAoB;IACxE,OAAO,MAAM,EAAE,CAAC,aAAa,CAAC,4BAAY,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE,EAAS,EAAE,CAAC,CAAA;AAC1G,CAAC;AAED,+FAA+F;AACxF,KAAK,UAAU,UAAU,CAAC,EAAiB,EAAE,MAAsB,EAAE,UAAsB;IAChG,IAAI,UAAU,CAAC,MAAM,KAAK,6BAAgB,CAAC,SAAS;QAAE,OAAO,EAAE,CAAA;IAC/D,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,CAAA;IAC5D,IAAI,CAAC,OAAO;QAAE,OAAO,CAAC,OAAO,CAAC,CAAA;IAC9B,MAAM,KAAK,GAAG,CAAC,MAAM,IAAA,wBAAO,EAAC,EAAE,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,uBAAM,CAAC,CAAA;IAC5D,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,EAAE,EAAE,UAAU,CAAC,EAAE,CAAC,CAAA;IACtD,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,aAAa,CAAC,gCAAc,CAAC,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,EAAS,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IACjJ,OAAO,IAAA,0BAAS,EAAC;QACf,KAAK,EAAE,KAAK,CAAC,MAAM;QACnB,SAAS,EAAE,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC,EAAE,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM;QAClG,cAAc,EAAE,UAAU,CAAC,cAAc;QACzC,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,CAAC;QACnC,IAAI,EAAE,OAAO,CAAC,SAAS,IAAI,IAAI,IAAI,SAAS,GAAG,CAAC;QAChD,MAAM,EAAE,UAAU,CAAC,KAAK,IAAI,IAAI,IAAI,UAAU,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,SAAS,IAAI,GAAG,CAAC;QAClF,eAAe,EAAE,CAAC,CAAC,OAAO,CAAC,eAAe;QAC1C,YAAY,EAAE,CAAC,CAAC,UAAU,CAAC,cAAc;KAC1C,CAAC,CAAA;AACJ,CAAC;AAED,4FAA4F;AAE5F,iHAAiH;AACjH,KAAK,UAAU,OAAO,CAAC,EAAiB,EAAE,MAAW,EAAE,UAAsB,EAAE,IAAU;IACvF,IAAI,UAAU,CAAC,MAAM,KAAK,6BAAgB,CAAC,SAAS;QAAE,MAAM,IAAI,eAAO,CAAC,+BAA+B,EAAE,EAAE,EAAE,4BAA4B,CAAC,CAAA;IAC1I,IAAI,UAAU,CAAC,SAAS,EAAE,CAAC;QACzB,IAAI,UAAU,CAAC,MAAM,KAAK,6BAAgB,CAAC,QAAQ;YAAE,MAAM,EAAE,CAAC,aAAa,CAAC,uBAAU,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,6BAAgB,CAAC,QAAQ,EAAS,EAAE,EAAE,MAAM,EAAE,6BAAgB,CAAC,WAAW,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,EAAS,CAAC,CAAA;QACvO,OAAO,MAAM,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,CAAC,CAAA;IACtD,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,IAAA,mCAAkB,EAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,QAAS,CAAC,CAAA;IAC1E,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,eAAO,CAAC,+BAA+B,EAAE,EAAE,EAAE,qCAAqC,CAAC,CAAA;IAC3G,MAAM,EAAE,CAAC,aAAa,CAAC,uBAAU,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,IAAA,YAAE,EAAC,qCAAwB,CAAC,EAAS,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,6BAAgB,CAAC,WAAW,EAAE,SAAS,EAAE,UAAU,CAAC,SAAS,IAAI,IAAI,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAS,CAAC,CAAA;IACrO,OAAO,MAAM,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,CAAC,CAAA;AACtD,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,EAAiB,EAAE,OAAuB,EAAE,MAAc;IACrF,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,aAAa,CAAC,wBAAU,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,EAAS,EAAE,CAAC,CAAA;IACtH,IAAI,CAAC,IAAI;QAAE,MAAM,QAAQ,EAAE,CAAA;IAC3B,OAAO,IAAI,CAAA;AACb,CAAC;AAOD;;;;;GAKG;AACI,KAAK,UAAU,UAAU,CAAC,YAAoB,EAAE,MAAc,EAAE,OAAe,EAAE,QAAgB,EAAE,OAAY,EAAE,MAAY,IAAI,IAAI,EAAE;IAC5I,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;IAC1C,IAAI,UAAU,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,CAAC,CAAA;IAChE,UAAU,GAAG,MAAM,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,CAAA;IACxD,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,UAAU,CAAC,SAAU,EAAE,EAAE,MAAM,CAAC,CAAA;IAC3E,IAAI,IAAI,CAAC,IAAI,KAAK,4BAAc,CAAC,IAAI;QAAE,MAAM,IAAI,eAAO,CAAC,2BAA2B,EAAE,EAAE,EAAE,uBAAuB,CAAC,CAAA;IAClH,MAAM,UAAU,GAAI,EAAoB,CAAC,aAAa,CAAC,4BAAY,CAAC,CAAA;IACpE,MAAM,QAAQ,GACZ,CAAC,MAAM,UAAU,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,UAAU,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,EAAS,EAAE,CAAC,CAAC;QAC1G,CAAC,MAAM,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAS,CAAC,CAAC,CAAA;IACtJ,IAAI,UAAU,CAAC,MAAM,KAAK,6BAAgB,CAAC,SAAS;QAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAA;IAErF,MAAM,OAAO,GAAG,IAAA,4BAAW,EAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,IAAI,KAAK,4BAAc,CAAC,KAAK,CAAC,CAAA;IACrG,IAAI,SAAS,IAAI,OAAO;QAAE,MAAM,IAAI,eAAO,CAAC,yBAAyB,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,EAAE,kDAAkD,OAAO,CAAC,OAAO,KAAK,CAAC,CAAA;IAC5K,QAAQ,CAAC,cAAc,IAAI,OAAO,CAAC,GAAG,CAAA;IACtC,QAAQ,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,CAAA;IAChD,QAAQ,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,CAAA;IAClD,QAAQ,CAAC,UAAU,GAAG,GAAG,CAAA;IACzB,IAAI,CAAC,QAAQ,CAAC,MAAM,IAAI,IAAA,yBAAQ,EAAC,IAAI,EAAE,QAAQ,CAAC;QAAE,QAAQ,CAAC,MAAM,GAAG,GAAG,CAAA;IACvE,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAA;IACvB,MAAM,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;IAC/B,IAAI,OAAO,CAAC,GAAG;QAAE,MAAM,EAAE,CAAC,aAAa,CAAC,uBAAU,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,UAAU,CAAC,EAAE,EAAS,EAAE,gBAAgB,EAAE,OAAO,CAAC,GAAG,CAAC,CAAA;IAC1H,UAAU,GAAG,MAAM,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,CAAA;IACpG,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAA;AACjC,CAAC;AAED,yFAAyF;AAClF,KAAK,UAAU,qBAAqB,CAAC,YAAoB,EAAE,OAAY;IAC5E,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;IAC1C,IAAI,UAAU,GAAG,MAAM,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,CAAC,EAAE,IAAI,CAAC,CAAA;IACjG,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,CAAA;IAC5D,IAAI,CAAC,OAAO,EAAE,eAAe;QAAE,MAAM,IAAI,eAAO,CAAC,6BAA6B,EAAE,EAAE,EAAE,wCAAwC,CAAC,CAAA;IAC7H,IAAI,CAAC,UAAU,CAAC,cAAc;QAAE,MAAM,EAAE,CAAC,aAAa,CAAC,uBAAU,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,UAAU,CAAC,EAAE,EAAS,EAAE,EAAE,cAAc,EAAE,IAAI,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAS,CAAC,CAAA;IAC7J,OAAO,MAAM,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,CAAA;AAChG,CAAC;AASD,6HAA6H;AAC7H,SAAgB,UAAU,CAAC,OAAoB,EAAE,SAA2B;IAC1E,OAAO;QACL,OAAO;QACP,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE;YACnC,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,KAAK,CAAC,UAAU,CAAE,CAAA;YACpE,OAAO;gBACL,EAAE,EAAE,QAAQ,CAAC,EAAE;gBACf,IAAI,EAAE,QAAQ,CAAC,IAAI;gBACnB,IAAI,EAAE,QAAQ,CAAC,IAAI;gBACnB,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,CAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;aAC9H,CAAA;QACH,CAAC,CAAC;KACH,CAAA;AACH,CAAC;AAED;;;GAGG;AACH;;;;GAIG;AACH,KAAK,UAAU,cAAc,CAAC,EAAiB,EAAE,MAAW,EAAE,UAAsB,EAAE,IAAU;IAC9F,MAAM,EAAE,CAAC,aAAa,CAAC,uBAAU,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,UAAU,CAAC,EAAE,EAAS,EAAE,EAAE,OAAO,EAAE,IAAI,EAAS,CAAC,CAAA;IACjG,OAAO,MAAM,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,CAAC,CAAA;AACtD,CAAC;AAEM,KAAK,UAAU,gBAAgB,CAAC,YAAoB,EAAE,OAAY;IACvE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;IAC1C,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,CAAC,EAAE,IAAI,CAAC,CAAA;IACpG,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAA;IACxD,IAAI,UAAU,CAAC,MAAM,KAAK,6BAAgB,CAAC,SAAS;QAAE,MAAM,IAAI,eAAO,CAAC,+BAA+B,EAAE,EAAE,EAAE,yBAAyB,CAAC,CAAA;IACvI,MAAM,OAAO,GAAG,CAAC,MAAM,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,CAAE,CAAA;IAC/D,MAAM,SAAS,GAAG,MAAM,IAAA,4BAAW,EAAC,EAAE,EAAE,OAAO,CAAC,EAAE,CAAC,CAAA;IACnD,IAAI,OAAO,CAAC,SAAS,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM;QAAE,MAAM,IAAI,eAAO,CAAC,kBAAkB,EAAE,EAAE,EAAE,wBAAwB,CAAC,CAAA;IACvH,MAAM,UAAU,GAAI,EAAoB,CAAC,aAAa,CAAC,0BAAW,CAAC,CAAA;IACnE,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,UAAU,CAAC,EAAE,EAAE,EAAE,WAAW,EAAE,IAAA,gBAAM,GAAE,EAAS,EAAE,CAAC,CAAA;IACxH,IAAI,OAAO;QAAE,OAAO,UAAU,CAAC,OAAO,EAAE,SAAS,CAAC,CAAA;IAClD,IAAI,OAAO,CAAC,YAAY,IAAI,IAAI,IAAI,UAAU,CAAC,QAAQ,IAAI,OAAO,CAAC,YAAY;QAAE,MAAM,IAAI,eAAO,CAAC,wBAAwB,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,YAAY,EAAE,EAAE,OAAO,OAAO,CAAC,YAAY,oBAAoB,CAAC,CAAA;IAC9M,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,IAAA,8BAAa,EAAC,SAAS,EAAE,OAAO,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAS,CAAC,CAAA;IAC1J,OAAO,UAAU,CAAC,OAAO,EAAE,SAAS,CAAC,CAAA;AACvC,CAAC;AASD;;;GAGG;AACI,KAAK,UAAU,iBAAiB,CAAC,SAAiB,EAAE,OAAiC,EAAE,OAAY;IACxG,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;IAC1C,MAAM,OAAO,GAAG,MAAO,EAAoB,CAAC,aAAa,CAAC,0BAAW,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAS,EAAE,CAAC,CAAA;IAC9I,IAAI,CAAC,OAAO;QAAE,MAAM,QAAQ,EAAE,CAAA;IAC9B,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,YAAa,EAAE,EAAE,CAAC,EAAE,IAAI,CAAC,CAAA;IACnH,MAAM,OAAO,GAAG,CAAC,MAAM,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,CAAE,CAAA;IAC/D,IAAI,OAAO,CAAC,YAAY,IAAI,IAAI,IAAI,UAAU,CAAC,QAAQ,IAAI,OAAO,CAAC,YAAY;QAAE,MAAM,IAAI,eAAO,CAAC,wBAAwB,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,YAAY,EAAE,EAAE,OAAO,OAAO,CAAC,YAAY,oBAAoB,CAAC,CAAA;IAC9M,MAAM,SAAS,GAAG,MAAM,IAAA,4BAAW,EAAC,EAAE,EAAE,OAAO,CAAC,EAAE,CAAC,CAAA;IACnD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,KAAK,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAqB,CAAA;IACtI,MAAM,IAAI,GAA6B,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC,OAAO,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAA;IAC3I,MAAM,KAAK,GAAG,IAAA,wBAAO,EAAC,KAAK,EAAE,IAAI,CAAC,CAAA;IAClC,MAAM,MAAM,GAAG,KAAK,IAAI,CAAC,OAAO,CAAC,SAAS,IAAI,GAAG,CAAC,CAAA;IAClD,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,aAAa,CAAC,0BAAW,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,WAAW,EAAE,IAAA,gBAAM,GAAE,EAAS,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAS,CAAC,CAAA;IAC7L,IAAI,CAAC,OAAO,CAAC,QAAQ;QAAE,MAAM,IAAI,eAAO,CAAC,uBAAuB,EAAE,EAAE,EAAE,8BAA8B,CAAC,CAAA;IACrG,MAAM,EAAE,CAAC,aAAa,CAAC,uBAAU,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,UAAU,CAAC,EAAE,EAAS,EAAE,UAAU,EAAE,CAAC,CAAC,CAAA;IACzF,MAAM,EAAE,CAAC,aAAa,CAAC,uBAAU,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,UAAU,CAAC,EAAE,EAAS,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE,IAAI,EAAS,CAAC,CAAA;IAChJ,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,CAAA;IACrG,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,CAAC,YAAY,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,YAAY,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;AACrJ,CAAC;AAED,8FAA8F;AAE9F,6GAA6G;AACtG,MAAM,eAAe,GAAG,CAAC,UAA0B,EAAE,GAAW,EAAE,EAAE,CAAC,OAAO,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,UAAU,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,EAAE,CAAA;AAA1J,QAAA,eAAe,mBAA2I;AAEvK;;;;GAIG;AACI,KAAK,UAAU,aAAa,CAAC,EAAiB,EAAE,MAAW,EAAE,UAAsB,EAAE,OAAY;IACtG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS;QAAE,OAAO,UAAU,CAAA;IAC1E,IAAI,CAAC,MAAM,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,MAAM;QAAE,OAAO,UAAU,CAAA;IACxE,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAA;IACtB,MAAM,OAAO,GAAG,MAAM,EAAE;SACrB,aAAa,CAAC,uBAAU,CAAC;SACzB,MAAM,CAAC,EAAE,EAAE,EAAE,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,IAAA,YAAE,EAAC,qCAAwB,CAAC,EAAS,EAAE,EAAE,MAAM,EAAE,6BAAgB,CAAC,SAAS,EAAE,WAAW,EAAE,GAAG,EAAE,aAAa,EAAE,IAAA,uBAAe,EAAC,UAAU,EAAE,IAAA,eAAO,EAAC,MAAM,EAAE,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,EAAS,CAAC,CAAA;IAC7O,IAAI,CAAC,OAAO,CAAC,QAAQ;QAAE,OAAO,MAAM,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,CAAC,CAAA;IAC3E,MAAM,SAAS,GAAG,MAAM,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,CAAC,CAAA;IAC/D,MAAM,IAAA,oCAAiB,EAAC,SAAS,CAAC,UAAU,CAAC,EAAE,SAAS,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;IAC5E,MAAM,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IAC7D,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,+FAA+F;AAE/F,2GAA2G;AACpG,KAAK,UAAU,aAAa,CAAC,OAAY;IAC9C,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;IACtC,MAAM,IAAI,GAAG,MAAM,IAAA,kCAAiB,EAAC,OAAO,CAAC;SAC1C,aAAa,CAAC,uBAAU,CAAC;SACzB,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,IAAA,YAAE,EAAC,CAAC,GAAG,qCAAwB,EAAE,6BAAgB,CAAC,SAAS,CAAC,CAAC,EAAS,EAAE,CAAC,CAAA;IACtJ,MAAM,IAAI,GAAG,CAAC,GAAe,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAC9D,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,MAAM,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,SAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,SAAU,CAAC,CAAC,CAAA;AAC1K,CAAC","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, assign, complete, issue } 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 to-do is issued as its own assignee: nobody else hands it out. 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/** Hands out 「<과정> 수강」 to the person, with the due day - nothing where the domain has not installed the to-do. */\nasync function issueTodo(tx: EntityManager, domain: any, enrollment: Enrollment, course: Course, person: User): Promise<string | null> {\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 context = todoContextOf(tx, domain, person)\n const input = { enrollmentId: enrollment.id, headline, address: learningAddressOf(enrollment.id) }\n const instance = await issue({ name, description: headline, activityId: activity.id, input, assignees: [], threadsMin: 0, dueAt } as any, context)\n await assign({ id: instance.id, assignees: [person.email], dueAt, reason: name }, 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(tx, domain, 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 an enrolment with a reason (decision 8). An open one is withdrawn; a completed one stays on record as\n * cancelled - never deleted - and whoever asked for it is told in the same transaction.\n */\nexport async function cancelEnrollment(id: string, reason: string, context: any): Promise<Enrollment> {\n const { domain, user, tx } = context.state\n if (!reason?.trim()) throw new Refusal('learning-cancel-reason-required', {}, 'say why it is cancelled')\n const row = await enrollmentOf(tx, domain, id)\n if (isOpen(row.status)) {\n if (!(await cancelOpen(tx, domain, row, user, reason.trim()))) 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.trim(), 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.trim(), 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, 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.kind === CourseUnitKind.VIDEO)\n if ('refusal' in verdict) throw new Refusal('learning-position-ahead', { allowed: verdict.allowed }, `the video cannot be ahead of what was watched (${verdict.allowed} s)`)\n progress.learnedSeconds += verdict.add\n progress.watchedSeconds = verdict.watchedSeconds\n progress.positionSeconds = verdict.positionSeconds\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 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, assign, complete, issue } 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 to-do is issued as its own assignee: nobody else hands it out. 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/** Hands out 「<과정> 수강」 to the person, with the due day - nothing where the domain has not installed the to-do. */\nasync function issueTodo(tx: EntityManager, domain: any, enrollment: Enrollment, course: Course, person: User): Promise<string | null> {\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 context = todoContextOf(tx, domain, person)\n const input = { enrollmentId: enrollment.id, headline, address: learningAddressOf(enrollment.id) }\n const instance = await issue({ name, description: headline, activityId: activity.id, input, assignees: [], threadsMin: 0, dueAt } as any, context)\n await assign({ id: instance.id, assignees: [person.email], dueAt, reason: name }, 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(tx, domain, 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 an enrolment with a reason (decision 8). An open one is withdrawn; a completed one stays on record as\n * cancelled - never deleted - and whoever asked for it is told in the same transaction.\n */\nexport async function cancelEnrollment(id: string, reason: string, context: any): Promise<Enrollment> {\n const { domain, user, tx } = context.state\n if (!reason?.trim()) throw new Refusal('learning-cancel-reason-required', {}, 'say why it is cancelled')\n const row = await enrollmentOf(tx, domain, id)\n if (isOpen(row.status)) {\n if (!(await cancelOpen(tx, domain, row, user, reason.trim()))) 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.trim(), 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.trim(), 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,14 +1,8 @@
|
|
|
1
|
-
import { EntityManager } from 'typeorm';
|
|
1
|
+
import { EntityManager, SelectQueryBuilder } from 'typeorm';
|
|
2
2
|
import { User } from '@things-factory/auth-base';
|
|
3
|
+
import { ListParam, ListValueCount } from '@things-factory/shell';
|
|
3
4
|
import { Course } from '../course/course';
|
|
4
5
|
import { Enrollment } from './enrollment';
|
|
5
|
-
export interface StatusFilter {
|
|
6
|
-
courseId?: string | null;
|
|
7
|
-
sourceKind?: string | null;
|
|
8
|
-
sourceId?: string | null;
|
|
9
|
-
/** ASSIGNED, IN_PROGRESS, COMPLETED, CANCELLED or OVERDUE. */
|
|
10
|
-
state?: string | null;
|
|
11
|
-
}
|
|
12
6
|
export interface StatusRow {
|
|
13
7
|
enrollment: Enrollment;
|
|
14
8
|
user: User | null;
|
|
@@ -22,19 +16,38 @@ export interface StatusRow {
|
|
|
22
16
|
}
|
|
23
17
|
/** The rows of the enrolments given - each with its person, course, department, state today and units done. */
|
|
24
18
|
export declare function rowsOf(tx: EntityManager, domain: any, enrollments: Enrollment[]): Promise<StatusRow[]>;
|
|
25
|
-
/** Every enrolment a manager asks for - by course, by source - with its progress and evidence, the soonest due first. */
|
|
26
|
-
export declare function enrollmentStatus(filter: StatusFilter, context: any): Promise<StatusRow[]>;
|
|
27
19
|
/** The departments the request's person heads, with those under them - none for anyone else. */
|
|
28
20
|
export declare function headedDepartmentsOf(context: any): Promise<string[]>;
|
|
29
|
-
/**
|
|
30
|
-
* Where the people of the request's departments stand (decision 11) - for a department head: each open or completed
|
|
31
|
-
* enrolment with its state, due day and completion day. Score, attempts and minutes are left out; refused to anyone who
|
|
32
|
-
* heads no department.
|
|
33
|
-
*/
|
|
34
|
-
export declare function teamEnrollmentStatus(filter: StatusFilter, context: any): Promise<StatusRow[]>;
|
|
35
21
|
/** An enrolment as a department head reads it - where it stands and its days, nothing of how it went. */
|
|
36
22
|
export declare function statusOnly(enrollment: Enrollment): Enrollment;
|
|
37
23
|
/** The completion register of a course - every completed enrolment, by completion day (decision 10). */
|
|
38
24
|
export declare function completionRegister(courseId: string, context: any): Promise<StatusRow[]>;
|
|
39
|
-
/**
|
|
40
|
-
export declare const
|
|
25
|
+
/** Where an enrolment stands as a list shows it - OVERDUE is an open one past its due day, read on the day. */
|
|
26
|
+
export declare const ENROLLMENT_STATES: readonly ["ASSIGNED", "IN_PROGRESS", "COMPLETED", "OVERDUE", "CANCELLED"];
|
|
27
|
+
/**
|
|
28
|
+
* Whose enrolments a request reads (decision 11): a manager everyone's; a department head the people of their
|
|
29
|
+
* departments - open and completed ones, status only; anyone else their own.
|
|
30
|
+
*/
|
|
31
|
+
export type EnrollmentReach = {
|
|
32
|
+
kind: 'all';
|
|
33
|
+
} | {
|
|
34
|
+
kind: 'team';
|
|
35
|
+
userIds: string[];
|
|
36
|
+
} | {
|
|
37
|
+
kind: 'own';
|
|
38
|
+
userId: string;
|
|
39
|
+
};
|
|
40
|
+
/** The people of the request's departments, refused to anyone who heads none. */
|
|
41
|
+
export declare function teamReachOf(context: any): Promise<EnrollmentReach>;
|
|
42
|
+
/** The widest reach the request has - a manager's, a department head's, or their own. */
|
|
43
|
+
export declare function reachOf(context: any): Promise<EnrollmentReach>;
|
|
44
|
+
/**
|
|
45
|
+
* One page of the enrolments a list asks for, within a reach - its conditions, search, sort and page by the framework's
|
|
46
|
+
* list rule, the rows then read with their people, courses and units. A department head's rows are status only.
|
|
47
|
+
*/
|
|
48
|
+
export declare function enrollmentPage(params: ListParam, reach: EnrollmentReach, context: any, extra?: (builder: SelectQueryBuilder<Enrollment>) => void): Promise<{
|
|
49
|
+
rows: StatusRow[];
|
|
50
|
+
total: number;
|
|
51
|
+
}>;
|
|
52
|
+
/** How many enrolments stand in each state under the list's other conditions - every state, in order. */
|
|
53
|
+
export declare function enrollmentStateCounts(params: ListParam, reach: EnrollmentReach, context: any): Promise<ListValueCount[]>;
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.ENROLLMENT_STATES = void 0;
|
|
4
4
|
exports.rowsOf = rowsOf;
|
|
5
|
-
exports.enrollmentStatus = enrollmentStatus;
|
|
6
5
|
exports.headedDepartmentsOf = headedDepartmentsOf;
|
|
7
|
-
exports.teamEnrollmentStatus = teamEnrollmentStatus;
|
|
8
6
|
exports.statusOnly = statusOnly;
|
|
9
7
|
exports.completionRegister = completionRegister;
|
|
8
|
+
exports.teamReachOf = teamReachOf;
|
|
9
|
+
exports.reachOf = reachOf;
|
|
10
|
+
exports.enrollmentPage = enrollmentPage;
|
|
11
|
+
exports.enrollmentStateCounts = enrollmentStateCounts;
|
|
10
12
|
/*
|
|
11
13
|
* Where enrolments stand, for managers and department heads (ADR-0194 decisions 10, 11): every enrolment of a course or
|
|
12
14
|
* a source with its progress and evidence for a manager (`course` query); for a department head, whether each person
|
|
@@ -69,21 +71,6 @@ async function rowsOf(tx, domain, enrollments) {
|
|
|
69
71
|
};
|
|
70
72
|
});
|
|
71
73
|
}
|
|
72
|
-
const filtered = (rows, state) => (state ? rows.filter(one => one.state === state) : rows);
|
|
73
|
-
/** Every enrolment a manager asks for - by course, by source - with its progress and evidence, the soonest due first. */
|
|
74
|
-
async function enrollmentStatus(filter, context) {
|
|
75
|
-
const { domain } = context.state;
|
|
76
|
-
const tx = (0, course_service_1.learningManagerOf)(context);
|
|
77
|
-
const where = { domain: { id: domain.id } };
|
|
78
|
-
if (filter.courseId)
|
|
79
|
-
where.course = { id: filter.courseId };
|
|
80
|
-
if (filter.sourceKind)
|
|
81
|
-
where.sourceKind = filter.sourceKind;
|
|
82
|
-
if (filter.sourceId != null && filter.sourceKind)
|
|
83
|
-
where.sourceId = filter.sourceId;
|
|
84
|
-
const enrollments = await tx.getRepository(enrollment_1.Enrollment).find({ where, order: { dueOn: 'ASC', createdAt: 'ASC' } });
|
|
85
|
-
return filtered(await rowsOf(tx, domain, enrollments), filter.state);
|
|
86
|
-
}
|
|
87
74
|
/** The departments the request's person heads, with those under them - none for anyone else. */
|
|
88
75
|
async function headedDepartmentsOf(context) {
|
|
89
76
|
const { domain, user } = context.state;
|
|
@@ -96,27 +83,6 @@ async function headedDepartmentsOf(context) {
|
|
|
96
83
|
return [];
|
|
97
84
|
return (0, organization_1.withDescendants)(managed.map(one => one.id), await (0, organization_1.departmentTreeOf)(domain, tx));
|
|
98
85
|
}
|
|
99
|
-
/**
|
|
100
|
-
* Where the people of the request's departments stand (decision 11) - for a department head: each open or completed
|
|
101
|
-
* enrolment with its state, due day and completion day. Score, attempts and minutes are left out; refused to anyone who
|
|
102
|
-
* heads no department.
|
|
103
|
-
*/
|
|
104
|
-
async function teamEnrollmentStatus(filter, context) {
|
|
105
|
-
const { domain } = context.state;
|
|
106
|
-
const tx = (0, course_service_1.learningManagerOf)(context);
|
|
107
|
-
const headed = await headedDepartmentsOf(context);
|
|
108
|
-
if (!headed.length)
|
|
109
|
-
throw new shell_1.Refusal('learning-not-a-department-head', {}, 'only a department head reads where their people stand');
|
|
110
|
-
const people = await tx.getRepository(organization_1.Employee).find({ where: { domain: { id: domain.id }, department: { id: (0, typeorm_1.In)(headed) } } });
|
|
111
|
-
const userIds = [...new Set(people.filter(one => one.active !== false && one.userId).map(one => one.userId))];
|
|
112
|
-
if (!userIds.length)
|
|
113
|
-
return [];
|
|
114
|
-
const where = { domain: { id: domain.id }, user: { id: (0, typeorm_1.In)(userIds) }, status: (0, typeorm_1.In)([...enrollment_1.OPEN_ENROLLMENT_STATUSES, enrollment_1.EnrollmentStatus.COMPLETED]) };
|
|
115
|
-
if (filter.courseId)
|
|
116
|
-
where.course = { id: filter.courseId };
|
|
117
|
-
const enrollments = await tx.getRepository(enrollment_1.Enrollment).find({ where, order: { dueOn: 'ASC' } });
|
|
118
|
-
return filtered(await rowsOf(tx, domain, enrollments), filter.state).map(row => ({ ...row, enrollment: statusOnly(row.enrollment) }));
|
|
119
|
-
}
|
|
120
86
|
/** An enrolment as a department head reads it - where it stands and its days, nothing of how it went. */
|
|
121
87
|
function statusOnly(enrollment) {
|
|
122
88
|
const { score, attempts, learnedSeconds, acknowledgedAt, certificateNo, cancelReason, ...kept } = enrollment;
|
|
@@ -129,7 +95,100 @@ async function completionRegister(courseId, context) {
|
|
|
129
95
|
const enrollments = await tx.getRepository(enrollment_1.Enrollment).find({ where: { domain: { id: domain.id }, course: { id: courseId }, status: enrollment_1.EnrollmentStatus.COMPLETED }, order: { completedAt: 'ASC' } });
|
|
130
96
|
return await rowsOf(tx, domain, enrollments);
|
|
131
97
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
exports.
|
|
98
|
+
/* ================================================================ the list rule */
|
|
99
|
+
/** Where an enrolment stands as a list shows it - OVERDUE is an open one past its due day, read on the day. */
|
|
100
|
+
exports.ENROLLMENT_STATES = ['ASSIGNED', 'IN_PROGRESS', 'COMPLETED', 'OVERDUE', 'CANCELLED'];
|
|
101
|
+
/** The people of the request's departments, refused to anyone who heads none. */
|
|
102
|
+
async function teamReachOf(context) {
|
|
103
|
+
const { domain } = context.state;
|
|
104
|
+
const headed = await headedDepartmentsOf(context);
|
|
105
|
+
if (!headed.length)
|
|
106
|
+
throw new shell_1.Refusal('learning-not-a-department-head', {}, 'only a department head reads where their people stand');
|
|
107
|
+
const people = await (0, course_service_1.learningManagerOf)(context).getRepository(organization_1.Employee).find({ where: { domain: { id: domain.id }, department: { id: (0, typeorm_1.In)(headed) } } });
|
|
108
|
+
return { kind: 'team', userIds: [...new Set(people.filter(one => one.active !== false && one.userId).map(one => one.userId))] };
|
|
109
|
+
}
|
|
110
|
+
/** The widest reach the request has - a manager's, a department head's, or their own. */
|
|
111
|
+
async function reachOf(context) {
|
|
112
|
+
if (await (0, auth_base_1.requestHolds)(context, 'course', 'query'))
|
|
113
|
+
return { kind: 'all' };
|
|
114
|
+
if ((await headedDepartmentsOf(context)).length)
|
|
115
|
+
return await teamReachOf(context);
|
|
116
|
+
return { kind: 'own', userId: context.state.user.id };
|
|
117
|
+
}
|
|
118
|
+
/*
|
|
119
|
+
* The list's names (getQueryBuilderFromListParams): `course` a course id, `learner` and `courseName` the words a search
|
|
120
|
+
* reads, `sourceKind` and `sourceId` who asked. `state` is not a column - narrowed below.
|
|
121
|
+
*/
|
|
122
|
+
const LIST = {
|
|
123
|
+
alias: 'E',
|
|
124
|
+
searchables: ['learner', 'courseName'],
|
|
125
|
+
columnExpressions: { course: 'C.id', learner: 'U.name', courseName: 'C.name' },
|
|
126
|
+
sortables: ['dueOn', 'createdAt', 'startedAt', 'completedAt', 'learner', 'courseName'],
|
|
127
|
+
defaultSortings: [{ name: 'dueOn' }, { name: 'createdAt' }]
|
|
128
|
+
};
|
|
129
|
+
const valuesOf = (filter) => (Array.isArray(filter.value) ? filter.value : [filter.value]).map(String).filter(Boolean);
|
|
130
|
+
/** The SQL of the states named - the parameters keyed apart so two conditions in one query never meet. */
|
|
131
|
+
function stateClause(states, today, key) {
|
|
132
|
+
const parameters = { [`${key}Open`]: enrollment_1.OPEN_ENROLLMENT_STATUSES, [`${key}Today`]: today };
|
|
133
|
+
const parts = states
|
|
134
|
+
.filter(state => exports.ENROLLMENT_STATES.includes(state))
|
|
135
|
+
.map((state, i) => {
|
|
136
|
+
if (state === 'OVERDUE')
|
|
137
|
+
return `(E.status IN (:...${key}Open) AND E.dueOn IS NOT NULL AND E.dueOn < :${key}Today)`;
|
|
138
|
+
parameters[`${key}S${i}`] = state;
|
|
139
|
+
if (enrollment_1.OPEN_ENROLLMENT_STATUSES.includes(state))
|
|
140
|
+
return `(E.status = :${key}S${i} AND (E.dueOn IS NULL OR E.dueOn >= :${key}Today))`;
|
|
141
|
+
return `E.status = :${key}S${i}`;
|
|
142
|
+
});
|
|
143
|
+
return parts.length ? { sql: `(${parts.join(' OR ')})`, parameters } : null;
|
|
144
|
+
}
|
|
145
|
+
let narrowSeq = 0;
|
|
146
|
+
/** The joins the list's names read, the reach, and the `state` filter as SQL. */
|
|
147
|
+
function narrow(builder, reach, state, today, extra) {
|
|
148
|
+
const key = `learningList${narrowSeq++}`;
|
|
149
|
+
builder.leftJoin('E.user', 'U').leftJoin('E.course', 'C');
|
|
150
|
+
if (reach.kind === 'own')
|
|
151
|
+
builder.andWhere(`U.id = :${key}Me`, { [`${key}Me`]: reach.userId });
|
|
152
|
+
if (reach.kind === 'team') {
|
|
153
|
+
if (!reach.userIds.length)
|
|
154
|
+
builder.andWhere('1 = 0');
|
|
155
|
+
else
|
|
156
|
+
builder.andWhere(`U.id IN (:...${key}Team)`, { [`${key}Team`]: reach.userIds }).andWhere(`E.status IN (:...${key}Shown)`, { [`${key}Shown`]: [...enrollment_1.OPEN_ENROLLMENT_STATUSES, enrollment_1.EnrollmentStatus.COMPLETED] });
|
|
157
|
+
}
|
|
158
|
+
const clause = state ? stateClause(valuesOf(state), today, `${key}State`) : null;
|
|
159
|
+
if (clause)
|
|
160
|
+
builder.andWhere(['noteq', 'notin'].includes(state.operator) ? `NOT ${clause.sql}` : clause.sql, clause.parameters);
|
|
161
|
+
extra?.(builder);
|
|
162
|
+
}
|
|
163
|
+
/** The filters but `state`, and the `state` one. */
|
|
164
|
+
function splitState(params) {
|
|
165
|
+
const filters = params?.filters ?? [];
|
|
166
|
+
return { rest: filters.filter(one => one.name !== 'state'), state: filters.find(one => one.name === 'state') };
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* One page of the enrolments a list asks for, within a reach - its conditions, search, sort and page by the framework's
|
|
170
|
+
* list rule, the rows then read with their people, courses and units. A department head's rows are status only.
|
|
171
|
+
*/
|
|
172
|
+
async function enrollmentPage(params, reach, context, extra) {
|
|
173
|
+
const { domain } = context.state;
|
|
174
|
+
const tx = (0, course_service_1.learningManagerOf)(context);
|
|
175
|
+
const { rest, state } = splitState(params);
|
|
176
|
+
const builder = (0, shell_1.getQueryBuilderFromListParams)({ ...LIST, repository: tx.getRepository(enrollment_1.Enrollment), params: { ...params, filters: rest }, domain });
|
|
177
|
+
narrow(builder, reach, state, (0, enrollment_service_1.todayIn)(domain), extra);
|
|
178
|
+
const [enrollments, total] = await builder.getManyAndCount();
|
|
179
|
+
const rows = await rowsOf(tx, domain, enrollments);
|
|
180
|
+
return { rows: reach.kind === 'team' ? rows.map(row => ({ ...row, enrollment: statusOnly(row.enrollment) })) : rows, total };
|
|
181
|
+
}
|
|
182
|
+
/** How many enrolments stand in each state under the list's other conditions - every state, in order. */
|
|
183
|
+
async function enrollmentStateCounts(params, reach, context) {
|
|
184
|
+
const { domain } = context.state;
|
|
185
|
+
const tx = (0, course_service_1.learningManagerOf)(context);
|
|
186
|
+
const today = (0, enrollment_service_1.todayIn)(domain);
|
|
187
|
+
const { rest } = splitState(params);
|
|
188
|
+
const common = { ...LIST, repository: tx.getRepository(enrollment_1.Enrollment), domain, params: { filters: rest, search: params?.search, inherited: params?.inherited }, field: 'status' };
|
|
189
|
+
const all = await (0, shell_1.countListByField)({ ...common, narrow: builder => narrow(builder, reach, undefined, today) });
|
|
190
|
+
const late = await (0, shell_1.countListByField)({ ...common, narrow: builder => narrow(builder, reach, { name: 'state', operator: 'eq', value: 'OVERDUE' }, today) });
|
|
191
|
+
const overdueCount = [...late.values()].reduce((sum, one) => sum + one, 0);
|
|
192
|
+
return exports.ENROLLMENT_STATES.map(value => ({ value, count: value === 'OVERDUE' ? overdueCount : (all.get(value) ?? 0) - (late.get(value) ?? 0) }));
|
|
193
|
+
}
|
|
135
194
|
//# sourceMappingURL=enrollment-status.js.map
|