@falkenslab/moodle-driver 0.4.1 → 0.5.0
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/README.md +3 -2
- package/dist/areas/activities/build.d.ts +47 -10
- package/dist/areas/activities/build.js +157 -24
- package/dist/areas/activities/build.js.map +1 -1
- package/dist/areas/activities/conditions.d.ts +77 -0
- package/dist/areas/activities/conditions.js +216 -0
- package/dist/areas/activities/conditions.js.map +1 -0
- package/dist/areas/activities/content.d.ts +3 -0
- package/dist/areas/activities/content.js +1 -0
- package/dist/areas/activities/content.js.map +1 -1
- package/dist/areas/assignments/grader.d.ts +32 -0
- package/dist/areas/assignments/grader.js +66 -2
- package/dist/areas/assignments/grader.js.map +1 -1
- package/dist/areas/assignments/grading.d.ts +69 -6
- package/dist/areas/assignments/grading.js +173 -13
- package/dist/areas/assignments/grading.js.map +1 -1
- package/dist/areas/assignments/student.d.ts +14 -0
- package/dist/areas/assignments/student.js +43 -2
- package/dist/areas/assignments/student.js.map +1 -1
- package/dist/areas/backup/backup.d.ts +86 -0
- package/dist/areas/backup/backup.js +368 -0
- package/dist/areas/backup/backup.js.map +1 -0
- package/dist/areas/calendar/calendar.d.ts +27 -0
- package/dist/areas/calendar/calendar.js +115 -9
- package/dist/areas/calendar/calendar.js.map +1 -1
- package/dist/areas/completion/completion.d.ts +3 -0
- package/dist/areas/completion/completion.js.map +1 -1
- package/dist/areas/courses/list.d.ts +2 -0
- package/dist/areas/courses/list.js.map +1 -1
- package/dist/areas/courses/reset.d.ts +51 -0
- package/dist/areas/courses/reset.js +123 -0
- package/dist/areas/courses/reset.js.map +1 -0
- package/dist/areas/courses/state.d.ts +17 -0
- package/dist/areas/courses/state.js +37 -6
- package/dist/areas/courses/state.js.map +1 -1
- package/dist/areas/courses/structure.d.ts +14 -2
- package/dist/areas/courses/structure.js +11 -5
- package/dist/areas/courses/structure.js.map +1 -1
- package/dist/areas/forums/discussions.d.ts +8 -0
- package/dist/areas/forums/discussions.js +10 -0
- package/dist/areas/forums/discussions.js.map +1 -1
- package/dist/areas/forums/grading.d.ts +49 -0
- package/dist/areas/forums/grading.js +125 -0
- package/dist/areas/forums/grading.js.map +1 -0
- package/dist/areas/forums/posts.d.ts +37 -0
- package/dist/areas/forums/posts.js +141 -6
- package/dist/areas/forums/posts.js.map +1 -1
- package/dist/areas/grades/export.d.ts +38 -0
- package/dist/areas/grades/export.js +88 -0
- package/dist/areas/grades/export.js.map +1 -0
- package/dist/areas/grades/gradebook.d.ts +50 -0
- package/dist/areas/grades/gradebook.js +120 -0
- package/dist/areas/grades/gradebook.js.map +1 -1
- package/dist/areas/grades/singleview.d.ts +39 -0
- package/dist/areas/grades/singleview.js +186 -0
- package/dist/areas/grades/singleview.js.map +1 -0
- package/dist/areas/h5p/h5p.d.ts +2 -0
- package/dist/areas/h5p/h5p.js +12 -7
- package/dist/areas/h5p/h5p.js.map +1 -1
- package/dist/areas/lessons/lessons.d.ts +1 -0
- package/dist/areas/lessons/lessons.js.map +1 -1
- package/dist/areas/messages/messages.d.ts +32 -0
- package/dist/areas/messages/messages.js +44 -2
- package/dist/areas/messages/messages.js.map +1 -1
- package/dist/areas/participants/enrolments.d.ts +60 -0
- package/dist/areas/participants/enrolments.js +218 -0
- package/dist/areas/participants/enrolments.js.map +1 -0
- package/dist/areas/participants/groups.d.ts +75 -0
- package/dist/areas/participants/groups.js +201 -0
- package/dist/areas/participants/groups.js.map +1 -0
- package/dist/areas/participants/methods.d.ts +56 -0
- package/dist/areas/participants/methods.js +154 -0
- package/dist/areas/participants/methods.js.map +1 -0
- package/dist/areas/participants/participants.d.ts +11 -0
- package/dist/areas/participants/participants.js +24 -19
- package/dist/areas/participants/participants.js.map +1 -1
- package/dist/areas/questions/banks.d.ts +36 -1
- package/dist/areas/questions/banks.js +140 -17
- package/dist/areas/questions/banks.js.map +1 -1
- package/dist/areas/quizzes/attempts.d.ts +2 -0
- package/dist/areas/quizzes/attempts.js +9 -0
- package/dist/areas/quizzes/attempts.js.map +1 -1
- package/dist/areas/quizzes/grading.d.ts +50 -0
- package/dist/areas/quizzes/grading.js +131 -0
- package/dist/areas/quizzes/grading.js.map +1 -0
- package/dist/areas/quizzes/results.d.ts +3 -0
- package/dist/areas/quizzes/results.js +2 -1
- package/dist/areas/quizzes/results.js.map +1 -1
- package/dist/areas/quizzes/structure.d.ts +65 -9
- package/dist/areas/quizzes/structure.js +222 -23
- package/dist/areas/quizzes/structure.js.map +1 -1
- package/dist/areas/ratings/ratings.d.ts +26 -0
- package/dist/areas/ratings/ratings.js +108 -0
- package/dist/areas/ratings/ratings.js.map +1 -0
- package/dist/areas/reports/dates.d.ts +18 -0
- package/dist/areas/reports/dates.js +134 -0
- package/dist/areas/reports/dates.js.map +1 -0
- package/dist/areas/reports/reports.d.ts +143 -0
- package/dist/areas/reports/reports.js +267 -0
- package/dist/areas/reports/reports.js.map +1 -0
- package/dist/areas/scorm/scorm.d.ts +1 -0
- package/dist/areas/scorm/scorm.js.map +1 -1
- package/dist/areas/site/users.d.ts +7 -1
- package/dist/areas/site/users.js +9 -2
- package/dist/areas/site/users.js.map +1 -1
- package/dist/areas/surveys/choice.d.ts +2 -0
- package/dist/areas/surveys/choice.js +6 -3
- package/dist/areas/surveys/choice.js.map +1 -1
- package/dist/areas/surveys/surveys.d.ts +3 -0
- package/dist/areas/surveys/surveys.js +1 -0
- package/dist/areas/surveys/surveys.js.map +1 -1
- package/dist/areas/workshops/workshops.d.ts +133 -0
- package/dist/areas/workshops/workshops.js +335 -0
- package/dist/areas/workshops/workshops.js.map +1 -0
- package/dist/browser/background.d.ts +43 -0
- package/dist/browser/background.js +164 -0
- package/dist/browser/background.js.map +1 -0
- package/dist/browser/host.d.ts +30 -0
- package/dist/browser/host.js +373 -0
- package/dist/browser/host.js.map +1 -0
- package/dist/browser/manager.d.ts +9 -0
- package/dist/browser/manager.js +8 -0
- package/dist/browser/manager.js.map +1 -1
- package/dist/categories.d.ts +1 -0
- package/dist/categories.js +67 -0
- package/dist/categories.js.map +1 -1
- package/dist/cli/args.d.ts +8 -0
- package/dist/cli/args.js +49 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/background.d.ts +23 -0
- package/dist/cli/background.js +128 -0
- package/dist/cli/background.js.map +1 -0
- package/dist/cli/command.d.ts +66 -0
- package/dist/cli/command.js +328 -0
- package/dist/cli/command.js.map +1 -0
- package/dist/cli/commands.generated.js +2635 -577
- package/dist/cli/commands.generated.js.map +1 -1
- package/dist/cli/config.d.ts +2 -0
- package/dist/cli/config.js +2 -0
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/main.js +30 -10
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/run.d.ts +3 -14
- package/dist/cli/run.js +9 -185
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/shell.d.ts +9 -0
- package/dist/cli/shell.js +233 -0
- package/dist/cli/shell.js.map +1 -0
- package/dist/client.d.ts +71 -8
- package/dist/client.js +75 -8
- package/dist/client.js.map +1 -1
- package/dist/core/errors.d.ts +1 -1
- package/dist/core/errors.js.map +1 -1
- package/dist/core/files.d.ts +5 -0
- package/dist/core/files.js +7 -3
- package/dist/core/files.js.map +1 -1
- package/dist/core/forms.d.ts +33 -3
- package/dist/core/forms.js +105 -8
- package/dist/core/forms.js.map +1 -1
- package/dist/core/http.d.ts +7 -0
- package/dist/core/http.js +28 -7
- package/dist/core/http.js.map +1 -1
- package/dist/core/login.d.ts +3 -0
- package/dist/core/login.js +2 -0
- package/dist/core/login.js.map +1 -1
- package/dist/core/session.d.ts +12 -1
- package/dist/core/session.js +12 -1
- package/dist/core/session.js.map +1 -1
- package/dist/index.d.ts +35 -11
- package/dist/index.js +32 -12
- package/dist/index.js.map +1 -1
- package/dist/internals.d.ts +9 -1
- package/dist/internals.js +9 -4
- package/dist/internals.js.map +1 -1
- package/dist/options.d.ts +2 -0
- package/dist/options.js.map +1 -1
- package/dist/testing/fakeMoodle.d.ts +9 -2
- package/dist/testing/fakeMoodle.js +5 -3
- package/dist/testing/fakeMoodle.js.map +1 -1
- package/dist/testing/index.d.ts +7 -0
- package/dist/testing/index.js.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import type { BrowserContext, Page } from "playwright-core";
|
|
2
|
+
import { MoodleError } from "../../core/errors.js";
|
|
3
|
+
import { type FieldValue } from "../../core/forms.js";
|
|
4
|
+
/** The activity isn't a workshop the person can view, or Moodle refused what was asked of it. */
|
|
5
|
+
export declare class WorkshopError extends MoodleError {
|
|
6
|
+
name: string;
|
|
7
|
+
constructor(message: string);
|
|
8
|
+
}
|
|
9
|
+
/** A workshop's phases, in their order. */
|
|
10
|
+
export type WorkshopPhase = "setup" | "submission" | "assessment" | "evaluation" | "closed";
|
|
11
|
+
/** The phase names, in their order. */
|
|
12
|
+
export declare const WORKSHOP_PHASES: WorkshopPhase[];
|
|
13
|
+
/** A submission in the workshop's report. */
|
|
14
|
+
export interface WorkshopSubmission {
|
|
15
|
+
id: number;
|
|
16
|
+
title: string;
|
|
17
|
+
authorId: number;
|
|
18
|
+
author: string;
|
|
19
|
+
/** The grade for the submission Moodle calculated from its assessments (out of the workshop's grade), once it has. */
|
|
20
|
+
grade?: number;
|
|
21
|
+
/** The teacher's override of it, when there is one. */
|
|
22
|
+
gradeOverride?: number;
|
|
23
|
+
}
|
|
24
|
+
/** An assessment of a submission, by its reviewer. */
|
|
25
|
+
export interface WorkshopAssessment {
|
|
26
|
+
id: number;
|
|
27
|
+
submissionId?: number;
|
|
28
|
+
/** The submission's author (the reviewee). */
|
|
29
|
+
authorId?: number;
|
|
30
|
+
author?: string;
|
|
31
|
+
/** Left out when the person can't see reviewers' names. */
|
|
32
|
+
reviewerId?: number;
|
|
33
|
+
reviewer?: string;
|
|
34
|
+
/** The grade it gives the submission (out of the workshop's grade); left out until it's assessed. */
|
|
35
|
+
grade?: number;
|
|
36
|
+
/** The grade for the assessment itself, once the evaluation phase calculated it, and the teacher's override of it. */
|
|
37
|
+
gradingGrade?: number;
|
|
38
|
+
gradingGradeOverride?: number;
|
|
39
|
+
/** Its weight in the submission's grade. */
|
|
40
|
+
weight: number;
|
|
41
|
+
}
|
|
42
|
+
/** A workshop's phase and, for those who can see them, its submissions and their assessments (`workshopInfo`). */
|
|
43
|
+
export interface WorkshopInfo {
|
|
44
|
+
activityId: number;
|
|
45
|
+
phase: WorkshopPhase;
|
|
46
|
+
/** From the workshop's report, which Moodle shows those who can see every submission (from the submission phase on); empty otherwise. */
|
|
47
|
+
submissions: WorkshopSubmission[];
|
|
48
|
+
assessments: WorkshopAssessment[];
|
|
49
|
+
}
|
|
50
|
+
/** One participant's rows of the report, as read in the page. */
|
|
51
|
+
interface ReportRow {
|
|
52
|
+
userId: number;
|
|
53
|
+
name: string;
|
|
54
|
+
submission?: {
|
|
55
|
+
id: number;
|
|
56
|
+
title: string;
|
|
57
|
+
};
|
|
58
|
+
grade?: string;
|
|
59
|
+
gradeOverride?: string;
|
|
60
|
+
received: ReportAssessment[];
|
|
61
|
+
given: ReportAssessment[];
|
|
62
|
+
}
|
|
63
|
+
interface ReportAssessment {
|
|
64
|
+
id: number;
|
|
65
|
+
grade: string;
|
|
66
|
+
gradingGrade: string;
|
|
67
|
+
gradingGradeOverride?: string;
|
|
68
|
+
weight?: string;
|
|
69
|
+
/** The other person: the reviewer of a received one, the author of a given one. */
|
|
70
|
+
userId?: number;
|
|
71
|
+
name?: string;
|
|
72
|
+
}
|
|
73
|
+
/** The report's rows (a participant's cells span several rows, one per assessment) and the last page number of its paging bar. */
|
|
74
|
+
export declare function readReport(page: Page): Promise<{
|
|
75
|
+
rows: ReportRow[];
|
|
76
|
+
lastPage: number;
|
|
77
|
+
}>;
|
|
78
|
+
/** The report's rows as submissions and assessments: an assessment appears as received by its author and given by its reviewer, and is merged by id. */
|
|
79
|
+
export declare function reportInfo(rows: ReportRow[]): Pick<WorkshopInfo, "submissions" | "assessments">;
|
|
80
|
+
/**
|
|
81
|
+
* A workshop's phase, and its report as the workshop's page shows it to a teacher: every submission
|
|
82
|
+
* (its author, title, and grade once calculated) and every assessment (reviewer, the submission's
|
|
83
|
+
* author, the grade it gives, its weight, the grade for assessing once calculated), all of its pages.
|
|
84
|
+
* The report is Moodle's own: it shows from the submission phase on to those who can see every
|
|
85
|
+
* submission, in the course's current group; for anyone else the lists are empty. Grades are out of
|
|
86
|
+
* the workshop's grade for submission, as Moodle shows them. Throws `WorkshopError` for an activity
|
|
87
|
+
* that isn't a workshop the person can view.
|
|
88
|
+
*/
|
|
89
|
+
export declare function workshopInfo(context: BrowserContext, siteUrl: string, activityId: number): Promise<WorkshopInfo>;
|
|
90
|
+
/**
|
|
91
|
+
* Switches a workshop to a phase, as its page's phase links do, confirming Moodle's question (any
|
|
92
|
+
* phase, forwards or back; Moodle allocates nothing and calculates no grades on its own when it
|
|
93
|
+
* switches). Returns a sentence of what changed and the phase afterwards. Throws `WorkshopError` when
|
|
94
|
+
* the person can't switch the workshop's phases (Moodle's `mod/workshop:switchphase`, a teacher's) or
|
|
95
|
+
* the id isn't a workshop's.
|
|
96
|
+
*/
|
|
97
|
+
export declare function setWorkshopPhase(context: BrowserContext, siteUrl: string, activityId: number, phase: WorkshopPhase): Promise<{
|
|
98
|
+
changed: string;
|
|
99
|
+
phase: WorkshopPhase;
|
|
100
|
+
}>;
|
|
101
|
+
/** What to say about one criterion of the assessment form, in the form's order. */
|
|
102
|
+
export interface WorkshopCriterion {
|
|
103
|
+
/**
|
|
104
|
+
* Accumulative grading: the points (the menu's number) or a scale item's name. Number of errors:
|
|
105
|
+
* `1` when the assertion holds, `-1` when it doesn't, or the word the teacher gave either. Rubric:
|
|
106
|
+
* the level, by its position (`1` for the first) or its definition. Comments: none.
|
|
107
|
+
*/
|
|
108
|
+
grade?: number | string;
|
|
109
|
+
/** The comment on the criterion (accumulative, comments and number of errors). */
|
|
110
|
+
comment?: string;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Assesses a submission as the person, through the workshop's assessment form (its grading
|
|
114
|
+
* strategy's: accumulative, comments, number of errors or rubric), as "Assess" on the submission's
|
|
115
|
+
* page and the form do: a teacher who hasn't assessed it yet gets an assessment of their own, and one
|
|
116
|
+
* already made is changed. `criteria` go in the form's order; `feedback` is the overall feedback for
|
|
117
|
+
* the author (HTML), where the workshop has it on; `fields` sets any other field of the form by name
|
|
118
|
+
* (`weight`…). Returns a sentence of what changed, the assessment's id, and the grade it gives the
|
|
119
|
+
* submission out of the workshop's grade. Throws `WorkshopError` outside the assessment phase, when a
|
|
120
|
+
* criterion's grade isn't one it takes, or when the workshop has no overall feedback; `FormError` when
|
|
121
|
+
* Moodle refuses the form (a criterion left without a grade).
|
|
122
|
+
*/
|
|
123
|
+
export declare function assessWorkshopSubmission(context: BrowserContext, siteUrl: string, activityId: number, submissionId: number, { criteria, feedback, fields }: {
|
|
124
|
+
criteria?: WorkshopCriterion[];
|
|
125
|
+
feedback?: string;
|
|
126
|
+
fields?: Record<string, FieldValue>;
|
|
127
|
+
}): Promise<{
|
|
128
|
+
changed: string;
|
|
129
|
+
assessmentId: number;
|
|
130
|
+
grade?: number;
|
|
131
|
+
maxGrade?: number;
|
|
132
|
+
}>;
|
|
133
|
+
export {};
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import { inPage } from "../../core/inPage.js";
|
|
2
|
+
import { moodleErrorMessage, openSitePage, readSessionFacts } from "../../core/session.js";
|
|
3
|
+
import { MoodleError } from "../../core/errors.js";
|
|
4
|
+
import { setFields, submitForm } from "../../core/forms.js";
|
|
5
|
+
/** The activity isn't a workshop the person can view, or Moodle refused what was asked of it. */
|
|
6
|
+
export class WorkshopError extends MoodleError {
|
|
7
|
+
name = "WorkshopError";
|
|
8
|
+
constructor(message) {
|
|
9
|
+
super(message, "workshop");
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/** Moodle's codes for the phases (workshop::PHASE_*). */
|
|
13
|
+
const PHASE_CODES = { setup: 10, submission: 20, assessment: 30, evaluation: 40, closed: 50 };
|
|
14
|
+
/** The phase names, in their order. */
|
|
15
|
+
export const WORKSHOP_PHASES = Object.keys(PHASE_CODES);
|
|
16
|
+
/** The user plan's current phase, `undefined` when the page has none. */
|
|
17
|
+
async function currentPhase(page) {
|
|
18
|
+
const code = await page.evaluate(() => {
|
|
19
|
+
const dt = globalThis.document.querySelector("dl.phase dt.active");
|
|
20
|
+
return /\bphase(\d+)\b/.exec(dt?.className ?? "")?.[1];
|
|
21
|
+
});
|
|
22
|
+
return WORKSHOP_PHASES.find((phase) => String(PHASE_CODES[phase]) === code);
|
|
23
|
+
}
|
|
24
|
+
async function openWorkshop(context, siteUrl, activityId, pageNumber = 0) {
|
|
25
|
+
const page = await openSitePage(context, siteUrl, `mod/workshop/view.php?id=${activityId}${pageNumber > 0 ? `&page=${pageNumber}` : ""}`);
|
|
26
|
+
if ((await page.locator("body#page-mod-workshop-view").count()) === 0) {
|
|
27
|
+
await page.close().catch(() => undefined);
|
|
28
|
+
throw new WorkshopError(`Activity ${activityId} isn't a workshop you can view. Take the id of a workshop (module "workshop") from the course structure.`);
|
|
29
|
+
}
|
|
30
|
+
return page;
|
|
31
|
+
}
|
|
32
|
+
/** The report's rows (a participant's cells span several rows, one per assessment) and the last page number of its paging bar. */
|
|
33
|
+
export function readReport(page) {
|
|
34
|
+
return inPage(page, () => {
|
|
35
|
+
const doc = globalThis.document;
|
|
36
|
+
const text = (el) => (el?.textContent ?? "").replace(/\s+/g, " ").trim();
|
|
37
|
+
const param = (href, name) => {
|
|
38
|
+
const match = new RegExp(`[?&]${name}=(\\d+)`).exec(href ?? "");
|
|
39
|
+
return match ? Number(match[1]) : undefined;
|
|
40
|
+
};
|
|
41
|
+
const assessment = (cell) => {
|
|
42
|
+
const link = cell.querySelector('a.grade[href*="assessment.php"]');
|
|
43
|
+
const id = param(link?.getAttribute("href"), "asid");
|
|
44
|
+
if (!link || id === undefined)
|
|
45
|
+
return undefined;
|
|
46
|
+
const gradingGrade = link.querySelector(".gradinggrade");
|
|
47
|
+
const over = gradingGrade?.querySelector("ins");
|
|
48
|
+
const user = cell.querySelector(".user");
|
|
49
|
+
return {
|
|
50
|
+
id,
|
|
51
|
+
grade: text(link.querySelector(".grade") ?? link),
|
|
52
|
+
gradingGrade: text(gradingGrade?.querySelector("del") ?? gradingGrade).replace(/^\(|\)$/g, ""),
|
|
53
|
+
...(over ? { gradingGradeOverride: text(over) } : {}),
|
|
54
|
+
...(link.querySelector(".weight") ? { weight: text(link.querySelector(".weight")) } : {}),
|
|
55
|
+
...(user ? { userId: param(user.querySelector('a[href*="/user/view.php"]')?.getAttribute("href"), "id"), name: text(user.querySelector(".fullname")) } : {}),
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
const rows = [];
|
|
59
|
+
for (const tr of Array.from(doc.querySelectorAll("table.grading-report tbody tr"))) {
|
|
60
|
+
const participant = tr.querySelector("td.participant");
|
|
61
|
+
if (participant) {
|
|
62
|
+
const title = tr.querySelector("td.submission a.title");
|
|
63
|
+
const total = tr.querySelector("td.submissiongrade");
|
|
64
|
+
const over = total?.querySelector("ins");
|
|
65
|
+
rows.push({
|
|
66
|
+
userId: param(participant.querySelector('a[href*="/user/view.php"]')?.getAttribute("href"), "id") ?? 0,
|
|
67
|
+
name: text(participant.querySelector(":scope > span")),
|
|
68
|
+
...(title ? { submission: { id: param(title.getAttribute("href"), "id") ?? 0, title: text(title) } } : {}),
|
|
69
|
+
...(total ? { grade: text(total.querySelector("del") ?? total) } : {}),
|
|
70
|
+
...(over ? { gradeOverride: text(over) } : {}),
|
|
71
|
+
received: [],
|
|
72
|
+
given: [],
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
const row = rows[rows.length - 1];
|
|
76
|
+
if (!row)
|
|
77
|
+
continue;
|
|
78
|
+
const received = tr.querySelector("td.receivedgrade");
|
|
79
|
+
const given = tr.querySelector("td.givengrade");
|
|
80
|
+
const r = received ? assessment(received) : undefined;
|
|
81
|
+
const g = given ? assessment(given) : undefined;
|
|
82
|
+
if (r)
|
|
83
|
+
row.received.push(r);
|
|
84
|
+
if (g)
|
|
85
|
+
row.given.push(g);
|
|
86
|
+
}
|
|
87
|
+
const pages = Array.from(doc.querySelectorAll('#region-main .pagination a[href*="page="]')).map((a) => param(a.getAttribute("href"), "page") ?? 0);
|
|
88
|
+
return { rows, lastPage: Math.max(0, ...pages) };
|
|
89
|
+
}, undefined);
|
|
90
|
+
}
|
|
91
|
+
/** A grade as the report shows it ("80.00", "80,00" where the site writes a decimal comma); `undefined` for Moodle's "-". */
|
|
92
|
+
function gradeNumber(shown) {
|
|
93
|
+
const match = /-?\d+(?:[.,]\d+)?/.exec(shown ?? "");
|
|
94
|
+
return match ? Number(match[0].replace(",", ".")) : undefined;
|
|
95
|
+
}
|
|
96
|
+
/** The report's rows as submissions and assessments: an assessment appears as received by its author and given by its reviewer, and is merged by id. */
|
|
97
|
+
export function reportInfo(rows) {
|
|
98
|
+
const submissions = [];
|
|
99
|
+
const assessments = new Map();
|
|
100
|
+
const submissionOf = new Map(rows.flatMap((row) => (row.submission ? [[row.userId, row.submission.id]] : [])));
|
|
101
|
+
const merge = (seen, sides) => {
|
|
102
|
+
const grade = gradeNumber(seen.grade);
|
|
103
|
+
const gradingGrade = gradeNumber(seen.gradingGrade);
|
|
104
|
+
const gradingGradeOverride = gradeNumber(seen.gradingGradeOverride);
|
|
105
|
+
const known = assessments.get(seen.id);
|
|
106
|
+
assessments.set(seen.id, {
|
|
107
|
+
id: seen.id,
|
|
108
|
+
...known,
|
|
109
|
+
...Object.fromEntries(Object.entries(sides).filter(([, value]) => value !== undefined && value !== "")),
|
|
110
|
+
...(grade !== undefined ? { grade } : {}),
|
|
111
|
+
...(gradingGrade !== undefined ? { gradingGrade } : {}),
|
|
112
|
+
...(gradingGradeOverride !== undefined ? { gradingGradeOverride } : {}),
|
|
113
|
+
weight: gradeNumber(seen.weight) ?? 1,
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
for (const row of rows) {
|
|
117
|
+
if (row.submission) {
|
|
118
|
+
const grade = gradeNumber(row.grade);
|
|
119
|
+
const gradeOverride = gradeNumber(row.gradeOverride);
|
|
120
|
+
submissions.push({
|
|
121
|
+
id: row.submission.id,
|
|
122
|
+
title: row.submission.title,
|
|
123
|
+
authorId: row.userId,
|
|
124
|
+
author: row.name,
|
|
125
|
+
...(grade !== undefined ? { grade } : {}),
|
|
126
|
+
...(gradeOverride !== undefined ? { gradeOverride } : {}),
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
for (const seen of row.received)
|
|
130
|
+
merge(seen, { submissionId: row.submission?.id, authorId: row.userId, author: row.name, reviewerId: seen.userId, reviewer: seen.name });
|
|
131
|
+
for (const seen of row.given)
|
|
132
|
+
merge(seen, { submissionId: seen.userId !== undefined ? submissionOf.get(seen.userId) : undefined, authorId: seen.userId, author: seen.name, reviewerId: row.userId, reviewer: row.name });
|
|
133
|
+
}
|
|
134
|
+
return { submissions, assessments: [...assessments.values()].sort((a, b) => a.id - b.id) };
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* A workshop's phase, and its report as the workshop's page shows it to a teacher: every submission
|
|
138
|
+
* (its author, title, and grade once calculated) and every assessment (reviewer, the submission's
|
|
139
|
+
* author, the grade it gives, its weight, the grade for assessing once calculated), all of its pages.
|
|
140
|
+
* The report is Moodle's own: it shows from the submission phase on to those who can see every
|
|
141
|
+
* submission, in the course's current group; for anyone else the lists are empty. Grades are out of
|
|
142
|
+
* the workshop's grade for submission, as Moodle shows them. Throws `WorkshopError` for an activity
|
|
143
|
+
* that isn't a workshop the person can view.
|
|
144
|
+
*/
|
|
145
|
+
export async function workshopInfo(context, siteUrl, activityId) {
|
|
146
|
+
const rows = [];
|
|
147
|
+
let phase;
|
|
148
|
+
let lastPage = 0;
|
|
149
|
+
for (let pageNumber = 0; pageNumber <= lastPage; pageNumber++) {
|
|
150
|
+
const page = await openWorkshop(context, siteUrl, activityId, pageNumber);
|
|
151
|
+
try {
|
|
152
|
+
phase ??= await currentPhase(page);
|
|
153
|
+
const report = await readReport(page);
|
|
154
|
+
rows.push(...report.rows);
|
|
155
|
+
lastPage = Math.max(lastPage, report.lastPage);
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
await page.close().catch(() => undefined);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (!phase)
|
|
162
|
+
throw new WorkshopError(`Workshop ${activityId}'s page shows no phase: it isn't what the driver expects of Moodle's workshop page.`);
|
|
163
|
+
return { activityId, phase, ...reportInfo(rows) };
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Switches a workshop to a phase, as its page's phase links do, confirming Moodle's question (any
|
|
167
|
+
* phase, forwards or back; Moodle allocates nothing and calculates no grades on its own when it
|
|
168
|
+
* switches). Returns a sentence of what changed and the phase afterwards. Throws `WorkshopError` when
|
|
169
|
+
* the person can't switch the workshop's phases (Moodle's `mod/workshop:switchphase`, a teacher's) or
|
|
170
|
+
* the id isn't a workshop's.
|
|
171
|
+
*/
|
|
172
|
+
export async function setWorkshopPhase(context, siteUrl, activityId, phase) {
|
|
173
|
+
if (!(phase in PHASE_CODES))
|
|
174
|
+
throw new MoodleError(`A workshop's phase is one of ${WORKSHOP_PHASES.join(", ")}; not "${phase}".`, "invalid-input");
|
|
175
|
+
const page = await openSitePage(context, siteUrl, `mod/workshop/switchphase.php?cmid=${activityId}&phase=${PHASE_CODES[phase]}`);
|
|
176
|
+
try {
|
|
177
|
+
const refused = await moodleErrorMessage(page);
|
|
178
|
+
if (refused !== undefined)
|
|
179
|
+
throw new WorkshopError(`Moodle didn't let you switch workshop ${activityId}'s phase: ${refused}`);
|
|
180
|
+
const confirm = page.locator('form[action*="/mod/workshop/switchphase.php"]:has(input[name="confirm"]) [type="submit"]');
|
|
181
|
+
if ((await confirm.count()) === 0)
|
|
182
|
+
throw new WorkshopError(`Activity ${activityId} isn't a workshop whose phase you can switch: Moodle asked no confirmation.`);
|
|
183
|
+
await Promise.all([page.waitForNavigation({ waitUntil: "domcontentloaded" }), confirm.first().click({ noWaitAfter: true })]);
|
|
184
|
+
const failed = await moodleErrorMessage(page);
|
|
185
|
+
if (failed !== undefined)
|
|
186
|
+
throw new WorkshopError(`Moodle didn't switch workshop ${activityId}'s phase: ${failed}`);
|
|
187
|
+
const now = await currentPhase(page);
|
|
188
|
+
if (now !== phase)
|
|
189
|
+
throw new WorkshopError(`Moodle didn't switch workshop ${activityId} to the ${phase} phase${now ? `: it's in the ${now} phase` : ""}.`);
|
|
190
|
+
return { changed: `Workshop ${activityId} switched to the ${phase} phase.`, phase };
|
|
191
|
+
}
|
|
192
|
+
finally {
|
|
193
|
+
await page.close().catch(() => undefined);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/** The assessment form's grading strategy and its criteria's grade controls, by index. */
|
|
197
|
+
function readAssessmentForm(page) {
|
|
198
|
+
return inPage(page, () => {
|
|
199
|
+
const doc = globalThis.document;
|
|
200
|
+
const form = doc.querySelector("form.assessmentform");
|
|
201
|
+
if (!form)
|
|
202
|
+
return { strategy: "", criteria: [], feedback: false };
|
|
203
|
+
const clean = (s) => (s ?? "").replace(/\s+/g, " ").trim();
|
|
204
|
+
const count = form.querySelectorAll('input[name^="dimensionid__idx_"]').length;
|
|
205
|
+
const criteria = [];
|
|
206
|
+
for (let i = 0; i < count; i++) {
|
|
207
|
+
const select = form.querySelector(`select[name="grade__idx_${i}"]`);
|
|
208
|
+
if (select) {
|
|
209
|
+
// Without its "Choose..." (-1), which isn't a grade.
|
|
210
|
+
const options = Array.from(select.querySelectorAll("option")).filter((o) => o.value !== "-1");
|
|
211
|
+
criteria.push({ name: select.name, rubric: false, options: options.map((o) => ({ value: o.value, text: clean(o.text) })) });
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
const radios = Array.from(form.querySelectorAll(`input[type="radio"][name="grade__idx_${i}"], input[type="radio"][name="chosenlevelid__idx_${i}"]`));
|
|
215
|
+
if (radios.length === 0) {
|
|
216
|
+
criteria.push(undefined);
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
criteria.push({
|
|
220
|
+
name: radios[0].name,
|
|
221
|
+
rubric: radios[0].name.startsWith("chosenlevelid"),
|
|
222
|
+
options: radios.map((radio) => ({ value: radio.value, text: clean((radio.closest("label") ?? form.querySelector(`label[for="${radio.id}"]`))?.textContent) })),
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
return { strategy: form.querySelector('input[name="strategy"]')?.value ?? "", criteria, feedback: form.querySelector('[name="feedbackauthor_editor[text]"]') !== null };
|
|
226
|
+
}, undefined);
|
|
227
|
+
}
|
|
228
|
+
/** The form values for the criteria given; refuses a grade the criterion doesn't take. */
|
|
229
|
+
function criteriaValues(submissionId, strategy, controls, criteria) {
|
|
230
|
+
if (criteria.length > controls.length)
|
|
231
|
+
throw new WorkshopError(`The workshop's assessment form has ${controls.length} criteria; ${criteria.length} were given.`);
|
|
232
|
+
const values = {};
|
|
233
|
+
criteria.forEach((criterion, i) => {
|
|
234
|
+
if (criterion.grade !== undefined) {
|
|
235
|
+
const control = controls[i];
|
|
236
|
+
if (!control)
|
|
237
|
+
throw new WorkshopError(`Criterion ${i + 1} of the assessment form (${strategy}) takes no grade, only a comment.`);
|
|
238
|
+
const options = control.options;
|
|
239
|
+
const wanted = String(criterion.grade);
|
|
240
|
+
const option = control.rubric
|
|
241
|
+
? (typeof criterion.grade === "number" ? options[criterion.grade - 1] : undefined) ?? options.find((o) => o.text === wanted)
|
|
242
|
+
: options.find((o) => o.value === wanted) ?? options.find((o) => o.text === wanted);
|
|
243
|
+
if (!option)
|
|
244
|
+
throw new WorkshopError(`Criterion ${i + 1} of submission ${submissionId}'s assessment takes ${options.map((o, n) => (control.rubric ? `${n + 1} ("${o.text}")` : `"${o.value}"${o.text && o.text !== o.value ? ` ("${o.text}")` : ""}`)).join(", ")}; not "${wanted}".`);
|
|
245
|
+
values[control.name] = option.value;
|
|
246
|
+
}
|
|
247
|
+
if (criterion.comment !== undefined)
|
|
248
|
+
values[`peercomment__idx_${i}`] = criterion.comment;
|
|
249
|
+
});
|
|
250
|
+
return values;
|
|
251
|
+
}
|
|
252
|
+
function formValueIn(page, form, name) {
|
|
253
|
+
return page.evaluate(({ form, name }) => globalThis.document.querySelector(`${form} [name="${name}"]`)?.value, { form, name });
|
|
254
|
+
}
|
|
255
|
+
/** Where the person's own assessment of a submission is: its id, from the submission page (adding it, as "Assess" does, when there's none). */
|
|
256
|
+
async function ownAssessment(page, activityId, submissionId) {
|
|
257
|
+
const me = (await readSessionFacts(page)).userId;
|
|
258
|
+
const existing = await page.evaluate((me) => {
|
|
259
|
+
const doc = globalThis.document;
|
|
260
|
+
for (const box of Array.from(doc.querySelectorAll(".assessment-full"))) {
|
|
261
|
+
const reviewer = box.querySelector('.reviewer .fullname a[href*="/user/view.php"]')?.getAttribute("href") ?? "";
|
|
262
|
+
if (new RegExp(`[?&]id=${me}(&|$)`).test(reviewer)) {
|
|
263
|
+
const asid = box.querySelector('.actions form[action*="/mod/workshop/assessment.php"] input[name="asid"]')?.value ?? /[?&]asid=(\d+)/.exec(box.querySelector('.actions form[action*="/mod/workshop/assessment.php"]')?.getAttribute("action") ?? "")?.[1];
|
|
264
|
+
if (asid)
|
|
265
|
+
return Number(asid);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return undefined;
|
|
269
|
+
}, me);
|
|
270
|
+
if (existing !== undefined)
|
|
271
|
+
return existing;
|
|
272
|
+
const assess = page.locator('form[action*="/mod/workshop/submission.php"]:has(input[name="assess"]) [type="submit"]');
|
|
273
|
+
if ((await assess.count()) === 0) {
|
|
274
|
+
throw new WorkshopError(`Moodle offers you no assessment of submission ${submissionId} in workshop ${activityId}: assessing is only open in the assessment phase, to its reviewers and teachers.`);
|
|
275
|
+
}
|
|
276
|
+
await Promise.all([page.waitForNavigation({ waitUntil: "domcontentloaded" }), assess.first().click({ noWaitAfter: true })]);
|
|
277
|
+
// Moodle redirects to the new assessment's page, whose form carries its id too.
|
|
278
|
+
const asid = /[?&]asid=(\d+)/.exec(page.url())?.[1] ?? (await formValueIn(page, "form.assessmentform", "asid"));
|
|
279
|
+
if (!asid)
|
|
280
|
+
throw new WorkshopError(`Moodle didn't open an assessment of submission ${submissionId} in workshop ${activityId}.`);
|
|
281
|
+
return Number(asid);
|
|
282
|
+
}
|
|
283
|
+
/** The person's assessment's grade and maximum, as its page's header shows them ("80.00 of 80.00"). */
|
|
284
|
+
async function assessmentGrade(page) {
|
|
285
|
+
const shown = await page.evaluate(() => {
|
|
286
|
+
const box = globalThis.document.querySelector(".assessment-full .header .grade:not(.nograde)");
|
|
287
|
+
return box?.textContent ?? "";
|
|
288
|
+
});
|
|
289
|
+
const [grade, maxGrade] = (shown.match(/\d+(?:[.,]\d+)?/g) ?? []).map((n) => Number(n.replace(",", ".")));
|
|
290
|
+
return { ...(grade !== undefined ? { grade } : {}), ...(maxGrade !== undefined ? { maxGrade } : {}) };
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Assesses a submission as the person, through the workshop's assessment form (its grading
|
|
294
|
+
* strategy's: accumulative, comments, number of errors or rubric), as "Assess" on the submission's
|
|
295
|
+
* page and the form do: a teacher who hasn't assessed it yet gets an assessment of their own, and one
|
|
296
|
+
* already made is changed. `criteria` go in the form's order; `feedback` is the overall feedback for
|
|
297
|
+
* the author (HTML), where the workshop has it on; `fields` sets any other field of the form by name
|
|
298
|
+
* (`weight`…). Returns a sentence of what changed, the assessment's id, and the grade it gives the
|
|
299
|
+
* submission out of the workshop's grade. Throws `WorkshopError` outside the assessment phase, when a
|
|
300
|
+
* criterion's grade isn't one it takes, or when the workshop has no overall feedback; `FormError` when
|
|
301
|
+
* Moodle refuses the form (a criterion left without a grade).
|
|
302
|
+
*/
|
|
303
|
+
export async function assessWorkshopSubmission(context, siteUrl, activityId, submissionId, { criteria = [], feedback, fields = {} }) {
|
|
304
|
+
const page = await openSitePage(context, siteUrl, `mod/workshop/submission.php?cmid=${activityId}&id=${submissionId}`);
|
|
305
|
+
try {
|
|
306
|
+
const refused = await moodleErrorMessage(page);
|
|
307
|
+
if (refused !== undefined)
|
|
308
|
+
throw new WorkshopError(`You can't open submission ${submissionId} of workshop ${activityId}: ${refused}`);
|
|
309
|
+
if ((await page.locator("body#page-mod-workshop-submission").count()) === 0)
|
|
310
|
+
throw new WorkshopError(`Activity ${activityId} isn't a workshop with submission ${submissionId}.`);
|
|
311
|
+
const assessmentId = await ownAssessment(page, activityId, submissionId);
|
|
312
|
+
if (!/\/mod\/workshop\/assessment\.php/.test(page.url()))
|
|
313
|
+
await page.goto(new URL(`mod/workshop/assessment.php?asid=${assessmentId}`, siteUrl).href, { waitUntil: "domcontentloaded" });
|
|
314
|
+
await page.waitForLoadState("load");
|
|
315
|
+
const form = await readAssessmentForm(page);
|
|
316
|
+
if (!form.strategy)
|
|
317
|
+
throw new WorkshopError(`Assessment ${assessmentId} has no form you can fill: assessing is only open in the assessment phase.`);
|
|
318
|
+
if (feedback !== undefined && !form.feedback)
|
|
319
|
+
throw new WorkshopError(`Workshop ${activityId} has no overall feedback: it's off in its settings.`);
|
|
320
|
+
const values = { ...criteriaValues(submissionId, form.strategy, form.criteria, criteria), ...(feedback !== undefined ? { "feedbackauthor_editor[text]": feedback } : {}), ...fields };
|
|
321
|
+
await setFields(page, values, "form.assessmentform");
|
|
322
|
+
// "Save and continue" comes back to the assessment, whose header then shows the grade.
|
|
323
|
+
await submitForm(page, "saveandcontinue");
|
|
324
|
+
const shown = await assessmentGrade(page);
|
|
325
|
+
return {
|
|
326
|
+
changed: `Submission ${submissionId} of workshop ${activityId} assessed${shown.grade !== undefined ? `: ${shown.grade}${shown.maxGrade !== undefined ? ` of ${shown.maxGrade}` : ""}` : ""}.`,
|
|
327
|
+
assessmentId,
|
|
328
|
+
...shown,
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
finally {
|
|
332
|
+
await page.close().catch(() => undefined);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
//# sourceMappingURL=workshops.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workshops.js","sourceRoot":"","sources":["../../../src/areas/workshops/workshops.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Channel } from "../options.js";
|
|
2
|
+
import { type BrowserManager } from "./manager.js";
|
|
3
|
+
/** The file a background browser leaves in its profile: where it is and its token, readable only by the person. */
|
|
4
|
+
export declare const BACKGROUND_FILE = ".browser.json";
|
|
5
|
+
/** A browser kept open by another process, as its profile's `.browser.json` says. */
|
|
6
|
+
export interface BackgroundBrowser {
|
|
7
|
+
/** The WebSocket to connect to, on 127.0.0.1. */
|
|
8
|
+
endpoint: string;
|
|
9
|
+
/** Sent as `Authorization: Bearer <token>`: without it the browser refuses the connection. */
|
|
10
|
+
token: string;
|
|
11
|
+
/** The process holding the browser. */
|
|
12
|
+
pid: number;
|
|
13
|
+
channel: Channel;
|
|
14
|
+
/** Whether it shows a window. */
|
|
15
|
+
headed: boolean;
|
|
16
|
+
}
|
|
17
|
+
/** The background browser `connectBrowserManager` connects to (`endpoint` and `token` as `backgroundBrowser` gives them), and how to use it. */
|
|
18
|
+
export interface ConnectOptions {
|
|
19
|
+
endpoint: string;
|
|
20
|
+
token: string;
|
|
21
|
+
/** Whether the browser shows a window: the pages functions open are then followed in it (ADR-007). */
|
|
22
|
+
headed?: boolean;
|
|
23
|
+
channel?: Channel;
|
|
24
|
+
/** The browser's profile, to save the session cookies in when disconnecting (ADR-003). */
|
|
25
|
+
profileDir?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The browser another process keeps open on `profileDir` (the `moodle open` command's), or `undefined`
|
|
29
|
+
* when there's none. A `.browser.json` left by a process that's gone is removed.
|
|
30
|
+
*/
|
|
31
|
+
export declare function backgroundBrowser(profileDir: string): Promise<BackgroundBrowser | undefined>;
|
|
32
|
+
/**
|
|
33
|
+
* Asks the background browser on `profileDir` to save the session and close, and waits until it has;
|
|
34
|
+
* `false` when there's none.
|
|
35
|
+
*/
|
|
36
|
+
export declare function closeBackgroundBrowser(profileDir: string, timeoutMs?: number): Promise<boolean>;
|
|
37
|
+
/**
|
|
38
|
+
* A browser manager on a browser another process keeps open (`backgroundBrowser`), for a program to
|
|
39
|
+
* share it: it connects on first use, works in the browser's own context, and `close()` only
|
|
40
|
+
* disconnects, leaving the browser open. With a window, the pages functions open are followed in its
|
|
41
|
+
* first tab (ADR-007); it can't add or remove a window from a browser it didn't launch.
|
|
42
|
+
*/
|
|
43
|
+
export declare function connectBrowserManager(options: ConnectOptions): BrowserManager;
|