@falkenslab/moodle-driver 0.0.0-stage → 0.4.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/LICENSE +21 -0
- package/README.md +131 -2
- package/dist/areas/activities/build.d.ts +98 -0
- package/dist/areas/activities/build.js +248 -0
- package/dist/areas/activities/build.js.map +1 -0
- package/dist/areas/activities/content.d.ts +53 -0
- package/dist/areas/activities/content.js +113 -0
- package/dist/areas/activities/content.js.map +1 -0
- package/dist/areas/assignments/grader.d.ts +68 -0
- package/dist/areas/assignments/grader.js +169 -0
- package/dist/areas/assignments/grader.js.map +1 -0
- package/dist/areas/assignments/grading.d.ts +86 -0
- package/dist/areas/assignments/grading.js +162 -0
- package/dist/areas/assignments/grading.js.map +1 -0
- package/dist/areas/assignments/student.d.ts +57 -0
- package/dist/areas/assignments/student.js +115 -0
- package/dist/areas/assignments/student.js.map +1 -0
- package/dist/areas/calendar/calendar.d.ts +43 -0
- package/dist/areas/calendar/calendar.js +84 -0
- package/dist/areas/calendar/calendar.js.map +1 -0
- package/dist/areas/completion/completion.d.ts +50 -0
- package/dist/areas/completion/completion.js +112 -0
- package/dist/areas/completion/completion.js.map +1 -0
- package/dist/areas/courses/list.d.ts +59 -0
- package/dist/areas/courses/list.js +74 -0
- package/dist/areas/courses/list.js.map +1 -0
- package/dist/areas/courses/overview.d.ts +83 -0
- package/dist/areas/courses/overview.js +137 -0
- package/dist/areas/courses/overview.js.map +1 -0
- package/dist/areas/courses/state.d.ts +51 -0
- package/dist/areas/courses/state.js +45 -0
- package/dist/areas/courses/state.js.map +1 -0
- package/dist/areas/courses/structure.d.ts +89 -0
- package/dist/areas/courses/structure.js +104 -0
- package/dist/areas/courses/structure.js.map +1 -0
- package/dist/areas/forums/discussions.d.ts +57 -0
- package/dist/areas/forums/discussions.js +119 -0
- package/dist/areas/forums/discussions.js.map +1 -0
- package/dist/areas/forums/posts.d.ts +83 -0
- package/dist/areas/forums/posts.js +114 -0
- package/dist/areas/forums/posts.js.map +1 -0
- package/dist/areas/grades/gradebook.d.ts +104 -0
- package/dist/areas/grades/gradebook.js +250 -0
- package/dist/areas/grades/gradebook.js.map +1 -0
- package/dist/areas/grades/user-report.d.ts +26 -0
- package/dist/areas/grades/user-report.js +71 -0
- package/dist/areas/grades/user-report.js.map +1 -0
- package/dist/areas/h5p/h5p.d.ts +33 -0
- package/dist/areas/h5p/h5p.js +104 -0
- package/dist/areas/h5p/h5p.js.map +1 -0
- package/dist/areas/lessons/lessons.d.ts +60 -0
- package/dist/areas/lessons/lessons.js +145 -0
- package/dist/areas/lessons/lessons.js.map +1 -0
- package/dist/areas/messages/messages.d.ts +68 -0
- package/dist/areas/messages/messages.js +84 -0
- package/dist/areas/messages/messages.js.map +1 -0
- package/dist/areas/participants/participants.d.ts +57 -0
- package/dist/areas/participants/participants.js +135 -0
- package/dist/areas/participants/participants.js.map +1 -0
- package/dist/areas/questions/banks.d.ts +83 -0
- package/dist/areas/questions/banks.js +239 -0
- package/dist/areas/questions/banks.js.map +1 -0
- package/dist/areas/quizzes/attempts.d.ts +123 -0
- package/dist/areas/quizzes/attempts.js +300 -0
- package/dist/areas/quizzes/attempts.js.map +1 -0
- package/dist/areas/quizzes/results.d.ts +36 -0
- package/dist/areas/quizzes/results.js +93 -0
- package/dist/areas/quizzes/results.js.map +1 -0
- package/dist/areas/quizzes/structure.d.ts +42 -0
- package/dist/areas/quizzes/structure.js +100 -0
- package/dist/areas/quizzes/structure.js.map +1 -0
- package/dist/areas/scorm/scorm.d.ts +60 -0
- package/dist/areas/scorm/scorm.js +171 -0
- package/dist/areas/scorm/scorm.js.map +1 -0
- package/dist/areas/site/info.d.ts +25 -0
- package/dist/areas/site/info.js +45 -0
- package/dist/areas/site/info.js.map +1 -0
- package/dist/areas/site/users.d.ts +56 -0
- package/dist/areas/site/users.js +89 -0
- package/dist/areas/site/users.js.map +1 -0
- package/dist/areas/surveys/choice.d.ts +38 -0
- package/dist/areas/surveys/choice.js +105 -0
- package/dist/areas/surveys/choice.js.map +1 -0
- package/dist/areas/surveys/surveys.d.ts +51 -0
- package/dist/areas/surveys/surveys.js +201 -0
- package/dist/areas/surveys/surveys.js.map +1 -0
- package/dist/browser/chromium.d.ts +15 -0
- package/dist/browser/chromium.js +53 -0
- package/dist/browser/chromium.js.map +1 -0
- package/dist/browser/cookies.d.ts +11 -0
- package/dist/browser/cookies.js +31 -0
- package/dist/browser/cookies.js.map +1 -0
- package/dist/browser/manager.d.ts +57 -0
- package/dist/browser/manager.js +177 -0
- package/dist/browser/manager.js.map +1 -0
- package/dist/browser.d.ts +53 -0
- package/dist/browser.js +167 -0
- package/dist/browser.js.map +1 -0
- package/dist/categories.d.ts +28 -0
- package/dist/categories.js +225 -0
- package/dist/categories.js.map +1 -0
- package/dist/chromiumInstaller.d.ts +15 -0
- package/dist/chromiumInstaller.js +52 -0
- package/dist/chromiumInstaller.js.map +1 -0
- package/dist/cli/args.d.ts +33 -0
- package/dist/cli/args.js +194 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/commands.generated.d.ts +4 -0
- package/dist/cli/commands.generated.js +2217 -0
- package/dist/cli/commands.generated.js.map +1 -0
- package/dist/cli/config.d.ts +46 -0
- package/dist/cli/config.js +107 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +19 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/output.d.ts +4 -0
- package/dist/cli/output.js +71 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/cli/run.d.ts +15 -0
- package/dist/cli/run.js +194 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/spec.d.ts +42 -0
- package/dist/cli/spec.js +4 -0
- package/dist/cli/spec.js.map +1 -0
- package/dist/client.d.ts +224 -0
- package/dist/client.js +182 -0
- package/dist/client.js.map +1 -0
- package/dist/core/ajax.d.ts +25 -0
- package/dist/core/ajax.js +85 -0
- package/dist/core/ajax.js.map +1 -0
- package/dist/core/editor.d.ts +18 -0
- package/dist/core/editor.js +51 -0
- package/dist/core/editor.js.map +1 -0
- package/dist/core/errors.d.ts +12 -0
- package/dist/core/errors.js +10 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/files.d.ts +16 -0
- package/dist/core/files.js +58 -0
- package/dist/core/files.js.map +1 -0
- package/dist/core/forms.d.ts +45 -0
- package/dist/core/forms.js +200 -0
- package/dist/core/forms.js.map +1 -0
- package/dist/core/http.d.ts +18 -0
- package/dist/core/http.js +22 -0
- package/dist/core/http.js.map +1 -0
- package/dist/core/inPage.d.ts +8 -0
- package/dist/core/inPage.js +10 -0
- package/dist/core/inPage.js.map +1 -0
- package/dist/core/login.d.ts +40 -0
- package/dist/core/login.js +83 -0
- package/dist/core/login.js.map +1 -0
- package/dist/core/pace.d.ts +4 -0
- package/dist/core/pace.js +21 -0
- package/dist/core/pace.js.map +1 -0
- package/dist/core/session.d.ts +55 -0
- package/dist/core/session.js +157 -0
- package/dist/core/session.js.map +1 -0
- package/dist/core/site.d.ts +8 -0
- package/dist/core/site.js +36 -0
- package/dist/core/site.js.map +1 -0
- package/dist/core/text.d.ts +21 -0
- package/dist/core/text.js +113 -0
- package/dist/core/text.js.map +1 -0
- package/dist/home.d.ts +19 -0
- package/dist/home.js +54 -0
- package/dist/home.js.map +1 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.js +52 -0
- package/dist/index.js.map +1 -0
- package/dist/internals.d.ts +11 -0
- package/dist/internals.js +15 -0
- package/dist/internals.js.map +1 -0
- package/dist/moodle/activities.d.ts +94 -0
- package/dist/moodle/activities.js +240 -0
- package/dist/moodle/activities.js.map +1 -0
- package/dist/moodle/ajax.d.ts +22 -0
- package/dist/moodle/ajax.js +91 -0
- package/dist/moodle/ajax.js.map +1 -0
- package/dist/moodle/assign.d.ts +65 -0
- package/dist/moodle/assign.js +162 -0
- package/dist/moodle/assign.js.map +1 -0
- package/dist/moodle/calendar.d.ts +41 -0
- package/dist/moodle/calendar.js +80 -0
- package/dist/moodle/calendar.js.map +1 -0
- package/dist/moodle/choice.d.ts +33 -0
- package/dist/moodle/choice.js +100 -0
- package/dist/moodle/choice.js.map +1 -0
- package/dist/moodle/completion.d.ts +48 -0
- package/dist/moodle/completion.js +108 -0
- package/dist/moodle/completion.js.map +1 -0
- package/dist/moodle/content.d.ts +43 -0
- package/dist/moodle/content.js +105 -0
- package/dist/moodle/content.js.map +1 -0
- package/dist/moodle/course.d.ts +51 -0
- package/dist/moodle/course.js +45 -0
- package/dist/moodle/course.js.map +1 -0
- package/dist/moodle/courses.d.ts +57 -0
- package/dist/moodle/courses.js +70 -0
- package/dist/moodle/courses.js.map +1 -0
- package/dist/moodle/editor.d.ts +18 -0
- package/dist/moodle/editor.js +50 -0
- package/dist/moodle/editor.js.map +1 -0
- package/dist/moodle/files.d.ts +16 -0
- package/dist/moodle/files.js +57 -0
- package/dist/moodle/files.js.map +1 -0
- package/dist/moodle/forms.d.ts +44 -0
- package/dist/moodle/forms.js +199 -0
- package/dist/moodle/forms.js.map +1 -0
- package/dist/moodle/forum.d.ts +55 -0
- package/dist/moodle/forum.js +115 -0
- package/dist/moodle/forum.js.map +1 -0
- package/dist/moodle/forumPosts.d.ts +83 -0
- package/dist/moodle/forumPosts.js +113 -0
- package/dist/moodle/forumPosts.js.map +1 -0
- package/dist/moodle/gradebook.d.ts +102 -0
- package/dist/moodle/gradebook.js +246 -0
- package/dist/moodle/gradebook.js.map +1 -0
- package/dist/moodle/grades.d.ts +24 -0
- package/dist/moodle/grades.js +67 -0
- package/dist/moodle/grades.js.map +1 -0
- package/dist/moodle/grading.d.ts +92 -0
- package/dist/moodle/grading.js +161 -0
- package/dist/moodle/grading.js.map +1 -0
- package/dist/moodle/h5p.d.ts +31 -0
- package/dist/moodle/h5p.js +100 -0
- package/dist/moodle/h5p.js.map +1 -0
- package/dist/moodle/inPage.d.ts +8 -0
- package/dist/moodle/inPage.js +10 -0
- package/dist/moodle/inPage.js.map +1 -0
- package/dist/moodle/lesson.d.ts +57 -0
- package/dist/moodle/lesson.js +140 -0
- package/dist/moodle/lesson.js.map +1 -0
- package/dist/moodle/login.d.ts +37 -0
- package/dist/moodle/login.js +77 -0
- package/dist/moodle/login.js.map +1 -0
- package/dist/moodle/messages.d.ts +66 -0
- package/dist/moodle/messages.js +80 -0
- package/dist/moodle/messages.js.map +1 -0
- package/dist/moodle/overview.d.ts +83 -0
- package/dist/moodle/overview.js +137 -0
- package/dist/moodle/overview.js.map +1 -0
- package/dist/moodle/pace.d.ts +4 -0
- package/dist/moodle/pace.js +19 -0
- package/dist/moodle/pace.js.map +1 -0
- package/dist/moodle/participants.d.ts +55 -0
- package/dist/moodle/participants.js +131 -0
- package/dist/moodle/participants.js.map +1 -0
- package/dist/moodle/questionBank.d.ts +81 -0
- package/dist/moodle/questionBank.js +235 -0
- package/dist/moodle/questionBank.js.map +1 -0
- package/dist/moodle/quiz.d.ts +117 -0
- package/dist/moodle/quiz.js +291 -0
- package/dist/moodle/quiz.js.map +1 -0
- package/dist/moodle/quizResults.d.ts +36 -0
- package/dist/moodle/quizResults.js +93 -0
- package/dist/moodle/quizResults.js.map +1 -0
- package/dist/moodle/quizStructure.d.ts +42 -0
- package/dist/moodle/quizStructure.js +100 -0
- package/dist/moodle/quizStructure.js.map +1 -0
- package/dist/moodle/session.d.ts +39 -0
- package/dist/moodle/session.js +99 -0
- package/dist/moodle/session.js.map +1 -0
- package/dist/moodle/site.d.ts +8 -0
- package/dist/moodle/site.js +36 -0
- package/dist/moodle/site.js.map +1 -0
- package/dist/moodle/siteInfo.d.ts +25 -0
- package/dist/moodle/siteInfo.js +45 -0
- package/dist/moodle/siteInfo.js.map +1 -0
- package/dist/moodle/structure.d.ts +94 -0
- package/dist/moodle/structure.js +103 -0
- package/dist/moodle/structure.js.map +1 -0
- package/dist/moodle/submission.d.ts +48 -0
- package/dist/moodle/submission.js +109 -0
- package/dist/moodle/submission.js.map +1 -0
- package/dist/moodle/survey.d.ts +47 -0
- package/dist/moodle/survey.js +197 -0
- package/dist/moodle/survey.js.map +1 -0
- package/dist/moodle/text.d.ts +21 -0
- package/dist/moodle/text.js +113 -0
- package/dist/moodle/text.js.map +1 -0
- package/dist/moodle/user.d.ts +54 -0
- package/dist/moodle/user.js +78 -0
- package/dist/moodle/user.js.map +1 -0
- package/dist/options.d.ts +30 -0
- package/dist/options.js +35 -0
- package/dist/options.js.map +1 -0
- package/dist/sessionCookies.d.ts +11 -0
- package/dist/sessionCookies.js +31 -0
- package/dist/sessionCookies.js.map +1 -0
- package/dist/testing/fakeMoodle.d.ts +55 -0
- package/dist/testing/fakeMoodle.js +103 -0
- package/dist/testing/fakeMoodle.js.map +1 -0
- package/dist/testing/gradingPanel.d.ts +6 -0
- package/dist/testing/gradingPanel.js +22 -0
- package/dist/testing/gradingPanel.js.map +1 -0
- package/dist/testing/index.d.ts +5 -0
- package/dist/testing/index.js +18 -0
- package/dist/testing/index.js.map +1 -0
- package/package.json +78 -4
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { BrowserContext, Page } from "playwright-core";
|
|
2
|
+
/** The gradebook can't be read or changed: not allowed, recalculating, or a value Moodle refuses. */
|
|
3
|
+
export declare class GradebookError extends Error {
|
|
4
|
+
name: string;
|
|
5
|
+
}
|
|
6
|
+
/** Moodle's aggregations by their constant (GRADE_AGGREGATE_*). */
|
|
7
|
+
declare const AGGREGATION_CODES: {
|
|
8
|
+
readonly mean: 0;
|
|
9
|
+
readonly median: 2;
|
|
10
|
+
readonly lowest: 4;
|
|
11
|
+
readonly highest: 6;
|
|
12
|
+
readonly mode: 8;
|
|
13
|
+
readonly "weighted-mean": 10;
|
|
14
|
+
readonly "simple-weighted-mean": 11;
|
|
15
|
+
readonly "mean-with-extra-credits": 12;
|
|
16
|
+
readonly natural: 13;
|
|
17
|
+
};
|
|
18
|
+
export type GradeAggregation = keyof typeof AGGREGATION_CODES;
|
|
19
|
+
export declare const GRADE_AGGREGATIONS: GradeAggregation[];
|
|
20
|
+
export interface GradeColumn {
|
|
21
|
+
itemId: number;
|
|
22
|
+
name: string;
|
|
23
|
+
kind: "item" | "category" | "course";
|
|
24
|
+
activityId?: number;
|
|
25
|
+
activityType?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface GraderReport {
|
|
28
|
+
columns: GradeColumn[];
|
|
29
|
+
/** Grades as the report shows them ("-" left out). */
|
|
30
|
+
rows: {
|
|
31
|
+
userId: number;
|
|
32
|
+
fullName: string;
|
|
33
|
+
grades: {
|
|
34
|
+
itemId: number;
|
|
35
|
+
grade?: string;
|
|
36
|
+
}[];
|
|
37
|
+
}[];
|
|
38
|
+
}
|
|
39
|
+
/** One page of the grader report, by data attributes and links, in any language. */
|
|
40
|
+
export declare function readGraderReport(page: Page): Promise<GraderReport>;
|
|
41
|
+
/**
|
|
42
|
+
* Everyone's grades in a course as the grader report shows them, page by page, in one group when
|
|
43
|
+
* given. People come as user id and full name only.
|
|
44
|
+
*/
|
|
45
|
+
export declare function graderReport(context: BrowserContext, siteUrl: string, courseId: number, { group }?: {
|
|
46
|
+
group?: number;
|
|
47
|
+
}): Promise<GraderReport>;
|
|
48
|
+
export interface GradebookCategory {
|
|
49
|
+
id: number;
|
|
50
|
+
/** The category's own grade item (its total). */
|
|
51
|
+
itemId: number;
|
|
52
|
+
name: string;
|
|
53
|
+
parentId?: number;
|
|
54
|
+
aggregation?: GradeAggregation;
|
|
55
|
+
maxGrade?: number;
|
|
56
|
+
/** Its weight in its parent, in percent, where the parent aggregates with weights. */
|
|
57
|
+
weight?: number;
|
|
58
|
+
}
|
|
59
|
+
export interface GradebookItem {
|
|
60
|
+
id: number;
|
|
61
|
+
name: string;
|
|
62
|
+
categoryId?: number;
|
|
63
|
+
/** The module ("assign", "quiz"…) for an activity's item, "manual" for one made in the gradebook. */
|
|
64
|
+
type: string;
|
|
65
|
+
maxGrade?: number;
|
|
66
|
+
/** In percent, where the category aggregates with weights. */
|
|
67
|
+
weight?: number;
|
|
68
|
+
weightOverridden: boolean;
|
|
69
|
+
}
|
|
70
|
+
/** The gradebook setup page's rows, by data attributes and field names. */
|
|
71
|
+
export declare function readGradebookSetup(page: Page): Promise<{
|
|
72
|
+
categories: GradebookCategory[];
|
|
73
|
+
items: GradebookItem[];
|
|
74
|
+
}>;
|
|
75
|
+
/** A course's gradebook setup: its categories (aggregation, weights) and grade items (category, maximum, weight). */
|
|
76
|
+
export declare function gradebookSetup(context: BrowserContext, siteUrl: string, courseId: number): Promise<{
|
|
77
|
+
categories: GradebookCategory[];
|
|
78
|
+
items: GradebookItem[];
|
|
79
|
+
}>;
|
|
80
|
+
/**
|
|
81
|
+
* Changes a grade item: moves it into a category, sets its weight (percent, overriding Natural's
|
|
82
|
+
* automatic one), or its maximum grade (a manual item's; an activity's is set in the activity).
|
|
83
|
+
* Returns the item afterwards.
|
|
84
|
+
*/
|
|
85
|
+
export declare function updateGradeItem(context: BrowserContext, siteUrl: string, courseId: number, itemId: number, { category, weight, maxGrade }: {
|
|
86
|
+
category?: number;
|
|
87
|
+
weight?: number;
|
|
88
|
+
maxGrade?: number;
|
|
89
|
+
}): Promise<{
|
|
90
|
+
changed: string;
|
|
91
|
+
item?: GradebookItem;
|
|
92
|
+
}>;
|
|
93
|
+
/** Adds a grade category (at the course's top level, or inside `parentId`); returns it as the setup shows it. */
|
|
94
|
+
export declare function addGradeCategory(context: BrowserContext, siteUrl: string, courseId: number, { name, aggregation, parentId }: {
|
|
95
|
+
name: string;
|
|
96
|
+
aggregation?: GradeAggregation;
|
|
97
|
+
parentId?: number;
|
|
98
|
+
}): Promise<{
|
|
99
|
+
changed: string;
|
|
100
|
+
category?: GradebookCategory;
|
|
101
|
+
}>;
|
|
102
|
+
export {};
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { callService } from "./ajax.js";
|
|
2
|
+
import { setFields } from "./forms.js";
|
|
3
|
+
import { moodleErrorMessage, openSitePage } from "./session.js";
|
|
4
|
+
/** The gradebook can't be read or changed: not allowed, recalculating, or a value Moodle refuses. */
|
|
5
|
+
export class GradebookError extends Error {
|
|
6
|
+
name = "GradebookError";
|
|
7
|
+
}
|
|
8
|
+
/** Moodle's aggregations by their constant (GRADE_AGGREGATE_*). */
|
|
9
|
+
const AGGREGATION_CODES = {
|
|
10
|
+
mean: 0,
|
|
11
|
+
median: 2,
|
|
12
|
+
lowest: 4,
|
|
13
|
+
highest: 6,
|
|
14
|
+
mode: 8,
|
|
15
|
+
"weighted-mean": 10,
|
|
16
|
+
"simple-weighted-mean": 11,
|
|
17
|
+
"mean-with-extra-credits": 12,
|
|
18
|
+
natural: 13,
|
|
19
|
+
};
|
|
20
|
+
export const GRADE_AGGREGATIONS = Object.keys(AGGREGATION_CODES);
|
|
21
|
+
/** One page of the grader report, by data attributes and links, in any language. */
|
|
22
|
+
export function readGraderReport(page) {
|
|
23
|
+
return page.evaluate(() => {
|
|
24
|
+
const doc = globalThis.document;
|
|
25
|
+
const columns = Array.from(doc.querySelectorAll("table#user-grades tr.heading th[data-itemid]")).map((th) => {
|
|
26
|
+
const header = th.querySelector(".gradeitemheader");
|
|
27
|
+
const copy = header?.cloneNode(true);
|
|
28
|
+
for (const extra of Array.from(copy?.querySelectorAll("img, .visually-hidden") ?? []))
|
|
29
|
+
extra.remove();
|
|
30
|
+
const match = /\/mod\/([a-z0-9_]+)\/view\.php\?(?:.*&)?id=(\d+)/.exec(header?.getAttribute("href") ?? "");
|
|
31
|
+
const kind = /\bcourseitem\b/.test(th.className) ? "course" : /\bcategoryitem\b/.test(th.className) ? "category" : "item";
|
|
32
|
+
return {
|
|
33
|
+
itemId: Number(th.getAttribute("data-itemid")),
|
|
34
|
+
name: (copy?.textContent ?? "").replace(/\s+/g, " ").trim(),
|
|
35
|
+
kind,
|
|
36
|
+
...(match ? { activityId: Number(match[2]), activityType: match[1] } : {}),
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
const rows = Array.from(doc.querySelectorAll("table#user-grades tr.userrow[data-uid]")).map((row) => {
|
|
40
|
+
const link = row.querySelector("a.username")?.cloneNode(true);
|
|
41
|
+
for (const extra of Array.from(link?.querySelectorAll(".userinitials, img, .visually-hidden") ?? []))
|
|
42
|
+
extra.remove();
|
|
43
|
+
const grades = Array.from(row.querySelectorAll("td.gradecell[data-itemid]")).map((cell) => {
|
|
44
|
+
const value = (cell.querySelector(".gradevalue")?.textContent ?? "").replace(/\s+/g, " ").trim();
|
|
45
|
+
return { itemId: Number(cell.getAttribute("data-itemid")), ...(value && value !== "-" ? { grade: value } : {}) };
|
|
46
|
+
});
|
|
47
|
+
return { userId: Number(row.getAttribute("data-uid")), fullName: (link?.textContent ?? "").replace(/\s+/g, " ").trim(), grades };
|
|
48
|
+
});
|
|
49
|
+
return { columns, rows };
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
const MAX_PAGES = 100;
|
|
53
|
+
/**
|
|
54
|
+
* Everyone's grades in a course as the grader report shows them, page by page, in one group when
|
|
55
|
+
* given. People come as user id and full name only.
|
|
56
|
+
*/
|
|
57
|
+
export async function graderReport(context, siteUrl, courseId, { group } = {}) {
|
|
58
|
+
const report = { columns: [], rows: [] };
|
|
59
|
+
const seen = new Set();
|
|
60
|
+
for (let p = 0; p < MAX_PAGES; p++) {
|
|
61
|
+
const page = await openSitePage(context, siteUrl, `grade/report/grader/index.php?id=${courseId}&page=${p}${group ? `&group=${group}` : ""}`);
|
|
62
|
+
try {
|
|
63
|
+
const refused = await moodleErrorMessage(page);
|
|
64
|
+
if (refused !== undefined || (await page.locator("body#page-grade-report-grader-index").count()) === 0) {
|
|
65
|
+
throw new GradebookError(`You can't see course ${courseId}'s grader report: ${refused ?? "the course doesn't exist, or you aren't allowed"}`);
|
|
66
|
+
}
|
|
67
|
+
// Moodle shows a progress bar instead while it recalculates grades (deleted activities, changed weights).
|
|
68
|
+
if ((await page.locator("table#user-grades").count()) === 0) {
|
|
69
|
+
throw new GradebookError(`Course ${courseId}'s grader report isn't available now: Moodle may be recalculating its grades. Try again in a moment.`);
|
|
70
|
+
}
|
|
71
|
+
const read = await readGraderReport(page);
|
|
72
|
+
if (p === 0)
|
|
73
|
+
report.columns = read.columns;
|
|
74
|
+
const fresh = read.rows.filter((row) => !seen.has(row.userId));
|
|
75
|
+
if (fresh.length === 0)
|
|
76
|
+
break;
|
|
77
|
+
for (const row of fresh)
|
|
78
|
+
seen.add(row.userId);
|
|
79
|
+
report.rows.push(...fresh);
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
await page.close().catch(() => undefined);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return report;
|
|
86
|
+
}
|
|
87
|
+
/** The gradebook setup page's rows, by data attributes and field names. */
|
|
88
|
+
export function readGradebookSetup(page) {
|
|
89
|
+
return page.evaluate((codes) => {
|
|
90
|
+
const doc = globalThis.document;
|
|
91
|
+
const names = Object.fromEntries(Object.entries(codes).map(([name, code]) => [String(code), name]));
|
|
92
|
+
const categories = [];
|
|
93
|
+
const items = [];
|
|
94
|
+
for (const row of Array.from(doc.querySelectorAll("#grade_edit_tree_table tr[data-itemid]"))) {
|
|
95
|
+
const itemId = Number(row.getAttribute("data-itemid"));
|
|
96
|
+
const name = (row.querySelector(".rowtitle")?.textContent ?? "").replace(/\s+/g, " ").trim();
|
|
97
|
+
const parent = Number((row.getAttribute("data-parent-category") ?? "").replace(/^cg/, "")) || undefined;
|
|
98
|
+
const max = Number(row.getAttribute("data-grademax"));
|
|
99
|
+
const weightField = row.querySelector(`input[type="text"][name="weight_${itemId}"]`);
|
|
100
|
+
const weight = weightField && weightField.value !== "" ? Number(weightField.value) : undefined;
|
|
101
|
+
const category = row.getAttribute("data-category");
|
|
102
|
+
if (category) {
|
|
103
|
+
const aggregation = names[row.getAttribute("data-aggregation") ?? ""];
|
|
104
|
+
categories.push({
|
|
105
|
+
id: Number(category.replace(/^cg/, "")),
|
|
106
|
+
itemId,
|
|
107
|
+
name,
|
|
108
|
+
...(parent ? { parentId: parent } : {}),
|
|
109
|
+
...(aggregation ? { aggregation: aggregation } : {}),
|
|
110
|
+
...(max > 0 ? { maxGrade: max } : {}),
|
|
111
|
+
...(weight !== undefined ? { weight } : {}),
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
// The item's icon comes from its module's theme folder, or core's for a manual item.
|
|
116
|
+
const icon = /image\.php\/[^/]+\/([a-z0-9_]+)\//.exec(row.querySelector("img.itemicon")?.getAttribute("src") ?? "")?.[1];
|
|
117
|
+
items.push({
|
|
118
|
+
id: itemId,
|
|
119
|
+
name,
|
|
120
|
+
...(parent ? { categoryId: parent } : {}),
|
|
121
|
+
type: !icon || icon === "core" ? "manual" : icon,
|
|
122
|
+
...(max > 0 ? { maxGrade: max } : {}),
|
|
123
|
+
...(weight !== undefined ? { weight } : {}),
|
|
124
|
+
weightOverridden: row.querySelector(`input[type="checkbox"][name="weightoverride_${itemId}"]`)?.checked ?? false,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return { categories, items };
|
|
129
|
+
}, AGGREGATION_CODES);
|
|
130
|
+
}
|
|
131
|
+
/** A course's gradebook setup: its categories (aggregation, weights) and grade items (category, maximum, weight). */
|
|
132
|
+
export async function gradebookSetup(context, siteUrl, courseId) {
|
|
133
|
+
const page = await openSitePage(context, siteUrl, `grade/edit/tree/index.php?id=${courseId}`);
|
|
134
|
+
try {
|
|
135
|
+
if ((await page.locator("body#page-grade-edit-tree-index #grade_edit_tree_table").count()) === 0) {
|
|
136
|
+
throw new GradebookError(`You can't see course ${courseId}'s gradebook setup: ${(await moodleErrorMessage(page)) ?? "the course doesn't exist, or you aren't allowed to manage its grades"}`);
|
|
137
|
+
}
|
|
138
|
+
const setup = await readGradebookSetup(page);
|
|
139
|
+
// Category and course totals are their categories' own items, not items of their own.
|
|
140
|
+
const totals = new Set(setup.categories.map((c) => c.itemId));
|
|
141
|
+
return { categories: setup.categories, items: setup.items.filter((item) => !totals.has(item.id)) };
|
|
142
|
+
}
|
|
143
|
+
finally {
|
|
144
|
+
await page.close().catch(() => undefined);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Fills one of Moodle's dynamic forms (core_form_dynamic_form) as its modal does: rendered by the
|
|
149
|
+
* service, filled in a blank tab, serialized and sent back. Throws `GradebookError` with Moodle's
|
|
150
|
+
* messages when it comes back unsubmitted; `prepare` can check the form before it's filled.
|
|
151
|
+
*/
|
|
152
|
+
async function submitDynamicForm(context, siteUrl, form, params, values, prepare) {
|
|
153
|
+
const formdata = new URLSearchParams(Object.entries(params).map(([key, value]) => [key, String(value)])).toString();
|
|
154
|
+
const rendered = await callService(context, siteUrl, "core_form_dynamic_form", { form, formdata });
|
|
155
|
+
const page = await context.newPage();
|
|
156
|
+
try {
|
|
157
|
+
await page.setContent(rendered.html);
|
|
158
|
+
await prepare?.(page);
|
|
159
|
+
await setFields(page, values);
|
|
160
|
+
const serialized = await page.evaluate(() => {
|
|
161
|
+
const g = globalThis;
|
|
162
|
+
return new g.URLSearchParams(new g.FormData(g.document.querySelector("form.mform"))).toString();
|
|
163
|
+
});
|
|
164
|
+
const answer = await callService(context, siteUrl, "core_form_dynamic_form", { form, formdata: serialized });
|
|
165
|
+
if (answer.submitted)
|
|
166
|
+
return;
|
|
167
|
+
// Back unsubmitted: the form again, with its errors (ids carry a random suffix).
|
|
168
|
+
await page.setContent(answer.html);
|
|
169
|
+
const errors = await page.evaluate(() => Array.from(globalThis.document.querySelectorAll('[id^="id_error_"]'))
|
|
170
|
+
.map((element) => ({ field: element.id.slice("id_error_".length).replace(/_[A-Za-z0-9]{15}$/, ""), message: (element.textContent ?? "").replace(/\s+/g, " ").trim() }))
|
|
171
|
+
.filter((error) => error.message !== ""));
|
|
172
|
+
throw new GradebookError(`Moodle refused the form: ${errors.map((e) => `${e.field}: ${e.message}`).join("; ") || "no reason given"}.`);
|
|
173
|
+
}
|
|
174
|
+
finally {
|
|
175
|
+
await page.close().catch(() => undefined);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/** Moves a grade item (`ig<id>`) or category (`cg<id>`) to the start of a category, as the setup page's "Move" does. */
|
|
179
|
+
async function moveIntoCategory(context, siteUrl, courseId, element, categoryId) {
|
|
180
|
+
const page = await openSitePage(context, siteUrl, `grade/edit/tree/index.php?id=${courseId}`);
|
|
181
|
+
try {
|
|
182
|
+
const sesskey = await page.evaluate(() => globalThis.M.cfg.sesskey);
|
|
183
|
+
const move = `grade/edit/tree/index.php?id=${courseId}&action=move&eid=${element}&moveafter=cg${categoryId}&first=1&sesskey=${sesskey}`;
|
|
184
|
+
await page.goto(new URL(move, siteUrl).href, { waitUntil: "domcontentloaded" });
|
|
185
|
+
if ((await page.locator("#grade_edit_tree_table").count()) === 0) {
|
|
186
|
+
throw new GradebookError(`Moodle didn't move ${element.startsWith("cg") ? "category" : "item"} ${element.slice(2)} into category ${categoryId}: check both ids in the gradebook setup.`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
finally {
|
|
190
|
+
await page.close().catch(() => undefined);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Changes a grade item: moves it into a category, sets its weight (percent, overriding Natural's
|
|
195
|
+
* automatic one), or its maximum grade (a manual item's; an activity's is set in the activity).
|
|
196
|
+
* Returns the item afterwards.
|
|
197
|
+
*/
|
|
198
|
+
export async function updateGradeItem(context, siteUrl, courseId, itemId, { category, weight, maxGrade }) {
|
|
199
|
+
if (category !== undefined)
|
|
200
|
+
await moveIntoCategory(context, siteUrl, courseId, `ig${itemId}`, category);
|
|
201
|
+
if (weight !== undefined || maxGrade !== undefined) {
|
|
202
|
+
const values = {};
|
|
203
|
+
if (weight !== undefined)
|
|
204
|
+
Object.assign(values, { weightoverride: 1, aggregationcoef2: weight });
|
|
205
|
+
if (maxGrade !== undefined)
|
|
206
|
+
values.grademax = maxGrade;
|
|
207
|
+
await submitDynamicForm(context, siteUrl, "core_grades\\form\\add_item", { courseid: courseId, itemid: itemId }, values, async (page) => {
|
|
208
|
+
const fields = await page.evaluate(() => {
|
|
209
|
+
const doc = globalThis.document;
|
|
210
|
+
const max = doc.querySelector('[name="grademax"]');
|
|
211
|
+
return { maxEditable: max !== null && !max.disabled, weight: doc.querySelector('[name="aggregationcoef2"]') !== null };
|
|
212
|
+
});
|
|
213
|
+
if (maxGrade !== undefined && !fields.maxEditable) {
|
|
214
|
+
throw new GradebookError(`Item ${itemId} belongs to an activity: its maximum grade is set in the activity's settings, not in the gradebook.`);
|
|
215
|
+
}
|
|
216
|
+
if (weight !== undefined && !fields.weight)
|
|
217
|
+
throw new GradebookError(`Item ${itemId}'s category doesn't aggregate with weights that can be set (Natural): change its aggregation first.`);
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
const done = [category !== undefined && `moved into category ${category}`, weight !== undefined && `weighted ${weight}%`, maxGrade !== undefined && `out of ${maxGrade}`].filter(Boolean);
|
|
221
|
+
const item = (await gradebookSetup(context, siteUrl, courseId)).items.find((i) => i.id === itemId);
|
|
222
|
+
return { changed: done.length > 0 ? `Grade item ${itemId} ${done.join(", ")}.` : "Nothing to change.", item };
|
|
223
|
+
}
|
|
224
|
+
/** Adds a grade category (at the course's top level, or inside `parentId`); returns it as the setup shows it. */
|
|
225
|
+
export async function addGradeCategory(context, siteUrl, courseId, { name, aggregation, parentId }) {
|
|
226
|
+
const before = new Set((await gradebookSetup(context, siteUrl, courseId)).categories.map((c) => c.id));
|
|
227
|
+
const values = { fullname: name };
|
|
228
|
+
if (aggregation !== undefined)
|
|
229
|
+
values.aggregation = AGGREGATION_CODES[aggregation];
|
|
230
|
+
await submitDynamicForm(context, siteUrl, "core_grades\\form\\add_category", { courseid: courseId }, values, async (page) => {
|
|
231
|
+
if (aggregation === undefined)
|
|
232
|
+
return;
|
|
233
|
+
const offered = await page.evaluate(() => Array.from(globalThis.document.querySelectorAll('[name="aggregation"] option')).map((o) => o.value));
|
|
234
|
+
if (!offered.includes(String(AGGREGATION_CODES[aggregation]))) {
|
|
235
|
+
const available = GRADE_AGGREGATIONS.filter((a) => offered.includes(String(AGGREGATION_CODES[a])));
|
|
236
|
+
throw new GradebookError(`The site doesn't offer the ${aggregation} aggregation; it offers: ${available.join(", ")}.`);
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
let created = (await gradebookSetup(context, siteUrl, courseId)).categories.find((c) => !before.has(c.id));
|
|
240
|
+
if (created && parentId !== undefined) {
|
|
241
|
+
await moveIntoCategory(context, siteUrl, courseId, `cg${created.id}`, parentId);
|
|
242
|
+
created = (await gradebookSetup(context, siteUrl, courseId)).categories.find((c) => c.id === created.id);
|
|
243
|
+
}
|
|
244
|
+
return { changed: `Grade category ${created?.id ?? "?"} "${name}" added${parentId !== undefined ? ` in category ${parentId}` : ""}.`, category: created };
|
|
245
|
+
}
|
|
246
|
+
//# sourceMappingURL=gradebook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gradebook.js","sourceRoot":"","sources":["../../src/moodle/gradebook.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { BrowserContext, Page } from "playwright-core";
|
|
2
|
+
/** A row of the user grade report, values as Moodle shows them ("-" left out). */
|
|
3
|
+
export interface GradeRow {
|
|
4
|
+
/** A category's total row is "categoryTotal"; the course's, "courseTotal". */
|
|
5
|
+
kind: "item" | "category" | "categoryTotal" | "courseTotal";
|
|
6
|
+
name: string;
|
|
7
|
+
activityId?: number;
|
|
8
|
+
activityType?: string;
|
|
9
|
+
grade?: string;
|
|
10
|
+
range?: string;
|
|
11
|
+
percentage?: string;
|
|
12
|
+
weight?: string;
|
|
13
|
+
letter?: string;
|
|
14
|
+
feedback?: string;
|
|
15
|
+
contribution?: string;
|
|
16
|
+
}
|
|
17
|
+
/** The person can't see the grade report of that course. */
|
|
18
|
+
export declare class NoGradeReportError extends Error {
|
|
19
|
+
name: string;
|
|
20
|
+
}
|
|
21
|
+
/** The rows of `table.user-grade`, by cell classes, never by header texts (ADR-004). */
|
|
22
|
+
export declare function readGradeRows(page: Page): Promise<GradeRow[]>;
|
|
23
|
+
/** The person's own grade report for a course. */
|
|
24
|
+
export declare function myGrades(context: BrowserContext, siteUrl: string, courseId: number): Promise<GradeRow[]>;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { openSitePage } from "./session.js";
|
|
2
|
+
/** The person can't see the grade report of that course. */
|
|
3
|
+
export class NoGradeReportError extends Error {
|
|
4
|
+
name = "NoGradeReportError";
|
|
5
|
+
}
|
|
6
|
+
/** The rows of `table.user-grade`, by cell classes, never by header texts (ADR-004). */
|
|
7
|
+
export function readGradeRows(page) {
|
|
8
|
+
return page.evaluate(() => {
|
|
9
|
+
const doc = globalThis.document;
|
|
10
|
+
const rows = Array.from(doc.querySelectorAll("table.user-grade tr"));
|
|
11
|
+
const result = [];
|
|
12
|
+
for (const row of rows) {
|
|
13
|
+
const nameCell = row.querySelector(".column-itemname");
|
|
14
|
+
if (!nameCell || row.querySelector("th.column-itemname[scope='col'], thead"))
|
|
15
|
+
continue;
|
|
16
|
+
const classes = nameCell.className;
|
|
17
|
+
// Both totals are "baggt" rows; a category's holds a categoryitem block (confirmed empirically, Moodle 5.2).
|
|
18
|
+
const total = nameCell.querySelector(".categoryitem") ? "categoryTotal" : "courseTotal";
|
|
19
|
+
const kind = /\bbaggt\b/.test(classes) ? total : /\bcategory\b/.test(classes) ? "category" : /\bitem\b/.test(classes) ? "item" : undefined;
|
|
20
|
+
if (!kind)
|
|
21
|
+
continue;
|
|
22
|
+
const values = {};
|
|
23
|
+
for (const column of ["grade", "range", "percentage", "weight", "lettergrade", "feedback", "contributiontocoursetotal"]) {
|
|
24
|
+
const cell = row.querySelector(`.column-${column}`);
|
|
25
|
+
if (!cell)
|
|
26
|
+
continue;
|
|
27
|
+
// The grade cell also holds an action menu whose text is in the site's language.
|
|
28
|
+
const copy = cell.cloneNode(true);
|
|
29
|
+
for (const menu of Array.from(copy.querySelectorAll(".action-menu, .dropdown, .visually-hidden")))
|
|
30
|
+
menu.remove();
|
|
31
|
+
const text = (copy.textContent ?? "").replace(/\s+/g, " ").trim();
|
|
32
|
+
values[column] = text === "" || text === "-" ? undefined : text;
|
|
33
|
+
}
|
|
34
|
+
const link = nameCell.querySelector('a[href*="/mod/"][href*="view.php"]');
|
|
35
|
+
const href = link?.getAttribute("href") ?? "";
|
|
36
|
+
const match = /\/mod\/([a-z0-9_]+)\/view\.php\?(?:.*&)?id=(\d+)/.exec(href);
|
|
37
|
+
result.push({
|
|
38
|
+
kind,
|
|
39
|
+
name: ((nameCell.querySelector(".rowtitle") ?? nameCell).textContent ?? "").replace(/\s+/g, " ").trim(),
|
|
40
|
+
activityId: match ? Number(match[2]) : undefined,
|
|
41
|
+
activityType: match ? match[1] : undefined,
|
|
42
|
+
grade: values.grade,
|
|
43
|
+
range: values.range,
|
|
44
|
+
percentage: values.percentage,
|
|
45
|
+
weight: values.weight,
|
|
46
|
+
letter: values.lettergrade,
|
|
47
|
+
feedback: values.feedback,
|
|
48
|
+
contribution: values.contributiontocoursetotal,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return result;
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/** The person's own grade report for a course. */
|
|
55
|
+
export async function myGrades(context, siteUrl, courseId) {
|
|
56
|
+
const page = await openSitePage(context, siteUrl, `grade/report/user/index.php?id=${courseId}`);
|
|
57
|
+
try {
|
|
58
|
+
if ((await page.locator("body#page-grade-report-user-index table.user-grade").count()) === 0) {
|
|
59
|
+
throw new NoGradeReportError(`There's no grade report of yours to read in course ${courseId}: the course doesn't exist, you aren't graded there, or grades are hidden.`);
|
|
60
|
+
}
|
|
61
|
+
return await readGradeRows(page);
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
await page.close().catch(() => undefined);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=grades.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grades.js","sourceRoot":"","sources":["../../src/moodle/grades.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { BrowserContext, Page } from "playwright-core";
|
|
2
|
+
import { type PluginContent } from "./assign.js";
|
|
3
|
+
import { type FieldValue } from "./forms.js";
|
|
4
|
+
export interface Submission {
|
|
5
|
+
userId: number;
|
|
6
|
+
fullName: string;
|
|
7
|
+
/** Moodle's status: "submitted", "draft", "new", "reopened", or "none" without a submission. */
|
|
8
|
+
status: string;
|
|
9
|
+
submitted: boolean;
|
|
10
|
+
needsGrading: boolean;
|
|
11
|
+
extensionGranted: boolean;
|
|
12
|
+
/** Until when, where an extension is granted. */
|
|
13
|
+
extensionDueDate?: string;
|
|
14
|
+
allowSubmissionsFrom?: string;
|
|
15
|
+
/** For this participant: overrides and extensions included. */
|
|
16
|
+
dueDate?: string;
|
|
17
|
+
cutoffDate?: string;
|
|
18
|
+
/** Per submission plugin; only for submitted and draft submissions. */
|
|
19
|
+
content?: PluginContent[];
|
|
20
|
+
}
|
|
21
|
+
/** Every participant's submission to an assignment, with its content unless `includeContent` is false. */
|
|
22
|
+
export declare function assignmentSubmissions(context: BrowserContext, siteUrl: string, cmid: number, { includeContent }?: {
|
|
23
|
+
includeContent?: boolean | undefined;
|
|
24
|
+
}): Promise<{
|
|
25
|
+
assignment: {
|
|
26
|
+
id: number;
|
|
27
|
+
instanceId: number;
|
|
28
|
+
};
|
|
29
|
+
submissions: Submission[];
|
|
30
|
+
}>;
|
|
31
|
+
/**
|
|
32
|
+
* Grades a participant as the grader does: a grade (simple grading), a rubric's level per criterion
|
|
33
|
+
* or a marking guide's score per criterion (by their ids, from `rubric()`), remarks per criterion,
|
|
34
|
+
* feedback comments (HTML), whether to notify, and any other grading field by name. Returns the
|
|
35
|
+
* grade saved and the gradebook's grade, the total Moodle computes for a rubric or a guide.
|
|
36
|
+
*/
|
|
37
|
+
export declare function gradeSubmission(context: BrowserContext, siteUrl: string, cmid: number, userId: number, { grade, rubric, guide, remarks, feedback, notify, fields, }: {
|
|
38
|
+
grade?: string;
|
|
39
|
+
/** Criterion id → level id. */
|
|
40
|
+
rubric?: Record<number, number>;
|
|
41
|
+
/** Criterion id → score. */
|
|
42
|
+
guide?: Record<number, number>;
|
|
43
|
+
/** Criterion id → remark, for a rubric or a guide. */
|
|
44
|
+
remarks?: Record<number, string>;
|
|
45
|
+
feedback?: string;
|
|
46
|
+
notify?: boolean;
|
|
47
|
+
fields?: Record<string, FieldValue>;
|
|
48
|
+
}): Promise<{
|
|
49
|
+
grade?: string;
|
|
50
|
+
gradebookGrade?: string;
|
|
51
|
+
feedback?: string;
|
|
52
|
+
changed: string;
|
|
53
|
+
}>;
|
|
54
|
+
export interface GradingCriterion {
|
|
55
|
+
id: number;
|
|
56
|
+
/** A rubric criterion's description; a guide criterion's short name. */
|
|
57
|
+
description: string;
|
|
58
|
+
/** A guide criterion's description for students and for markers. */
|
|
59
|
+
details?: string;
|
|
60
|
+
markers?: string;
|
|
61
|
+
/** A rubric criterion's levels, by the ids grading takes. */
|
|
62
|
+
levels?: {
|
|
63
|
+
id: number;
|
|
64
|
+
definition: string;
|
|
65
|
+
score: number;
|
|
66
|
+
}[];
|
|
67
|
+
/** A guide criterion's maximum score. */
|
|
68
|
+
maxScore?: number;
|
|
69
|
+
}
|
|
70
|
+
export interface GradingDefinition {
|
|
71
|
+
/** "simple" for a plain grade (or a scale), "rubric" or "guide" for advanced grading. */
|
|
72
|
+
method: "simple" | "rubric" | "guide";
|
|
73
|
+
criteria: GradingCriterion[];
|
|
74
|
+
}
|
|
75
|
+
/** The grading method and its criteria, read from a grading panel's form by field names and blocks. */
|
|
76
|
+
export declare function readGradingDefinition(page: Page, panelHtml: string): Promise<GradingDefinition>;
|
|
77
|
+
/**
|
|
78
|
+
* An assignment's grading method and, for a rubric or a marking guide, its criteria with the ids
|
|
79
|
+
* `gradeSubmission` takes: read from the grader's panel for its first participant.
|
|
80
|
+
*/
|
|
81
|
+
export declare function rubric(context: BrowserContext, siteUrl: string, cmid: number): Promise<{
|
|
82
|
+
assignment: {
|
|
83
|
+
id: number;
|
|
84
|
+
};
|
|
85
|
+
} & GradingDefinition>;
|
|
86
|
+
/**
|
|
87
|
+
* Grants participants an extension until a date, through the assignment's extension form (several at
|
|
88
|
+
* once as its bulk action does). Moodle refuses a date before the assignment's due date.
|
|
89
|
+
*/
|
|
90
|
+
export declare function grantExtension(context: BrowserContext, siteUrl: string, cmid: number, userIds: number[], until: Date): Promise<{
|
|
91
|
+
changed: string;
|
|
92
|
+
}>;
|