@things-factory/learning 10.1.260 → 10.1.262
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 -5
- package/dist-client/pages/learning-course-list.js +54 -16
- package/dist-client/pages/learning-course-list.js.map +1 -1
- package/dist-client/pages/learning-enrollment-status.d.ts +31 -16
- package/dist-client/pages/learning-enrollment-status.js +104 -117
- 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 +2 -0
- package/dist-server/service/course/course-query.js +27 -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-mutation.d.ts +1 -1
- package/dist-server/service/enrollment/enrollment-mutation.js +6 -6
- package/dist-server/service/enrollment/enrollment-mutation.js.map +1 -1
- package/dist-server/service/enrollment/enrollment-query.d.ts +8 -4
- package/dist-server/service/enrollment/enrollment-query.js +47 -36
- package/dist-server/service/enrollment/enrollment-query.js.map +1 -1
- package/dist-server/service/enrollment/enrollment-service.d.ts +4 -3
- package/dist-server/service/enrollment/enrollment-service.js +41 -18
- package/dist-server/service/enrollment/enrollment-service.js.map +1 -1
- package/dist-server/service/enrollment/enrollment-status.d.ts +42 -18
- package/dist-server/service/enrollment/enrollment-status.js +143 -42
- package/dist-server/service/enrollment/enrollment-status.js.map +1 -1
- package/dist-server/service/enrollment/enrollment-type.d.ts +2 -8
- package/dist-server/service/enrollment/enrollment-type.js +20 -42
- 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/index.d.ts +3 -3
- package/dist-server/service/enrollment/index.js +3 -3
- package/dist-server/service/enrollment/index.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 +40 -11
- package/dist-server/service/enrollment/progress-rules.js +54 -13
- 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 +2 -2
- package/dist-server/service/index.js +2 -2
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +14 -13
- package/translations/en.json +42 -9
- package/translations/es.json +42 -9
- package/translations/ja.json +42 -9
- package/translations/ko.json +42 -9
- package/translations/ms.json +42 -9
- package/translations/zh.json +42 -9
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
export declare const UNIT_FIELDS = "id seq kind name body url durationSeconds minSeconds file { id name fullpath mimetype }";
|
|
2
|
-
|
|
1
|
+
export declare const UNIT_FIELDS = "id seq kind name body url durationSeconds pageCount secondsPerPage minSeconds file { id name fullpath mimetype }";
|
|
2
|
+
/** One unit's progress as the server keeps it - where it stands and the time it acknowledged. */
|
|
3
|
+
export declare const PROGRESS_FIELDS = "learnedSeconds watchedSeconds positionSeconds pagesSeen pagesRead pageSeconds pageAt doneAt";
|
|
4
|
+
export declare const LEARNING_FIELDS = "\n id status state dueOn startedAt learnedSeconds score attempts attemptsLeft quizSize missing acknowledgedAt completedAt certificateNo\n course { id name description category }\n version { id number minSeconds acknowledgement passScore questionCount attemptLimit }\n units { counts unit { id seq kind name body url durationSeconds pageCount secondsPerPage minSeconds file { id name fullpath mimetype } } progress { learnedSeconds watchedSeconds positionSeconds pagesSeen pagesRead pageSeconds pageAt doneAt } }\n lastUnitId\n";
|
|
3
5
|
export declare const MY_LIST_FIELDS = "\n id status state dueOn completedAt certificateNo\n course { id name category }\n units { counts progress { doneAt } }\n";
|
|
4
|
-
export declare const VERSION_FIELDS = "\n id number status minSeconds acknowledgement passScore questionCount attemptLimit sourceKind sourceNote publishedAt closedAt\n units { id seq kind name body url durationSeconds minSeconds file { id name fullpath mimetype } }\n questions { id seq kind text explanation drafted choices { id text correct } }\n";
|
|
6
|
+
export declare const VERSION_FIELDS = "\n id number status minSeconds acknowledgement passScore questionCount attemptLimit sourceKind sourceNote publishedAt closedAt\n units { id seq kind name body url durationSeconds pageCount secondsPerPage minSeconds file { id name fullpath mimetype } }\n questions { id seq kind text explanation drafted choices { id text correct } }\n";
|
|
5
7
|
export declare const COURSE_FIELDS = "\n id name description category updatedAt\n manager { id name }\n publishedVersion { id number }\n versions { id number status publishedAt closedAt }\n";
|
|
6
8
|
export declare const STATUS_ROW_FIELDS = "\n id state departmentName units unitsDone versionNumber\n user { id name email }\n course { id name }\n enrollment { id status dueOn sourceKind startedAt completedAt learnedSeconds score attempts certificateNo cancelReason }\n";
|
|
7
9
|
/** Units done of those that count, from an enrolment's units. */
|
|
@@ -14,3 +16,46 @@ export declare function unitsDoneOf(units?: {
|
|
|
14
16
|
done: number;
|
|
15
17
|
total: number;
|
|
16
18
|
};
|
|
19
|
+
type UnitRow = {
|
|
20
|
+
counts: boolean;
|
|
21
|
+
unit: {
|
|
22
|
+
id: string;
|
|
23
|
+
};
|
|
24
|
+
progress?: {
|
|
25
|
+
doneAt?: string | null;
|
|
26
|
+
} | null;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* The unit the course opens on: the one learned last, on any device; else the first that counts and is not done; else
|
|
30
|
+
* the first. Opening on the first not done sent a learner back from where they were (2026-11-08).
|
|
31
|
+
*/
|
|
32
|
+
export declare function resumeUnitId(enrollment: {
|
|
33
|
+
lastUnitId?: string | null;
|
|
34
|
+
units?: UnitRow[];
|
|
35
|
+
} | null | undefined): string;
|
|
36
|
+
/**
|
|
37
|
+
* The seconds the page in reading was looked at already, as the server acknowledged them - a learner coming back to the
|
|
38
|
+
* page carries on from there. They belong to the furthest page reached, and only while it is the page left on.
|
|
39
|
+
*/
|
|
40
|
+
export declare function pageLookedOf(progress: {
|
|
41
|
+
pagesSeen?: number | null;
|
|
42
|
+
pageAt?: number | null;
|
|
43
|
+
pageSeconds?: number | null;
|
|
44
|
+
} | null | undefined): number;
|
|
45
|
+
/**
|
|
46
|
+
* The progress a course screen starts each lesson from (2026-11-08). The server's answer to each tick is kept by unit;
|
|
47
|
+
* a lesson opened - the same unit coming back too - starts from the newest of that and what the page read. Taken once
|
|
48
|
+
* per lesson shown: the lesson counts its own time on from there, and a newer value given while it plays would count
|
|
49
|
+
* that time twice. `reopen` lets the next lesson shown take the newest again.
|
|
50
|
+
*/
|
|
51
|
+
export declare class LessonStarts {
|
|
52
|
+
private saved;
|
|
53
|
+
private starts;
|
|
54
|
+
/** The server's answer for a unit of an enrolment. */
|
|
55
|
+
keep(enrollmentId: string, unitId: string, progress: any): void;
|
|
56
|
+
/** Where the lesson of a unit starts - fixed until `reopen`. */
|
|
57
|
+
startOf(enrollmentId: string, unitId: string, read: any): any;
|
|
58
|
+
/** The lesson shown has left - the next one starts from the newest answer. */
|
|
59
|
+
reopen(): void;
|
|
60
|
+
}
|
|
61
|
+
export {};
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
/* The fields the screens of this module read - one place, so a page and a popup ask for the same shape. */
|
|
2
|
-
export const UNIT_FIELDS = `id seq kind name body url durationSeconds minSeconds file { id name fullpath mimetype }`;
|
|
2
|
+
export const UNIT_FIELDS = `id seq kind name body url durationSeconds pageCount secondsPerPage minSeconds file { id name fullpath mimetype }`;
|
|
3
|
+
/** One unit's progress as the server keeps it - where it stands and the time it acknowledged. */
|
|
4
|
+
export const PROGRESS_FIELDS = `learnedSeconds watchedSeconds positionSeconds pagesSeen pagesRead pageSeconds pageAt doneAt`;
|
|
3
5
|
export const LEARNING_FIELDS = `
|
|
4
6
|
id status state dueOn startedAt learnedSeconds score attempts attemptsLeft quizSize missing acknowledgedAt completedAt certificateNo
|
|
5
7
|
course { id name description category }
|
|
6
8
|
version { id number minSeconds acknowledgement passScore questionCount attemptLimit }
|
|
7
|
-
units { counts unit { ${UNIT_FIELDS} } progress {
|
|
9
|
+
units { counts unit { ${UNIT_FIELDS} } progress { ${PROGRESS_FIELDS} } }
|
|
10
|
+
lastUnitId
|
|
8
11
|
`;
|
|
9
12
|
export const MY_LIST_FIELDS = `
|
|
10
13
|
id status state dueOn completedAt certificateNo
|
|
@@ -33,4 +36,50 @@ export function unitsDoneOf(units = []) {
|
|
|
33
36
|
const counted = units.filter(one => one.counts);
|
|
34
37
|
return { done: counted.filter(one => one.progress?.doneAt).length, total: counted.length };
|
|
35
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* The unit the course opens on: the one learned last, on any device; else the first that counts and is not done; else
|
|
41
|
+
* the first. Opening on the first not done sent a learner back from where they were (2026-11-08).
|
|
42
|
+
*/
|
|
43
|
+
export function resumeUnitId(enrollment) {
|
|
44
|
+
const units = enrollment?.units ?? [];
|
|
45
|
+
const last = units.find(row => row.unit.id === enrollment?.lastUnitId);
|
|
46
|
+
return (last ?? units.find(row => row.counts && !row.progress?.doneAt) ?? units[0])?.unit.id ?? '';
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The seconds the page in reading was looked at already, as the server acknowledged them - a learner coming back to the
|
|
50
|
+
* page carries on from there. They belong to the furthest page reached, and only while it is the page left on.
|
|
51
|
+
*/
|
|
52
|
+
export function pageLookedOf(progress) {
|
|
53
|
+
if (!progress?.pageAt || progress.pageAt !== progress.pagesSeen)
|
|
54
|
+
return 0;
|
|
55
|
+
return progress.pageSeconds ?? 0;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The progress a course screen starts each lesson from (2026-11-08). The server's answer to each tick is kept by unit;
|
|
59
|
+
* a lesson opened - the same unit coming back too - starts from the newest of that and what the page read. Taken once
|
|
60
|
+
* per lesson shown: the lesson counts its own time on from there, and a newer value given while it plays would count
|
|
61
|
+
* that time twice. `reopen` lets the next lesson shown take the newest again.
|
|
62
|
+
*/
|
|
63
|
+
export class LessonStarts {
|
|
64
|
+
constructor() {
|
|
65
|
+
this.saved = new Map();
|
|
66
|
+
this.starts = new Map();
|
|
67
|
+
}
|
|
68
|
+
/** The server's answer for a unit of an enrolment. */
|
|
69
|
+
keep(enrollmentId, unitId, progress) {
|
|
70
|
+
if (progress)
|
|
71
|
+
this.saved.set(`${enrollmentId}:${unitId}`, progress);
|
|
72
|
+
}
|
|
73
|
+
/** Where the lesson of a unit starts - fixed until `reopen`. */
|
|
74
|
+
startOf(enrollmentId, unitId, read) {
|
|
75
|
+
const key = `${enrollmentId}:${unitId}`;
|
|
76
|
+
if (!this.starts.has(key))
|
|
77
|
+
this.starts.set(key, this.saved.get(key) ?? read ?? {});
|
|
78
|
+
return this.starts.get(key);
|
|
79
|
+
}
|
|
80
|
+
/** The lesson shown has left - the next one starts from the newest answer. */
|
|
81
|
+
reopen() {
|
|
82
|
+
this.starts.clear();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
36
85
|
//# sourceMappingURL=learning-api.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"learning-api.js","sourceRoot":"","sources":["../../client/utils/learning-api.ts"],"names":[],"mappings":"AAAA,2GAA2G;AAE3G,MAAM,CAAC,MAAM,WAAW,GAAG,
|
|
1
|
+
{"version":3,"file":"learning-api.js","sourceRoot":"","sources":["../../client/utils/learning-api.ts"],"names":[],"mappings":"AAAA,2GAA2G;AAE3G,MAAM,CAAC,MAAM,WAAW,GAAG,kHAAkH,CAAA;AAE7I,iGAAiG;AACjG,MAAM,CAAC,MAAM,eAAe,GAAG,6FAA6F,CAAA;AAE5H,MAAM,CAAC,MAAM,eAAe,GAAG;;;;0BAIL,WAAW,iBAAiB,eAAe;;CAEpE,CAAA;AAED,MAAM,CAAC,MAAM,cAAc,GAAG;;;;CAI7B,CAAA;AAED,MAAM,CAAC,MAAM,cAAc,GAAG;;YAElB,WAAW;;CAEtB,CAAA;AAED,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;CAK5B,CAAA;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAG;;;;;CAKhC,CAAA;AAED,iEAAiE;AACjE,MAAM,UAAU,WAAW,CAAC,QAA6E,EAAE;IACzG,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IAC/C,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,MAAM,EAAE,CAAA;AAC5F,CAAC;AAID;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,UAAgF;IAC3G,MAAM,KAAK,GAAG,UAAU,EAAE,KAAK,IAAI,EAAE,CAAA;IACrC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,UAAU,EAAE,UAAU,CAAC,CAAA;IACtE,OAAO,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,CAAA;AACpG,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,QAA+G;IAC1I,IAAI,CAAC,QAAQ,EAAE,MAAM,IAAI,QAAQ,CAAC,MAAM,KAAK,QAAQ,CAAC,SAAS;QAAE,OAAO,CAAC,CAAA;IACzE,OAAO,QAAQ,CAAC,WAAW,IAAI,CAAC,CAAA;AAClC,CAAC;AAED;;;;;GAKG;AACH,MAAM,OAAO,YAAY;IAAzB;QACU,UAAK,GAAG,IAAI,GAAG,EAAe,CAAA;QAC9B,WAAM,GAAG,IAAI,GAAG,EAAe,CAAA;IAkBzC,CAAC;IAhBC,sDAAsD;IACtD,IAAI,CAAC,YAAoB,EAAE,MAAc,EAAE,QAAa;QACtD,IAAI,QAAQ;YAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,YAAY,IAAI,MAAM,EAAE,EAAE,QAAQ,CAAC,CAAA;IACrE,CAAC;IAED,gEAAgE;IAChE,OAAO,CAAC,YAAoB,EAAE,MAAc,EAAE,IAAS;QACrD,MAAM,GAAG,GAAG,GAAG,YAAY,IAAI,MAAM,EAAE,CAAA;QACvC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC,CAAA;QAClF,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IAC7B,CAAC;IAED,8EAA8E;IAC9E,MAAM;QACJ,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAA;IACrB,CAAC;CACF","sourcesContent":["/* The fields the screens of this module read - one place, so a page and a popup ask for the same shape. */\n\nexport const UNIT_FIELDS = `id seq kind name body url durationSeconds pageCount secondsPerPage minSeconds file { id name fullpath mimetype }`\n\n/** One unit's progress as the server keeps it - where it stands and the time it acknowledged. */\nexport const PROGRESS_FIELDS = `learnedSeconds watchedSeconds positionSeconds pagesSeen pagesRead pageSeconds pageAt doneAt`\n\nexport const LEARNING_FIELDS = `\n id status state dueOn startedAt learnedSeconds score attempts attemptsLeft quizSize missing acknowledgedAt completedAt certificateNo\n course { id name description category }\n version { id number minSeconds acknowledgement passScore questionCount attemptLimit }\n units { counts unit { ${UNIT_FIELDS} } progress { ${PROGRESS_FIELDS} } }\n lastUnitId\n`\n\nexport const MY_LIST_FIELDS = `\n id status state dueOn completedAt certificateNo\n course { id name category }\n units { counts progress { doneAt } }\n`\n\nexport const VERSION_FIELDS = `\n id number status minSeconds acknowledgement passScore questionCount attemptLimit sourceKind sourceNote publishedAt closedAt\n units { ${UNIT_FIELDS} }\n questions { id seq kind text explanation drafted choices { id text correct } }\n`\n\nexport const COURSE_FIELDS = `\n id name description category updatedAt\n manager { id name }\n publishedVersion { id number }\n versions { id number status publishedAt closedAt }\n`\n\nexport const STATUS_ROW_FIELDS = `\n id state departmentName units unitsDone versionNumber\n user { id name email }\n course { id name }\n enrollment { id status dueOn sourceKind startedAt completedAt learnedSeconds score attempts certificateNo cancelReason }\n`\n\n/** Units done of those that count, from an enrolment's units. */\nexport function unitsDoneOf(units: { counts: boolean; progress?: { doneAt?: string | null } | null }[] = []) {\n const counted = units.filter(one => one.counts)\n return { done: counted.filter(one => one.progress?.doneAt).length, total: counted.length }\n}\n\ntype UnitRow = { counts: boolean; unit: { id: string }; progress?: { doneAt?: string | null } | null }\n\n/**\n * The unit the course opens on: the one learned last, on any device; else the first that counts and is not done; else\n * the first. Opening on the first not done sent a learner back from where they were (2026-11-08).\n */\nexport function resumeUnitId(enrollment: { lastUnitId?: string | null; units?: UnitRow[] } | null | undefined): string {\n const units = enrollment?.units ?? []\n const last = units.find(row => row.unit.id === enrollment?.lastUnitId)\n return (last ?? units.find(row => row.counts && !row.progress?.doneAt) ?? units[0])?.unit.id ?? ''\n}\n\n/**\n * The seconds the page in reading was looked at already, as the server acknowledged them - a learner coming back to the\n * page carries on from there. They belong to the furthest page reached, and only while it is the page left on.\n */\nexport function pageLookedOf(progress: { pagesSeen?: number | null; pageAt?: number | null; pageSeconds?: number | null } | null | undefined): number {\n if (!progress?.pageAt || progress.pageAt !== progress.pagesSeen) return 0\n return progress.pageSeconds ?? 0\n}\n\n/**\n * The progress a course screen starts each lesson from (2026-11-08). The server's answer to each tick is kept by unit;\n * a lesson opened - the same unit coming back too - starts from the newest of that and what the page read. Taken once\n * per lesson shown: the lesson counts its own time on from there, and a newer value given while it plays would count\n * that time twice. `reopen` lets the next lesson shown take the newest again.\n */\nexport class LessonStarts {\n private saved = new Map<string, any>()\n private starts = new Map<string, any>()\n\n /** The server's answer for a unit of an enrolment. */\n keep(enrollmentId: string, unitId: string, progress: any) {\n if (progress) this.saved.set(`${enrollmentId}:${unitId}`, progress)\n }\n\n /** Where the lesson of a unit starts - fixed until `reopen`. */\n startOf(enrollmentId: string, unitId: string, read: any): any {\n const key = `${enrollmentId}:${unitId}`\n if (!this.starts.has(key)) this.starts.set(key, this.saved.get(key) ?? read ?? {})\n return this.starts.get(key)\n }\n\n /** The lesson shown has left - the next one starts from the newest answer. */\n reopen() {\n this.starts.clear()\n }\n}\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type MaterialPart = {
|
|
2
|
+
id: string;
|
|
3
|
+
text?: string;
|
|
4
|
+
image?: string;
|
|
5
|
+
};
|
|
6
|
+
export type MaterialSource = {
|
|
7
|
+
fileId: string;
|
|
8
|
+
parts: MaterialPart[];
|
|
9
|
+
};
|
|
10
|
+
/** Complete captions, never a guessed transcript from the video's name or a few sampled frames. */
|
|
11
|
+
export declare function captionParts(text: string): MaterialPart[];
|
|
12
|
+
export declare function pdfMaterial(fileId: string, url: string, signal: AbortSignal): Promise<MaterialSource>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { drawPdfPage, loadPdfjs, isScannedPage, pdfPageText } from '@things-factory/ai-assistant/dist-client/index.js';
|
|
2
|
+
/** Complete captions, never a guessed transcript from the video's name or a few sampled frames. */
|
|
3
|
+
export function captionParts(text) {
|
|
4
|
+
const clean = text.replace(/^\uFEFF/, '').trim();
|
|
5
|
+
if (!clean || clean.length > 120_000)
|
|
6
|
+
throw new Error('learning-ai-source-limit');
|
|
7
|
+
// Keep timestamps and cue order as evidence. Plain transcripts are also accepted, explicitly supplied by the manager.
|
|
8
|
+
const blocks = clean.split(/\r?\n\s*\r?\n/).filter(s => s.trim() && s.trim() !== 'WEBVTT');
|
|
9
|
+
const parts = [];
|
|
10
|
+
let current = '';
|
|
11
|
+
for (const block of blocks) {
|
|
12
|
+
if (current && current.length + block.length > 8000) {
|
|
13
|
+
parts.push({ id: `caption-${parts.length + 1}`, text: current });
|
|
14
|
+
current = '';
|
|
15
|
+
}
|
|
16
|
+
current += (current ? '\n\n' : '') + block;
|
|
17
|
+
}
|
|
18
|
+
if (current)
|
|
19
|
+
parts.push({ id: `caption-${parts.length + 1}`, text: current });
|
|
20
|
+
if (!parts.length || parts.length > 80)
|
|
21
|
+
throw new Error('learning-ai-source-limit');
|
|
22
|
+
return parts;
|
|
23
|
+
}
|
|
24
|
+
const blobData = (blob) => new Promise((resolve, reject) => {
|
|
25
|
+
const reader = new FileReader();
|
|
26
|
+
reader.onload = () => resolve(String(reader.result));
|
|
27
|
+
reader.onerror = () => reject(reader.error);
|
|
28
|
+
reader.readAsDataURL(blob);
|
|
29
|
+
});
|
|
30
|
+
export async function pdfMaterial(fileId, url, signal) {
|
|
31
|
+
const response = await fetch(url, { credentials: 'include', signal });
|
|
32
|
+
if (!response.ok)
|
|
33
|
+
throw new Error('learning-ai-source-read');
|
|
34
|
+
const blob = await response.blob();
|
|
35
|
+
if (blob.size > 50 * 1024 * 1024)
|
|
36
|
+
throw new Error('learning-ai-source-limit');
|
|
37
|
+
const pdfjs = await loadPdfjs();
|
|
38
|
+
const loading = pdfjs.getDocument({ data: new Uint8Array(await blob.arrayBuffer()) });
|
|
39
|
+
const abort = () => { void loading.destroy(); };
|
|
40
|
+
signal.addEventListener('abort', abort, { once: true });
|
|
41
|
+
try {
|
|
42
|
+
if (signal.aborted)
|
|
43
|
+
throw new Error('learning-ai-source-read');
|
|
44
|
+
const pdf = await loading.promise;
|
|
45
|
+
if (!pdf.numPages || pdf.numPages > 80)
|
|
46
|
+
throw new Error('learning-ai-source-limit');
|
|
47
|
+
const parts = [];
|
|
48
|
+
let chars = 0, scans = 0, imageChars = 0;
|
|
49
|
+
for (let n = 1; n <= pdf.numPages; n++) {
|
|
50
|
+
if (signal.aborted)
|
|
51
|
+
throw new Error('learning-ai-source-read');
|
|
52
|
+
const page = await pdf.getPage(n);
|
|
53
|
+
const text = pdfPageText((await page.getTextContent()).items);
|
|
54
|
+
if (isScannedPage(text)) {
|
|
55
|
+
if (++scans > 12)
|
|
56
|
+
throw new Error('learning-ai-source-limit');
|
|
57
|
+
const image = await blobData(await drawPdfPage(pdf, n));
|
|
58
|
+
imageChars += image.length;
|
|
59
|
+
if (imageChars > 8_000_000)
|
|
60
|
+
throw new Error('learning-ai-source-limit');
|
|
61
|
+
parts.push({ id: `page-${n}`, image });
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
chars += text.length;
|
|
65
|
+
if (chars > 120_000)
|
|
66
|
+
throw new Error('learning-ai-source-limit');
|
|
67
|
+
parts.push({ id: `page-${n}`, text });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return { fileId, parts };
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
signal.removeEventListener('abort', abort);
|
|
74
|
+
await loading.destroy();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=material-source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"material-source.js","sourceRoot":"","sources":["../../client/utils/material-source.ts"],"names":[],"mappings":"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","sourcesContent":["import { drawPdfPage, loadPdfjs, isScannedPage, pdfPageText } from '@things-factory/ai-assistant/dist-client/index.js'\n\nexport type MaterialPart = { id: string; text?: string; image?: string }\nexport type MaterialSource = { fileId: string; parts: MaterialPart[] }\n\n/** Complete captions, never a guessed transcript from the video's name or a few sampled frames. */\nexport function captionParts(text: string): MaterialPart[] {\n const clean = text.replace(/^\\uFEFF/, '').trim()\n if (!clean || clean.length > 120_000) throw new Error('learning-ai-source-limit')\n // Keep timestamps and cue order as evidence. Plain transcripts are also accepted, explicitly supplied by the manager.\n const blocks = clean.split(/\\r?\\n\\s*\\r?\\n/).filter(s => s.trim() && s.trim() !== 'WEBVTT')\n const parts: MaterialPart[] = []\n let current = ''\n for (const block of blocks) {\n if (current && current.length + block.length > 8000) { parts.push({ id: `caption-${parts.length + 1}`, text: current }); current = '' }\n current += (current ? '\\n\\n' : '') + block\n }\n if (current) parts.push({ id: `caption-${parts.length + 1}`, text: current })\n if (!parts.length || parts.length > 80) throw new Error('learning-ai-source-limit')\n return parts\n}\n\nconst blobData = (blob: Blob): Promise<string> => new Promise((resolve, reject) => {\n const reader = new FileReader()\n reader.onload = () => resolve(String(reader.result))\n reader.onerror = () => reject(reader.error)\n reader.readAsDataURL(blob)\n})\n\nexport async function pdfMaterial(fileId: string, url: string, signal: AbortSignal): Promise<MaterialSource> {\n const response = await fetch(url, { credentials: 'include', signal })\n if (!response.ok) throw new Error('learning-ai-source-read')\n const blob = await response.blob()\n if (blob.size > 50 * 1024 * 1024) throw new Error('learning-ai-source-limit')\n const pdfjs = await loadPdfjs()\n const loading = pdfjs.getDocument({ data: new Uint8Array(await blob.arrayBuffer()) })\n const abort = () => { void loading.destroy() }\n signal.addEventListener('abort', abort, { once: true })\n try {\n if (signal.aborted) throw new Error('learning-ai-source-read')\n const pdf = await loading.promise\n if (!pdf.numPages || pdf.numPages > 80) throw new Error('learning-ai-source-limit')\n const parts: MaterialPart[] = []\n let chars = 0, scans = 0, imageChars = 0\n for (let n = 1; n <= pdf.numPages; n++) {\n if (signal.aborted) throw new Error('learning-ai-source-read')\n const page = await pdf.getPage(n)\n const text = pdfPageText((await page.getTextContent()).items)\n if (isScannedPage(text)) {\n if (++scans > 12) throw new Error('learning-ai-source-limit')\n const image = await blobData(await drawPdfPage(pdf, n))\n imageChars += image.length\n if (imageChars > 8_000_000) throw new Error('learning-ai-source-limit')\n parts.push({ id: `page-${n}`, image })\n } else {\n chars += text.length\n if (chars > 120_000) throw new Error('learning-ai-source-limit')\n parts.push({ id: `page-${n}`, text })\n }\n }\n return { fileId, parts }\n } finally { signal.removeEventListener('abort', abort); await loading.destroy() }\n}\n"]}
|
package/dist-server/ai-reads.js
CHANGED
|
@@ -25,7 +25,7 @@ exports.LEARNING_ENROLLMENTS_READ = {
|
|
|
25
25
|
{ name: 'unitsDone', type: 'number', labelKey: 'field.learning-units-done' }
|
|
26
26
|
],
|
|
27
27
|
status: { field: 'state', values: ['ASSIGNED', 'IN_PROGRESS', 'COMPLETED', 'OVERDUE'] },
|
|
28
|
-
search: "learner's name
|
|
28
|
+
search: "learner's name or course",
|
|
29
29
|
route: 'learning-enrollment-status'
|
|
30
30
|
};
|
|
31
31
|
exports.LEARNING_CONTENT_READ = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-reads.js","sourceRoot":"","sources":["../server/ai-reads.ts"],"names":[],"mappings":";;;AA+DA,sDAIC;AAnED;;;GAGG;AACH,+DAA4K;AAE5K,kEAAgE;AAEnD,QAAA,yBAAyB,GAAa;IACjD,GAAG,EAAE,sBAAsB;IAC3B,KAAK,EAAE,kCAAkC;IACzC,IAAI,EAAE,4RAA4R;IAClS,SAAS,EAAE,qBAAqB;IAChC,SAAS,EAAE,6FAA6F;IACxG,OAAO,EAAE;QACP,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,wBAAwB,EAAE;QACvE,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,2BAA2B,EAAE;QACjF,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,4BAA4B,EAAE;QAC1E,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,sBAAsB,EAAE;QACnE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,uBAAuB,EAAE;QAClE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,0BAA0B,EAAE;QAC1E,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,6BAA6B,EAAE;QAC9E,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,2BAA2B,EAAE;KAC7E;IACD,MAAM,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,UAAU,EAAE,aAAa,EAAE,WAAW,EAAE,SAAS,CAAC,EAAE;IACvF,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"ai-reads.js","sourceRoot":"","sources":["../server/ai-reads.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * The assistant's reads and entry of learning (ADR-0194 decision 12) - registered at load; an application names the keys\n * in its assistant mode's `readTargets`.\n */\nimport { listReadTarget, registerDetailRead, registerEntryTarget, registerReadTarget, type DetailRead, type EntryTarget, type ListRead } from '@things-factory/ai-assistant'\n\nimport { choicesOfText } from './service/enrollment/learning-ai'\n\nexport const LEARNING_ENROLLMENTS_READ: ListRead = {\n key: 'learning.enrollments',\n title: 'title.learning-enrollment-status',\n name: 'Course enrolments (수강) - who is to take which course by when and where each stands: ASSIGNED (not started), IN_PROGRESS, COMPLETED or OVERDUE (open past its due day), with the days left to the due day. A manager reads everyone, a department head their people, anyone else their own.',\n listQuery: 'learningEnrollments',\n selection: 'id learner departmentName course state dueOn daysLeft startedAt completedAt unitsDone units',\n columns: [\n { name: 'learner', type: 'string', labelKey: 'field.learning-learner' },\n { name: 'departmentName', type: 'string', labelKey: 'field.learning-department' },\n { name: 'course', type: 'string', labelKey: 'field.learning-course-name' },\n { name: 'state', type: 'string', labelKey: 'field.learning-state' },\n { name: 'dueOn', type: 'date', labelKey: 'field.learning-due-on' },\n { name: 'daysLeft', type: 'number', labelKey: 'field.learning-days-left' },\n { name: 'completedAt', type: 'date', labelKey: 'field.learning-completed-on' },\n { name: 'unitsDone', type: 'number', labelKey: 'field.learning-units-done' }\n ],\n status: { field: 'state', values: ['ASSIGNED', 'IN_PROGRESS', 'COMPLETED', 'OVERDUE'] },\n search: \"learner's name or course\",\n route: 'learning-enrollment-status'\n}\n\nexport const LEARNING_CONTENT_READ: DetailRead = {\n key: 'learning.course-content',\n labelKey: 'title.learning-units',\n description:\n \"The units of one course version - names and texts - to answer a question on the course the person is taking (give their enrollmentId; only that course's version, refused while their quiz attempt is open - do not help with the quiz) or, for a manager, to draft questions of a version (give versionId). Answer only from these units.\",\n query: 'query ($enrollmentId: String, $versionId: String) { learningContent(enrollmentId: $enrollmentId, versionId: $versionId) { course version units { seq kind name text } } }',\n args: [\n { name: 'enrollmentId', type: 'string', axis: 'enrollment', description: 'The enrolment open on the course screen (learning-view?id=...)' },\n { name: 'versionId', type: 'string', axis: 'course-version', description: 'A version open in the course editor - managers only' }\n ]\n}\n\nexport const QUESTION_DRAFT_TARGET: EntryTarget = {\n key: 'learning.question-draft',\n labelKey: 'title.learning-questions',\n description:\n 'A quiz question drafted from the units of a DRAFT course version (read learning.course-content with its versionId first). It is entered marked as an AI draft; a manager reviews and saves it before the version can be published. Only a draft version takes it.',\n fields: [\n { name: 'versionId', type: 'string', required: true, axis: 'course-version', description: 'The draft version the question belongs to', labelKey: 'title.learning-versions' },\n { name: 'kind', type: 'enum', required: true, values: ['SINGLE', 'MULTIPLE', 'TRUE_FALSE'], valueKeyPrefix: 'text.learning-question-', labelKey: 'field.learning-question-kind' },\n { name: 'text', type: 'text', required: true, description: 'What it asks - from the units only', labelKey: 'field.learning-question-text' },\n { name: 'choices', type: 'text', required: true, description: 'One choice a line; start each right choice with *. TRUE_FALSE has two lines.', labelKey: 'field.learning-question-choices' },\n { name: 'explanation', type: 'text', description: 'Why the answer is right, citing the unit', labelKey: 'field.learning-question-explanation' }\n ],\n mutation: 'mutation ($versionId: String!, $question: CourseQuestionInput!) { addDraftedCourseQuestion(versionId: $versionId, question: $question) { id } }',\n variables: row => ({\n versionId: row.values.versionId,\n question: { kind: row.values.kind, text: row.values.text, choices: choicesOfText(String(row.values.choices ?? '')), explanation: row.values.explanation ?? null }\n }),\n appliedOf: (data, row) => ({ id: data?.addDraftedCourseQuestion?.id, label: String(row.values.text ?? '').slice(0, 60) }),\n screen: 'learning-course-editor',\n maxRows: 30\n}\n\nexport function registerLearningReads(): void {\n registerReadTarget(listReadTarget(LEARNING_ENROLLMENTS_READ))\n registerDetailRead(LEARNING_CONTENT_READ)\n registerEntryTarget(QUESTION_DRAFT_TARGET)\n}\n\n/** The keys an application's assistant mode names to read learning. */\nexport const LEARNING_READ_TARGETS = [LEARNING_ENROLLMENTS_READ.key, LEARNING_CONTENT_READ.key]\n\n/** The keys an application's assistant mode names to let the assistant enter learning records. */\nexport const LEARNING_ENTRY_TARGETS = [QUESTION_DRAFT_TARGET.key]\n"]}
|
|
@@ -10,7 +10,7 @@ export declare class CourseMutation {
|
|
|
10
10
|
createCourseVersion(courseId: string, fromId: string, context: ResolverContext): Promise<CourseVersion>;
|
|
11
11
|
updateCourseVersion(id: string, patch: CourseVersionPatch, context: ResolverContext): Promise<CourseVersion>;
|
|
12
12
|
saveCourseUnits(versionId: string, units: CourseUnitInput[], context: ResolverContext): Promise<CourseUnit[]>;
|
|
13
|
-
attachCourseUnitFile(unitId: string, file: FileUpload, durationSeconds: number, context: ResolverContext): Promise<CourseUnit>;
|
|
13
|
+
attachCourseUnitFile(unitId: string, file: FileUpload, durationSeconds: number, pageCount: number, context: ResolverContext): Promise<CourseUnit>;
|
|
14
14
|
saveCourseQuestions(versionId: string, questions: CourseQuestionInput[], context: ResolverContext): Promise<CourseQuestion[]>;
|
|
15
15
|
addDraftedCourseQuestion(versionId: string, question: CourseQuestionInput, context: ResolverContext): Promise<CourseQuestion>;
|
|
16
16
|
publishCourseVersion(id: string, context: ResolverContext): Promise<CourseVersion>;
|
|
@@ -28,8 +28,8 @@ let CourseMutation = class CourseMutation {
|
|
|
28
28
|
async saveCourseUnits(versionId, units, context) {
|
|
29
29
|
return await (0, course_service_1.saveCourseUnits)(versionId, units, context);
|
|
30
30
|
}
|
|
31
|
-
async attachCourseUnitFile(unitId, file, durationSeconds, context) {
|
|
32
|
-
return await (0, course_service_1.attachCourseUnitFile)(unitId, file, durationSeconds, context);
|
|
31
|
+
async attachCourseUnitFile(unitId, file, durationSeconds, pageCount, context) {
|
|
32
|
+
return await (0, course_service_1.attachCourseUnitFile)(unitId, file, { durationSeconds, pageCount }, context);
|
|
33
33
|
}
|
|
34
34
|
async saveCourseQuestions(versionId, questions, context) {
|
|
35
35
|
return await (0, course_service_1.saveCourseQuestions)(versionId, questions, context);
|
|
@@ -108,13 +108,14 @@ tslib_1.__decorate([
|
|
|
108
108
|
tslib_1.__decorate([
|
|
109
109
|
(0, type_graphql_1.Directive)('@privilege(category: "course", privilege: "mutation", domainOwnerGranted: true)'),
|
|
110
110
|
(0, type_graphql_1.Directive)('@transaction'),
|
|
111
|
-
(0, type_graphql_1.Mutation)(returns => course_unit_1.CourseUnit, { description: "To attach a video's or a PDF's file to a unit of a draft version - a video with its length." }),
|
|
111
|
+
(0, type_graphql_1.Mutation)(returns => course_unit_1.CourseUnit, { description: "To attach a video's or a PDF's file to a unit of a draft version - a video with its length, a PDF with its page count." }),
|
|
112
112
|
tslib_1.__param(0, (0, type_graphql_1.Arg)('unitId')),
|
|
113
113
|
tslib_1.__param(1, (0, type_graphql_1.Arg)('file', type => GraphQLUpload_js_1.default)),
|
|
114
|
-
tslib_1.__param(2, (0, type_graphql_1.Arg)('durationSeconds', type => type_graphql_1.Int, { nullable: true })),
|
|
115
|
-
tslib_1.__param(3, (0, type_graphql_1.
|
|
114
|
+
tslib_1.__param(2, (0, type_graphql_1.Arg)('durationSeconds', type => type_graphql_1.Int, { nullable: true, description: "A video's length in seconds, read from the file." })),
|
|
115
|
+
tslib_1.__param(3, (0, type_graphql_1.Arg)('pageCount', type => type_graphql_1.Int, { nullable: true, description: "A PDF's page count, read from the file." })),
|
|
116
|
+
tslib_1.__param(4, (0, type_graphql_1.Ctx)()),
|
|
116
117
|
tslib_1.__metadata("design:type", Function),
|
|
117
|
-
tslib_1.__metadata("design:paramtypes", [String, Object, Number, Object]),
|
|
118
|
+
tslib_1.__metadata("design:paramtypes", [String, Object, Number, Number, Object]),
|
|
118
119
|
tslib_1.__metadata("design:returntype", Promise)
|
|
119
120
|
], CourseMutation.prototype, "attachCourseUnitFile", null);
|
|
120
121
|
tslib_1.__decorate([
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"course-mutation.js","sourceRoot":"","sources":["../../../server/service/course/course-mutation.ts"],"names":[],"mappings":"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","sourcesContent":["import type { FileUpload } from 'graphql-upload/GraphQLUpload.js'\nimport GraphQLUpload from 'graphql-upload/GraphQLUpload.js'\nimport { Arg, Ctx, Directive, Int, Mutation, Resolver, UseMiddleware } from 'type-graphql'\n\nimport { Refusal, SpeakRefusals } from '@things-factory/shell'\n\nimport { Course } from './course'\nimport { CourseQuestion } from './course-question'\nimport { CourseUnit } from './course-unit'\nimport { CourseVersion } from './course-version'\nimport { CoursePatch, CourseQuestionInput, CourseUnitInput, CourseVersionPatch, NewCourse } from './course-type'\nimport {\n addDraftedQuestions,\n attachCourseUnitFile,\n closeCourseVersion,\n createCourse,\n createCourseVersion,\n deleteCourseVersion,\n publishCourseVersion,\n saveCourseQuestions,\n saveCourseUnits,\n updateCourse,\n updateCourseVersion\n} from './course-service'\n\n\n/* The managers' writes of courses (ADR-0194 decisions 2-4) - `course` mutation, the task `learning.managing`. */\n@Resolver(Course)\n@UseMiddleware(SpeakRefusals)\nexport class CourseMutation {\n @Directive('@privilege(category: \"course\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => Course, { description: 'To make a course.' })\n async createCourse(@Arg('course', type => NewCourse) course: NewCourse, @Ctx() context: ResolverContext): Promise<Course> {\n return await createCourse(course, context)\n }\n\n @Directive('@privilege(category: \"course\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => Course, { description: \"To change a course's name, description, category or manager.\" })\n async updateCourse(@Arg('id') id: string, @Arg('patch', type => CoursePatch) patch: CoursePatch, @Ctx() context: ResolverContext): Promise<Course> {\n return await updateCourse(id, patch, context)\n }\n\n @Directive('@privilege(category: \"course\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => CourseVersion, { description: 'To make the next draft version of a course - a copy of `fromId`, or of the latest version.' })\n async createCourseVersion(@Arg('courseId') courseId: string, @Arg('fromId', { nullable: true }) fromId: string, @Ctx() context: ResolverContext): Promise<CourseVersion> {\n return await createCourseVersion(courseId, fromId, context)\n }\n\n @Directive('@privilege(category: \"course\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => CourseVersion, { description: 'To change what a draft version says - a published one never changes.' })\n async updateCourseVersion(@Arg('id') id: string, @Arg('patch', type => CourseVersionPatch) patch: CourseVersionPatch, @Ctx() context: ResolverContext): Promise<CourseVersion> {\n return await updateCourseVersion(id, patch, context)\n }\n\n @Directive('@privilege(category: \"course\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => [CourseUnit], { description: 'To save the units of a draft version, in order - the ones left out are deleted.' })\n async saveCourseUnits(@Arg('versionId') versionId: string, @Arg('units', type => [CourseUnitInput]) units: CourseUnitInput[], @Ctx() context: ResolverContext): Promise<CourseUnit[]> {\n return await saveCourseUnits(versionId, units, context)\n }\n\n @Directive('@privilege(category: \"course\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => CourseUnit, { description: \"To attach a video's or a PDF's file to a unit of a draft version - a video with its length.\" })\n async attachCourseUnitFile(\n @Arg('unitId') unitId: string,\n @Arg('file', type => GraphQLUpload) file: FileUpload,\n @Arg('durationSeconds', type => Int, { nullable: true }) durationSeconds: number,\n @Ctx() context: ResolverContext\n ): Promise<CourseUnit> {\n return await attachCourseUnitFile(unitId, file, durationSeconds, context)\n }\n\n @Directive('@privilege(category: \"course\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => [CourseQuestion], { description: 'To save the questions of a draft version, in order - the ones left out are deleted.' })\n async saveCourseQuestions(@Arg('versionId') versionId: string, @Arg('questions', type => [CourseQuestionInput]) questions: CourseQuestionInput[], @Ctx() context: ResolverContext): Promise<CourseQuestion[]> {\n return await saveCourseQuestions(versionId, questions, context)\n }\n\n @Directive('@privilege(category: \"course\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => CourseQuestion, { description: 'To add a question drafted by the assistant to a draft version - marked drafted until a manager saves it (ADR-0194 decision 12).' })\n async addDraftedCourseQuestion(@Arg('versionId') versionId: string, @Arg('question', type => CourseQuestionInput) question: CourseQuestionInput, @Ctx() context: ResolverContext): Promise<CourseQuestion> {\n const [saved] = await addDraftedQuestions(versionId, [question], context)\n if (!saved) throw new Refusal('learning-question-draft-invalid', {}, 'the drafted question is not complete - its text, two choices and the right ones')\n return saved\n }\n\n @Directive('@privilege(category: \"course\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => CourseVersion, { description: 'To publish a draft version - the version published before is closed.' })\n async publishCourseVersion(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<CourseVersion> {\n return await publishCourseVersion(id, context)\n }\n\n @Directive('@privilege(category: \"course\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => CourseVersion, { description: 'To close the published version - nobody starts it any more.' })\n async closeCourseVersion(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<CourseVersion> {\n return await closeCourseVersion(id, context)\n }\n\n @Directive('@privilege(category: \"course\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => Boolean, { description: 'To delete a draft version.' })\n async deleteCourseVersion(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<boolean> {\n return await deleteCourseVersion(id, context)\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"course-mutation.js","sourceRoot":"","sources":["../../../server/service/course/course-mutation.ts"],"names":[],"mappings":";;;;AACA,+FAA2D;AAC3D,+CAA0F;AAE1F,iDAA8D;AAE9D,qCAAiC;AACjC,uDAAkD;AAClD,+CAA0C;AAC1C,qDAAgD;AAChD,+CAAgH;AAChH,qDAYyB;AAGzB,iHAAiH;AAG1G,IAAM,cAAc,GAApB,MAAM,cAAc;IAInB,AAAN,KAAK,CAAC,YAAY,CAAmC,MAAiB,EAAS,OAAwB;QACrG,OAAO,MAAM,IAAA,6BAAY,EAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC5C,CAAC;IAKK,AAAN,KAAK,CAAC,YAAY,CAAY,EAAU,EAAqC,KAAkB,EAAS,OAAwB;QAC9H,OAAO,MAAM,IAAA,6BAAY,EAAC,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,CAAA;IAC/C,CAAC;IAKK,AAAN,KAAK,CAAC,mBAAmB,CAAkB,QAAgB,EAAqC,MAAc,EAAS,OAAwB;QAC7I,OAAO,MAAM,IAAA,oCAAmB,EAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,CAAA;IAC7D,CAAC;IAKK,AAAN,KAAK,CAAC,mBAAmB,CAAY,EAAU,EAA4C,KAAyB,EAAS,OAAwB;QACnJ,OAAO,MAAM,IAAA,oCAAmB,EAAC,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,CAAA;IACtD,CAAC;IAKK,AAAN,KAAK,CAAC,eAAe,CAAmB,SAAiB,EAA2C,KAAwB,EAAS,OAAwB;QAC3J,OAAO,MAAM,IAAA,gCAAe,EAAC,SAAS,EAAE,KAAK,EAAE,OAAO,CAAC,CAAA;IACzD,CAAC;IAKK,AAAN,KAAK,CAAC,oBAAoB,CACT,MAAc,EACO,IAAgB,EACsE,eAAuB,EACtC,SAAiB,EACrH,OAAwB;QAE/B,OAAO,MAAM,IAAA,qCAAoB,EAAC,MAAM,EAAE,IAAI,EAAE,EAAE,eAAe,EAAE,SAAS,EAAE,EAAE,OAAO,CAAC,CAAA;IAC1F,CAAC;IAKK,AAAN,KAAK,CAAC,mBAAmB,CAAmB,SAAiB,EAAmD,SAAgC,EAAS,OAAwB;QAC/K,OAAO,MAAM,IAAA,oCAAmB,EAAC,SAAS,EAAE,SAAS,EAAE,OAAO,CAAC,CAAA;IACjE,CAAC;IAKK,AAAN,KAAK,CAAC,wBAAwB,CAAmB,SAAiB,EAAgD,QAA6B,EAAS,OAAwB;QAC9K,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,IAAA,oCAAmB,EAAC,SAAS,EAAE,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAA;QACzE,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,eAAO,CAAC,iCAAiC,EAAE,EAAE,EAAE,iFAAiF,CAAC,CAAA;QACvJ,OAAO,KAAK,CAAA;IACd,CAAC;IAKK,AAAN,KAAK,CAAC,oBAAoB,CAAY,EAAU,EAAS,OAAwB;QAC/E,OAAO,MAAM,IAAA,qCAAoB,EAAC,EAAE,EAAE,OAAO,CAAC,CAAA;IAChD,CAAC;IAKK,AAAN,KAAK,CAAC,kBAAkB,CAAY,EAAU,EAAS,OAAwB;QAC7E,OAAO,MAAM,IAAA,mCAAkB,EAAC,EAAE,EAAE,OAAO,CAAC,CAAA;IAC9C,CAAC;IAKK,AAAN,KAAK,CAAC,mBAAmB,CAAY,EAAU,EAAS,OAAwB;QAC9E,OAAO,MAAM,IAAA,oCAAmB,EAAC,EAAE,EAAE,OAAO,CAAC,CAAA;IAC/C,CAAC;CACF,CAAA;AArFY,wCAAc;AAInB;IAHL,IAAA,wBAAS,EAAC,iFAAiF,CAAC;IAC5F,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,eAAM,EAAE,EAAE,WAAW,EAAE,mBAAmB,EAAE,CAAC;IAC9C,mBAAA,IAAA,kBAAG,EAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC,uBAAS,CAAC,CAAA;IAAqB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;6CAAjB,uBAAS;;kDAErE;AAKK;IAHL,IAAA,wBAAS,EAAC,iFAAiF,CAAC;IAC5F,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,eAAM,EAAE,EAAE,WAAW,EAAE,8DAA8D,EAAE,CAAC;IACzF,mBAAA,IAAA,kBAAG,EAAC,IAAI,CAAC,CAAA;IAAc,mBAAA,IAAA,kBAAG,EAAC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC,yBAAW,CAAC,CAAA;IAAsB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;qDAAnB,yBAAW;;kDAE9F;AAKK;IAHL,IAAA,wBAAS,EAAC,iFAAiF,CAAC;IAC5F,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,8BAAa,EAAE,EAAE,WAAW,EAAE,4FAA4F,EAAE,CAAC;IACvH,mBAAA,IAAA,kBAAG,EAAC,UAAU,CAAC,CAAA;IAAoB,mBAAA,IAAA,kBAAG,EAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;IAAkB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;yDAErH;AAKK;IAHL,IAAA,wBAAS,EAAC,iFAAiF,CAAC;IAC5F,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,8BAAa,EAAE,EAAE,WAAW,EAAE,sEAAsE,EAAE,CAAC;IACjG,mBAAA,IAAA,kBAAG,EAAC,IAAI,CAAC,CAAA;IAAc,mBAAA,IAAA,kBAAG,EAAC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC,gCAAkB,CAAC,CAAA;IAA6B,mBAAA,IAAA,kBAAG,GAAE,CAAA;;qDAA1B,gCAAkB;;yDAEnH;AAKK;IAHL,IAAA,wBAAS,EAAC,iFAAiF,CAAC;IAC5F,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,CAAC,wBAAU,CAAC,EAAE,EAAE,WAAW,EAAE,iFAAiF,EAAE,CAAC;IAC/G,mBAAA,IAAA,kBAAG,EAAC,WAAW,CAAC,CAAA;IAAqB,mBAAA,IAAA,kBAAG,EAAC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,6BAAe,CAAC,CAAC,CAAA;IAA4B,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;qDAEnI;AAKK;IAHL,IAAA,wBAAS,EAAC,iFAAiF,CAAC;IAC5F,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,wBAAU,EAAE,EAAE,WAAW,EAAE,wHAAwH,EAAE,CAAC;IAExK,mBAAA,IAAA,kBAAG,EAAC,QAAQ,CAAC,CAAA;IACb,mBAAA,IAAA,kBAAG,EAAC,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC,0BAAa,CAAC,CAAA;IAClC,mBAAA,IAAA,kBAAG,EAAC,iBAAiB,EAAE,IAAI,CAAC,EAAE,CAAC,kBAAG,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,kDAAkD,EAAE,CAAC,CAAA;IACxH,mBAAA,IAAA,kBAAG,EAAC,WAAW,EAAE,IAAI,CAAC,EAAE,CAAC,kBAAG,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,yCAAyC,EAAE,CAAC,CAAA;IACzG,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;0DAGP;AAKK;IAHL,IAAA,wBAAS,EAAC,iFAAiF,CAAC;IAC5F,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,CAAC,gCAAc,CAAC,EAAE,EAAE,WAAW,EAAE,qFAAqF,EAAE,CAAC;IACnH,mBAAA,IAAA,kBAAG,EAAC,WAAW,CAAC,CAAA;IAAqB,mBAAA,IAAA,kBAAG,EAAC,WAAW,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,iCAAmB,CAAC,CAAC,CAAA;IAAoC,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;yDAEvJ;AAKK;IAHL,IAAA,wBAAS,EAAC,iFAAiF,CAAC;IAC5F,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,gCAAc,EAAE,EAAE,WAAW,EAAE,iIAAiI,EAAE,CAAC;IACxJ,mBAAA,IAAA,kBAAG,EAAC,WAAW,CAAC,CAAA;IAAqB,mBAAA,IAAA,kBAAG,EAAC,UAAU,EAAE,IAAI,CAAC,EAAE,CAAC,iCAAmB,CAAC,CAAA;IAAiC,mBAAA,IAAA,kBAAG,GAAE,CAAA;;qDAA3B,iCAAmB;;8DAI9I;AAKK;IAHL,IAAA,wBAAS,EAAC,iFAAiF,CAAC;IAC5F,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,8BAAa,EAAE,EAAE,WAAW,EAAE,sEAAsE,EAAE,CAAC;IAChG,mBAAA,IAAA,kBAAG,EAAC,IAAI,CAAC,CAAA;IAAc,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;0DAEvD;AAKK;IAHL,IAAA,wBAAS,EAAC,iFAAiF,CAAC;IAC5F,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,8BAAa,EAAE,EAAE,WAAW,EAAE,6DAA6D,EAAE,CAAC;IACzF,mBAAA,IAAA,kBAAG,EAAC,IAAI,CAAC,CAAA;IAAc,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;wDAErD;AAKK;IAHL,IAAA,wBAAS,EAAC,iFAAiF,CAAC;IAC5F,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,4BAA4B,EAAE,CAAC;IACjD,mBAAA,IAAA,kBAAG,EAAC,IAAI,CAAC,CAAA;IAAc,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;yDAEtD;yBApFU,cAAc;IAF1B,IAAA,uBAAQ,EAAC,eAAM,CAAC;IAChB,IAAA,4BAAa,EAAC,qBAAa,CAAC;GAChB,cAAc,CAqF1B","sourcesContent":["import type { FileUpload } from 'graphql-upload/GraphQLUpload.js'\nimport GraphQLUpload from 'graphql-upload/GraphQLUpload.js'\nimport { Arg, Ctx, Directive, Int, Mutation, Resolver, UseMiddleware } from 'type-graphql'\n\nimport { Refusal, SpeakRefusals } from '@things-factory/shell'\n\nimport { Course } from './course'\nimport { CourseQuestion } from './course-question'\nimport { CourseUnit } from './course-unit'\nimport { CourseVersion } from './course-version'\nimport { CoursePatch, CourseQuestionInput, CourseUnitInput, CourseVersionPatch, NewCourse } from './course-type'\nimport {\n addDraftedQuestions,\n attachCourseUnitFile,\n closeCourseVersion,\n createCourse,\n createCourseVersion,\n deleteCourseVersion,\n publishCourseVersion,\n saveCourseQuestions,\n saveCourseUnits,\n updateCourse,\n updateCourseVersion\n} from './course-service'\n\n\n/* The managers' writes of courses (ADR-0194 decisions 2-4) - `course` mutation, the task `learning.managing`. */\n@Resolver(Course)\n@UseMiddleware(SpeakRefusals)\nexport class CourseMutation {\n @Directive('@privilege(category: \"course\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => Course, { description: 'To make a course.' })\n async createCourse(@Arg('course', type => NewCourse) course: NewCourse, @Ctx() context: ResolverContext): Promise<Course> {\n return await createCourse(course, context)\n }\n\n @Directive('@privilege(category: \"course\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => Course, { description: \"To change a course's name, description, category or manager.\" })\n async updateCourse(@Arg('id') id: string, @Arg('patch', type => CoursePatch) patch: CoursePatch, @Ctx() context: ResolverContext): Promise<Course> {\n return await updateCourse(id, patch, context)\n }\n\n @Directive('@privilege(category: \"course\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => CourseVersion, { description: 'To make the next draft version of a course - a copy of `fromId`, or of the latest version.' })\n async createCourseVersion(@Arg('courseId') courseId: string, @Arg('fromId', { nullable: true }) fromId: string, @Ctx() context: ResolverContext): Promise<CourseVersion> {\n return await createCourseVersion(courseId, fromId, context)\n }\n\n @Directive('@privilege(category: \"course\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => CourseVersion, { description: 'To change what a draft version says - a published one never changes.' })\n async updateCourseVersion(@Arg('id') id: string, @Arg('patch', type => CourseVersionPatch) patch: CourseVersionPatch, @Ctx() context: ResolverContext): Promise<CourseVersion> {\n return await updateCourseVersion(id, patch, context)\n }\n\n @Directive('@privilege(category: \"course\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => [CourseUnit], { description: 'To save the units of a draft version, in order - the ones left out are deleted.' })\n async saveCourseUnits(@Arg('versionId') versionId: string, @Arg('units', type => [CourseUnitInput]) units: CourseUnitInput[], @Ctx() context: ResolverContext): Promise<CourseUnit[]> {\n return await saveCourseUnits(versionId, units, context)\n }\n\n @Directive('@privilege(category: \"course\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => CourseUnit, { description: \"To attach a video's or a PDF's file to a unit of a draft version - a video with its length, a PDF with its page count.\" })\n async attachCourseUnitFile(\n @Arg('unitId') unitId: string,\n @Arg('file', type => GraphQLUpload) file: FileUpload,\n @Arg('durationSeconds', type => Int, { nullable: true, description: \"A video's length in seconds, read from the file.\" }) durationSeconds: number,\n @Arg('pageCount', type => Int, { nullable: true, description: \"A PDF's page count, read from the file.\" }) pageCount: number,\n @Ctx() context: ResolverContext\n ): Promise<CourseUnit> {\n return await attachCourseUnitFile(unitId, file, { durationSeconds, pageCount }, context)\n }\n\n @Directive('@privilege(category: \"course\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => [CourseQuestion], { description: 'To save the questions of a draft version, in order - the ones left out are deleted.' })\n async saveCourseQuestions(@Arg('versionId') versionId: string, @Arg('questions', type => [CourseQuestionInput]) questions: CourseQuestionInput[], @Ctx() context: ResolverContext): Promise<CourseQuestion[]> {\n return await saveCourseQuestions(versionId, questions, context)\n }\n\n @Directive('@privilege(category: \"course\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => CourseQuestion, { description: 'To add a question drafted by the assistant to a draft version - marked drafted until a manager saves it (ADR-0194 decision 12).' })\n async addDraftedCourseQuestion(@Arg('versionId') versionId: string, @Arg('question', type => CourseQuestionInput) question: CourseQuestionInput, @Ctx() context: ResolverContext): Promise<CourseQuestion> {\n const [saved] = await addDraftedQuestions(versionId, [question], context)\n if (!saved) throw new Refusal('learning-question-draft-invalid', {}, 'the drafted question is not complete - its text, two choices and the right ones')\n return saved\n }\n\n @Directive('@privilege(category: \"course\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => CourseVersion, { description: 'To publish a draft version - the version published before is closed.' })\n async publishCourseVersion(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<CourseVersion> {\n return await publishCourseVersion(id, context)\n }\n\n @Directive('@privilege(category: \"course\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => CourseVersion, { description: 'To close the published version - nobody starts it any more.' })\n async closeCourseVersion(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<CourseVersion> {\n return await closeCourseVersion(id, context)\n }\n\n @Directive('@privilege(category: \"course\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => Boolean, { description: 'To delete a draft version.' })\n async deleteCourseVersion(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<boolean> {\n return await deleteCourseVersion(id, context)\n }\n}\n"]}
|
|
@@ -16,9 +16,11 @@ export declare class CourseQuery {
|
|
|
16
16
|
}
|
|
17
17
|
export declare class CourseVersionFields {
|
|
18
18
|
units(version: CourseVersion, context: ResolverContext): Promise<CourseUnit[]>;
|
|
19
|
+
expectedSeconds(version: CourseVersion, context: ResolverContext): Promise<number | null>;
|
|
19
20
|
questions(version: CourseVersion, context: ResolverContext): Promise<CourseQuestion[]>;
|
|
20
21
|
}
|
|
21
22
|
export declare class CourseUnitFields {
|
|
23
|
+
secondsPerPage(unit: CourseUnit): number | null;
|
|
22
24
|
file(unit: CourseUnit, context: ResolverContext): Promise<Attachment | null>;
|
|
23
25
|
}
|
|
24
26
|
export declare class CourseQuestionFields {
|
|
@@ -6,6 +6,7 @@ const type_graphql_1 = require("type-graphql");
|
|
|
6
6
|
const attachment_base_1 = require("@things-factory/attachment-base");
|
|
7
7
|
const auth_base_1 = require("@things-factory/auth-base");
|
|
8
8
|
const shell_1 = require("@things-factory/shell");
|
|
9
|
+
const progress_rules_1 = require("../enrollment/progress-rules");
|
|
9
10
|
const course_1 = require("./course");
|
|
10
11
|
const course_question_1 = require("./course-question");
|
|
11
12
|
const course_unit_1 = require("./course-unit");
|
|
@@ -32,7 +33,7 @@ let CourseQuery = class CourseQuery {
|
|
|
32
33
|
return await (0, shell_1.getRepository)(course_version_1.CourseVersion).findOne({ where: { id, domain: { id: context.state.domain.id } } });
|
|
33
34
|
}
|
|
34
35
|
async versions(course, context) {
|
|
35
|
-
if (!(await (0,
|
|
36
|
+
if (!(await (0, auth_base_1.requestHolds)(context, 'course', 'query')))
|
|
36
37
|
return [];
|
|
37
38
|
return await (0, shell_1.getRepository)(course_version_1.CourseVersion).find({ where: { course: { id: course.id } }, order: { number: 'DESC' } });
|
|
38
39
|
}
|
|
@@ -104,8 +105,13 @@ let CourseVersionFields = class CourseVersionFields {
|
|
|
104
105
|
return [];
|
|
105
106
|
return await (0, course_service_1.unitsOf)((0, course_service_1.learningManagerOf)(context), version.id);
|
|
106
107
|
}
|
|
108
|
+
async expectedSeconds(version, context) {
|
|
109
|
+
if (!(await (0, course_service_1.mayReadVersion)(context, version)))
|
|
110
|
+
return null;
|
|
111
|
+
return (0, progress_rules_1.expectedSecondsOf)(await (0, course_service_1.unitsOf)((0, course_service_1.learningManagerOf)(context), version.id), version.minSeconds);
|
|
112
|
+
}
|
|
107
113
|
async questions(version, context) {
|
|
108
|
-
if (!(await (0,
|
|
114
|
+
if (!(await (0, auth_base_1.requestHolds)(context, 'course', 'query')))
|
|
109
115
|
return [];
|
|
110
116
|
return await (0, course_service_1.questionsOf)((0, course_service_1.learningManagerOf)(context), version.id);
|
|
111
117
|
}
|
|
@@ -119,6 +125,14 @@ tslib_1.__decorate([
|
|
|
119
125
|
tslib_1.__metadata("design:paramtypes", [course_version_1.CourseVersion, Object]),
|
|
120
126
|
tslib_1.__metadata("design:returntype", Promise)
|
|
121
127
|
], CourseVersionFields.prototype, "units", null);
|
|
128
|
+
tslib_1.__decorate([
|
|
129
|
+
(0, type_graphql_1.FieldResolver)(type => type_graphql_1.Int, { nullable: true, description: 'The seconds it takes at least - every unit done (a video its length, a PDF or text its least time) and its least time, the longer. Empty for whoever may not read it.' }),
|
|
130
|
+
tslib_1.__param(0, (0, type_graphql_1.Root)()),
|
|
131
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
132
|
+
tslib_1.__metadata("design:type", Function),
|
|
133
|
+
tslib_1.__metadata("design:paramtypes", [course_version_1.CourseVersion, Object]),
|
|
134
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
135
|
+
], CourseVersionFields.prototype, "expectedSeconds", null);
|
|
122
136
|
tslib_1.__decorate([
|
|
123
137
|
(0, type_graphql_1.FieldResolver)(type => [course_question_1.CourseQuestion], { description: 'Its questions, in order - for a manager; nothing for anyone else.' }),
|
|
124
138
|
tslib_1.__param(0, (0, type_graphql_1.Root)()),
|
|
@@ -131,6 +145,9 @@ exports.CourseVersionFields = CourseVersionFields = tslib_1.__decorate([
|
|
|
131
145
|
(0, type_graphql_1.Resolver)(course_version_1.CourseVersion)
|
|
132
146
|
], CourseVersionFields);
|
|
133
147
|
let CourseUnitFields = class CourseUnitFields {
|
|
148
|
+
secondsPerPage(unit) {
|
|
149
|
+
return unit.kind === 'PDF' ? (0, progress_rules_1.secondsPerPage)(unit) : null;
|
|
150
|
+
}
|
|
134
151
|
async file(unit, context) {
|
|
135
152
|
const version = await (0, course_service_1.learningManagerOf)(context).getRepository(course_version_1.CourseVersion).findOne({ where: { id: unit.versionId }, withDeleted: true });
|
|
136
153
|
if (!(await (0, course_service_1.mayReadVersion)(context, version)))
|
|
@@ -139,6 +156,13 @@ let CourseUnitFields = class CourseUnitFields {
|
|
|
139
156
|
}
|
|
140
157
|
};
|
|
141
158
|
exports.CourseUnitFields = CourseUnitFields;
|
|
159
|
+
tslib_1.__decorate([
|
|
160
|
+
(0, type_graphql_1.FieldResolver)(type => type_graphql_1.Int, { nullable: true, description: 'The fewest seconds a PDF page is looked at to count as read - the screen and the server read the same.' }),
|
|
161
|
+
tslib_1.__param(0, (0, type_graphql_1.Root)()),
|
|
162
|
+
tslib_1.__metadata("design:type", Function),
|
|
163
|
+
tslib_1.__metadata("design:paramtypes", [course_unit_1.CourseUnit]),
|
|
164
|
+
tslib_1.__metadata("design:returntype", Number)
|
|
165
|
+
], CourseUnitFields.prototype, "secondsPerPage", null);
|
|
142
166
|
tslib_1.__decorate([
|
|
143
167
|
(0, type_graphql_1.FieldResolver)(type => attachment_base_1.Attachment, { nullable: true, description: "A video's or a PDF's file." }),
|
|
144
168
|
tslib_1.__param(0, (0, type_graphql_1.Root)()),
|
|
@@ -152,7 +176,7 @@ exports.CourseUnitFields = CourseUnitFields = tslib_1.__decorate([
|
|
|
152
176
|
], CourseUnitFields);
|
|
153
177
|
let CourseQuestionFields = class CourseQuestionFields {
|
|
154
178
|
async choices(question, context) {
|
|
155
|
-
if (!(await (0,
|
|
179
|
+
if (!(await (0, auth_base_1.requestHolds)(context, 'course', 'query')))
|
|
156
180
|
return [];
|
|
157
181
|
return question.choices ?? [];
|
|
158
182
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"course-query.js","sourceRoot":"","sources":["../../../server/service/course/course-query.ts"],"names":[],"mappings":";;;;AAAA,+CAA6G;AAE7G,qEAA4D;AAC5D,yDAAgD;AAChD,iDAA8G;AAE9G,qCAAiC;AACjC,uDAAkD;AAClD,+CAA0C;AAC1C,qDAAgD;AAChD,+CAA2D;AAC3D,qDAAkI;AAGlI;;;GAGG;AAGI,IAAM,WAAW,GAAjB,MAAM,WAAW;IAGhB,AAAN,KAAK,CAAC,OAAO,CAA0B,MAAiB,EAAS,OAAwB;QACvF,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAChC,MAAM,EAAE,GAAG,IAAA,qCAA6B,EAAC,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,IAAA,qBAAa,EAAC,eAAM,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,UAAU,CAAC,EAAE,CAAC,CAAA;QAC7J,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM;YAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;QAC1D,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,eAAe,EAAE,CAAA;QACjD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAA;IACzB,CAAC;IAIK,AAAN,KAAK,CAAC,MAAM,CAAY,EAAU,EAAS,OAAwB;QACjE,OAAO,MAAM,IAAA,qBAAa,EAAC,eAAM,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,EAAS,EAAE,CAAC,CAAA;IAC/G,CAAC;IAIK,AAAN,KAAK,CAAC,aAAa,CAAY,EAAU,EAAS,OAAwB;QACxE,OAAO,MAAM,IAAA,qBAAa,EAAC,8BAAa,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,EAAS,EAAE,CAAC,CAAA;IACtH,CAAC;IAGK,AAAN,KAAK,CAAC,QAAQ,CAAS,MAAc,EAAS,OAAwB;QACpE,IAAI,CAAC,CAAC,MAAM,IAAA,sBAAK,EAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;YAAE,OAAO,EAAE,CAAA;QACzD,OAAO,MAAM,IAAA,qBAAa,EAAC,8BAAa,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAS,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,EAAS,EAAE,CAAC,CAAA;IACnI,CAAC;IAGK,AAAN,KAAK,CAAC,gBAAgB,CAAS,MAAc,EAAS,OAAwB;QAC5E,OAAO,MAAM,IAAA,mCAAkB,EAAC,IAAA,kCAAiB,EAAC,OAAO,CAAC,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,QAAS,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,CAAA;IAClG,CAAC;IAGK,AAAN,KAAK,CAAC,OAAO,CAAS,MAAc;QAClC,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,IAAA,qBAAa,EAAC,gBAAI,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAChG,CAAC;CACF,CAAA;AAtCY,kCAAW;AAGhB;IAFL,IAAA,wBAAS,EAAC,8EAA8E,CAAC;IACzF,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,wBAAU,EAAE,EAAE,WAAW,EAAE,4BAA4B,EAAE,CAAC;IAC7D,mBAAA,IAAA,mBAAI,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAS,CAAC,CAAA;IAAqB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;6CAAjB,iBAAS;;0CAMvD;AAIK;IAFL,IAAA,wBAAS,EAAC,8EAA8E,CAAC;IACzF,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,eAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,aAAa,EAAE,CAAC;IAC3D,mBAAA,IAAA,kBAAG,EAAC,IAAI,CAAC,CAAA;IAAc,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;yCAEzC;AAIK;IAFL,IAAA,wBAAS,EAAC,8EAA8E,CAAC;IACzF,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,8BAAa,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,wDAAwD,EAAE,CAAC;IACtG,mBAAA,IAAA,kBAAG,EAAC,IAAI,CAAC,CAAA;IAAc,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;gDAEhD;AAGK;IADL,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,8BAAa,CAAC,EAAE,EAAE,WAAW,EAAE,0EAA0E,EAAE,CAAC;IACpH,mBAAA,IAAA,mBAAI,GAAE,CAAA;IAAkB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;6CAAd,eAAM;;2CAGpC;AAGK;IADL,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,8BAAa,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,gCAAgC,EAAE,CAAC;IAChF,mBAAA,IAAA,mBAAI,GAAE,CAAA;IAAkB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;6CAAd,eAAM;;mDAE5C;AAGK;IADL,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,gBAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/D,mBAAA,IAAA,mBAAI,GAAE,CAAA;;6CAAS,eAAM;;0CAEnC;sBArCU,WAAW;IAFvB,IAAA,uBAAQ,EAAC,eAAM,CAAC;IAChB,IAAA,4BAAa,EAAC,qBAAa,CAAC;GAChB,WAAW,CAsCvB;AAGM,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;IAExB,AAAN,KAAK,CAAC,KAAK,CAAS,OAAsB,EAAS,OAAwB;QACzE,IAAI,CAAC,CAAC,MAAM,IAAA,+BAAc,EAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAAE,OAAO,EAAE,CAAA;QACxD,OAAO,MAAM,IAAA,wBAAO,EAAC,IAAA,kCAAiB,EAAC,OAAO,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC,CAAA;IAC9D,CAAC;IAGK,AAAN,KAAK,CAAC,SAAS,CAAS,OAAsB,EAAS,OAAwB;QAC7E,IAAI,CAAC,CAAC,MAAM,IAAA,sBAAK,EAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;YAAE,OAAO,EAAE,CAAA;QACzD,OAAO,MAAM,IAAA,4BAAW,EAAC,IAAA,kCAAiB,EAAC,OAAO,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC,CAAA;IAClE,CAAC;CACF,CAAA;AAZY,kDAAmB;AAExB;IADL,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,wBAAU,CAAC,EAAE,EAAE,WAAW,EAAE,uFAAuF,EAAE,CAAC;IACjI,mBAAA,IAAA,mBAAI,GAAE,CAAA;IAA0B,mBAAA,IAAA,kBAAG,GAAE,CAAA;;6CAArB,8BAAa;;gDAGzC;AAGK;IADL,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,gCAAc,CAAC,EAAE,EAAE,WAAW,EAAE,mEAAmE,EAAE,CAAC;IAC7G,mBAAA,IAAA,mBAAI,GAAE,CAAA;IAA0B,mBAAA,IAAA,kBAAG,GAAE,CAAA;;6CAArB,8BAAa;;oDAG7C;8BAXU,mBAAmB;IAD/B,IAAA,uBAAQ,EAAC,8BAAa,CAAC;GACX,mBAAmB,CAY/B;AAGM,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;IAErB,AAAN,KAAK,CAAC,IAAI,CAAS,IAAgB,EAAS,OAAwB;QAClE,MAAM,OAAO,GAAG,MAAM,IAAA,kCAAiB,EAAC,OAAO,CAAC,CAAC,aAAa,CAAC,8BAAa,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,SAAS,EAAS,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAA;QAClJ,IAAI,CAAC,CAAC,MAAM,IAAA,+BAAc,EAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAAE,OAAO,IAAI,CAAA;QAC1D,OAAO,CAAC,MAAM,IAAA,4BAAW,EAAC,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,IAAI,CAAA;IAClF,CAAC;CACF,CAAA;AAPY,4CAAgB;AAErB;IADL,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,4BAAU,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,4BAA4B,EAAE,CAAC;IACrF,mBAAA,IAAA,mBAAI,GAAE,CAAA;IAAoB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;6CAAlB,wBAAU;;4CAIlC;2BANU,gBAAgB;IAD5B,IAAA,uBAAQ,EAAC,wBAAU,CAAC;GACR,gBAAgB,CAO5B;AAGM,IAAM,oBAAoB,GAA1B,MAAM,oBAAoB;IAEzB,AAAN,KAAK,CAAC,OAAO,CAAS,QAAwB,EAAS,OAAwB;QAC7E,IAAI,CAAC,CAAC,MAAM,IAAA,sBAAK,EAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;YAAE,OAAO,EAAE,CAAA;QACzD,OAAO,QAAQ,CAAC,OAAO,IAAI,EAAE,CAAA;IAC/B,CAAC;CACF,CAAA;AANY,oDAAoB;AAEzB;IADL,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,6BAAe,CAAC,EAAE,EAAE,WAAW,EAAE,wDAAwD,EAAE,CAAC;IACrG,mBAAA,IAAA,mBAAI,GAAE,CAAA;IAA4B,mBAAA,IAAA,kBAAG,GAAE,CAAA;;6CAAtB,gCAAc;;mDAG7C;+BALU,oBAAoB;IADhC,IAAA,uBAAQ,EAAC,gCAAc,CAAC;GACZ,oBAAoB,CAMhC","sourcesContent":["import { Arg, Args, Ctx, Directive, FieldResolver, Query, Resolver, Root, UseMiddleware } from 'type-graphql'\n\nimport { Attachment } from '@things-factory/attachment-base'\nimport { User } from '@things-factory/auth-base'\nimport { getQueryBuilderFromListParams, getRepository, ListParam, SpeakRefusals } from '@things-factory/shell'\n\nimport { Course } from './course'\nimport { CourseQuestion } from './course-question'\nimport { CourseUnit } from './course-unit'\nimport { CourseVersion } from './course-version'\nimport { CourseChoiceKey, CourseList } from './course-type'\nimport { holds, learningManagerOf, mayReadVersion, publishedVersionOf, questionsOf, unitFilesOf, unitsOf } from './course-service'\n\n\n/*\n * The managers' reads of courses (ADR-0194 decisions 2-4, 10, 11) - `course` query, the task `learning.managing`. A learner\n * reads a course through their enrolment (enrollment-query.ts), never these.\n */\n@Resolver(Course)\n@UseMiddleware(SpeakRefusals)\nexport class CourseQuery {\n @Directive('@privilege(category: \"course\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => CourseList, { description: 'The courses of the domain.' })\n async courses(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<CourseList> {\n const { domain } = context.state\n const qb = getQueryBuilderFromListParams({ domain, params, repository: getRepository(Course), alias: 'C', searchables: ['name', 'description', 'category'] })\n if (!params?.sortings?.length) qb.orderBy('C.name', 'ASC')\n const [items, total] = await qb.getManyAndCount()\n return { items, total }\n }\n\n @Directive('@privilege(category: \"course\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => Course, { nullable: true, description: 'One course.' })\n async course(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<Course | null> {\n return await getRepository(Course).findOne({ where: { id, domain: { id: context.state.domain.id } } as any })\n }\n\n @Directive('@privilege(category: \"course\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => CourseVersion, { nullable: true, description: 'One version of a course, with its units and questions.' })\n async courseVersion(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<CourseVersion | null> {\n return await getRepository(CourseVersion).findOne({ where: { id, domain: { id: context.state.domain.id } } as any })\n }\n\n @FieldResolver(type => [CourseVersion], { description: 'Its versions, the newest first - for a manager; nothing for anyone else.' })\n async versions(@Root() course: Course, @Ctx() context: ResolverContext): Promise<CourseVersion[]> {\n if (!(await holds(context, 'course', 'query'))) return []\n return await getRepository(CourseVersion).find({ where: { course: { id: course.id } } as any, order: { number: 'DESC' } as any })\n }\n\n @FieldResolver(type => CourseVersion, { nullable: true, description: 'The version learners take now.' })\n async publishedVersion(@Root() course: Course, @Ctx() context: ResolverContext): Promise<CourseVersion | null> {\n return await publishedVersionOf(learningManagerOf(context), { id: course.domainId! }, course.id)\n }\n\n @FieldResolver(type => User, { nullable: true, description: 'Who keeps it.' })\n async manager(@Root() course: Course): Promise<User | null> {\n return course.managerId ? await getRepository(User).findOneBy({ id: course.managerId }) : null\n }\n}\n\n@Resolver(CourseVersion)\nexport class CourseVersionFields {\n @FieldResolver(type => [CourseUnit], { description: 'Its units, in order - for a manager, or a learner whose enrolment shows this version.' })\n async units(@Root() version: CourseVersion, @Ctx() context: ResolverContext): Promise<CourseUnit[]> {\n if (!(await mayReadVersion(context, version))) return []\n return await unitsOf(learningManagerOf(context), version.id)\n }\n\n @FieldResolver(type => [CourseQuestion], { description: 'Its questions, in order - for a manager; nothing for anyone else.' })\n async questions(@Root() version: CourseVersion, @Ctx() context: ResolverContext): Promise<CourseQuestion[]> {\n if (!(await holds(context, 'course', 'query'))) return []\n return await questionsOf(learningManagerOf(context), version.id)\n }\n}\n\n@Resolver(CourseUnit)\nexport class CourseUnitFields {\n @FieldResolver(type => Attachment, { nullable: true, description: \"A video's or a PDF's file.\" })\n async file(@Root() unit: CourseUnit, @Ctx() context: ResolverContext): Promise<Attachment | null> {\n const version = await learningManagerOf(context).getRepository(CourseVersion).findOne({ where: { id: unit.versionId } as any, withDeleted: true })\n if (!(await mayReadVersion(context, version))) return null\n return (await unitFilesOf(context.state.domain, [unit.id])).get(unit.id) ?? null\n }\n}\n\n@Resolver(CourseQuestion)\nexport class CourseQuestionFields {\n @FieldResolver(type => [CourseChoiceKey], { description: 'Its choices with which are right - for a manager only.' })\n async choices(@Root() question: CourseQuestion, @Ctx() context: ResolverContext): Promise<CourseChoiceKey[]> {\n if (!(await holds(context, 'course', 'query'))) return []\n return question.choices ?? []\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"course-query.js","sourceRoot":"","sources":["../../../server/service/course/course-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 { Attachment } from '@things-factory/attachment-base'\nimport { requestHolds, User } from '@things-factory/auth-base'\nimport { getQueryBuilderFromListParams, getRepository, ListParam, SpeakRefusals } from '@things-factory/shell'\n\nimport { expectedSecondsOf, secondsPerPage } from '../enrollment/progress-rules'\nimport { Course } from './course'\nimport { CourseQuestion } from './course-question'\nimport { CourseUnit } from './course-unit'\nimport { CourseVersion } from './course-version'\nimport { CourseChoiceKey, CourseList } from './course-type'\nimport { learningManagerOf, mayReadVersion, publishedVersionOf, questionsOf, unitFilesOf, unitsOf } from './course-service'\n\n\n/*\n * The managers' reads of courses (ADR-0194 decisions 2-4, 10, 11) - `course` query, the task `learning.managing`. A learner\n * reads a course through their enrolment (enrollment-query.ts), never these.\n */\n@Resolver(Course)\n@UseMiddleware(SpeakRefusals)\nexport class CourseQuery {\n @Directive('@privilege(category: \"course\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => CourseList, { description: 'The courses of the domain.' })\n async courses(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<CourseList> {\n const { domain } = context.state\n const qb = getQueryBuilderFromListParams({ domain, params, repository: getRepository(Course), alias: 'C', searchables: ['name', 'description', 'category'] })\n if (!params?.sortings?.length) qb.orderBy('C.name', 'ASC')\n const [items, total] = await qb.getManyAndCount()\n return { items, total }\n }\n\n @Directive('@privilege(category: \"course\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => Course, { nullable: true, description: 'One course.' })\n async course(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<Course | null> {\n return await getRepository(Course).findOne({ where: { id, domain: { id: context.state.domain.id } } as any })\n }\n\n @Directive('@privilege(category: \"course\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => CourseVersion, { nullable: true, description: 'One version of a course, with its units and questions.' })\n async courseVersion(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<CourseVersion | null> {\n return await getRepository(CourseVersion).findOne({ where: { id, domain: { id: context.state.domain.id } } as any })\n }\n\n @FieldResolver(type => [CourseVersion], { description: 'Its versions, the newest first - for a manager; nothing for anyone else.' })\n async versions(@Root() course: Course, @Ctx() context: ResolverContext): Promise<CourseVersion[]> {\n if (!(await requestHolds(context, 'course', 'query'))) return []\n return await getRepository(CourseVersion).find({ where: { course: { id: course.id } } as any, order: { number: 'DESC' } as any })\n }\n\n @FieldResolver(type => CourseVersion, { nullable: true, description: 'The version learners take now.' })\n async publishedVersion(@Root() course: Course, @Ctx() context: ResolverContext): Promise<CourseVersion | null> {\n return await publishedVersionOf(learningManagerOf(context), { id: course.domainId! }, course.id)\n }\n\n @FieldResolver(type => User, { nullable: true, description: 'Who keeps it.' })\n async manager(@Root() course: Course): Promise<User | null> {\n return course.managerId ? await getRepository(User).findOneBy({ id: course.managerId }) : null\n }\n}\n\n@Resolver(CourseVersion)\nexport class CourseVersionFields {\n @FieldResolver(type => [CourseUnit], { description: 'Its units, in order - for a manager, or a learner whose enrolment shows this version.' })\n async units(@Root() version: CourseVersion, @Ctx() context: ResolverContext): Promise<CourseUnit[]> {\n if (!(await mayReadVersion(context, version))) return []\n return await unitsOf(learningManagerOf(context), version.id)\n }\n\n @FieldResolver(type => Int, { nullable: true, description: 'The seconds it takes at least - every unit done (a video its length, a PDF or text its least time) and its least time, the longer. Empty for whoever may not read it.' })\n async expectedSeconds(@Root() version: CourseVersion, @Ctx() context: ResolverContext): Promise<number | null> {\n if (!(await mayReadVersion(context, version))) return null\n return expectedSecondsOf(await unitsOf(learningManagerOf(context), version.id), version.minSeconds)\n }\n\n @FieldResolver(type => [CourseQuestion], { description: 'Its questions, in order - for a manager; nothing for anyone else.' })\n async questions(@Root() version: CourseVersion, @Ctx() context: ResolverContext): Promise<CourseQuestion[]> {\n if (!(await requestHolds(context, 'course', 'query'))) return []\n return await questionsOf(learningManagerOf(context), version.id)\n }\n}\n\n@Resolver(CourseUnit)\nexport class CourseUnitFields {\n @FieldResolver(type => Int, { nullable: true, description: 'The fewest seconds a PDF page is looked at to count as read - the screen and the server read the same.' })\n secondsPerPage(@Root() unit: CourseUnit): number | null {\n return unit.kind === 'PDF' ? secondsPerPage(unit) : null\n }\n\n @FieldResolver(type => Attachment, { nullable: true, description: \"A video's or a PDF's file.\" })\n async file(@Root() unit: CourseUnit, @Ctx() context: ResolverContext): Promise<Attachment | null> {\n const version = await learningManagerOf(context).getRepository(CourseVersion).findOne({ where: { id: unit.versionId } as any, withDeleted: true })\n if (!(await mayReadVersion(context, version))) return null\n return (await unitFilesOf(context.state.domain, [unit.id])).get(unit.id) ?? null\n }\n}\n\n@Resolver(CourseQuestion)\nexport class CourseQuestionFields {\n @FieldResolver(type => [CourseChoiceKey], { description: 'Its choices with which are right - for a manager only.' })\n async choices(@Root() question: CourseQuestion, @Ctx() context: ResolverContext): Promise<CourseChoiceKey[]> {\n if (!(await requestHolds(context, 'course', 'query'))) return []\n return question.choices ?? []\n }\n}\n"]}
|
|
@@ -17,6 +17,7 @@ export interface UnitInput {
|
|
|
17
17
|
body?: string | null;
|
|
18
18
|
url?: string | null;
|
|
19
19
|
durationSeconds?: number | null;
|
|
20
|
+
pageCount?: number | null;
|
|
20
21
|
minSeconds?: number | null;
|
|
21
22
|
}
|
|
22
23
|
/** Why a unit may not be kept as given. A video's or a PDF's file is attached apart - publishing asks for it. */
|
|
@@ -36,6 +37,7 @@ export interface PublishFacts {
|
|
|
36
37
|
units: {
|
|
37
38
|
kind: string;
|
|
38
39
|
durationSeconds?: number | null;
|
|
40
|
+
pageCount?: number | null;
|
|
39
41
|
hasFile: boolean;
|
|
40
42
|
}[];
|
|
41
43
|
questions: {
|
|
@@ -44,6 +46,6 @@ export interface PublishFacts {
|
|
|
44
46
|
}
|
|
45
47
|
/**
|
|
46
48
|
* Why a draft may not be published (decisions 2-4): a unit or more; a video or a PDF with its file, a video with its
|
|
47
|
-
* length; a quiz with questions - as many as it draws, none of them a draft the assistant made that no manager looked over.
|
|
49
|
+
* length, a PDF with its page count; a quiz with questions - as many as it draws, none of them a draft the assistant made that no manager looked over.
|
|
48
50
|
*/
|
|
49
51
|
export declare function publishRefusal({ version, units, questions }: PublishFacts): string | null;
|