Skip to content

Releases: falkenslab/moodle-driver

v0.8.0

Choose a tag to compare

@fvarrui fvarrui released this 14 Oct 00:09

New

  • Back up part of a course (#57). backupCourse(courseId, destDir, { sections, activities }) backs up only the given sections (with everything in them) and activities or resources, without people's data or the course's own parts (groups, blocks, badges, events…); the driver reads the file back and refuses it if it holds more than asked.
  • Restore it at a chosen place (#57). restoreCourse into an existing course returns the new sectionIds and activityIds, and takes sectionId (every restored activity into that section) or afterSectionId (the backup's sections as new sections after that one). Before the wizard, a backup from a newer Moodle, or with activity types the site doesn't offer, is refused instead of being restored partly and silently.
  • Know what a backup holds (#58). backupContents(file) reads a .mbz locally, with no browser or session: type, Moodle version, origin, sections, activities, plugins needed, whether it has users' data, each activity's dependencies outside the backup (restrictions, links, grade category), and what in it still identifies a person or a site (identifies). Restoring into an existing course also returns warnings naming the restrictions and links left broken.
  • Anonymous backups (#59). anonymizeBackup(file, destFile) writes a copy of a backup that names no person (user ids, authors, creators, modifiers…) and no site or course of origin, leaving content, settings and files as they are. A backup of part of a course is anonymous by default (anonymous: false keeps the metadata). See the backup page for what a neutral origin changes in a restore.
  • The moodle command gains backup-contents and anonymize-backup, which need no site or profile.
  • Several sites in one browser: documented in the browser concepts page (one profile can hold sessions for several sites; two accounts on one site need separate profiles).

Fixes

  • startQuizAttempt starts a quiz with a time limit (#56): it confirms Moodle's preflight dialog instead of waiting for a navigation that never comes, and also handles quizzes that open the attempt in a secure pop-up window. A quiz with a password is refused with a clear QuizError, and Moodle's own refusal message now reaches the error.

Internal

  • BackupError moved to the shared errors module; it's still exported the same way.
  • The .mbz reading and writing uses Node's zlib with a small tar reader and writer: no new dependency.

v0.7.0

Choose a tag to compare

@fvarrui fvarrui released this 12 Oct 20:35

New

  • Long lists a page at a time (#55). notifications, conversations, conversation, forumDiscussions, discussion, assignmentSubmissions, quizResults, questions, participants and courseLogs take limit and offset. With limit, the list comes as { items, more } (ListPage); without it, the same plain list as before, and TypeScript gives the right type for each call. The driver asks Moodle only for the page wherever Moodle can page (services' limits, a page's own paging); the docs say where it can't. The moodle command takes --limit and --offset and says when there are more. See Long lists.
  • notifications({ unreadOnly: true, limit }) pages over unread notifications.
  • assignmentSubmissions, graderReport and completionReport take userIds, to read only some participants.
  • New types: ListPage, PageOptions, NotificationOptions, SubmissionOptions, ParticipantOptions, CourseLogOptions, QuizResultsOptions and the results of the functions above.

Fixes

  • assignmentSubmissions no longer creates empty submissions in Moodle just by reading (#54). Moodle's grading panel creates one for a participant who has none, so the panel is now opened only for participants with a submission; those without one come back without feedback files, workflow state or markers. rubric, allocateMarker and setWorkflowState read the panel of a participant who submitted, where there is one. What reading still writes (a "not graded" grade for a submitted, ungraded participant, as Moodle's own grader does) is said in the docs.

Breaking changes

  • courseLogs(courseId, { limit }) now returns { items, more } instead of a list, like every paged function. Read .items; more says whether to ask again with offset. Called without limit, it still returns a list (its default of 100 entries stays).

The Moodle sandbox regression suite passed for the touched areas (messages, forums, assignments, lifecycle).

v0.6.0

Choose a tag to compare

@fvarrui fvarrui released this 12 Oct 16:22

New

  • Images and files embedded in rich text (#52): an embed option (local paths) on createActivity, updateActivity, addSection, editSection, startDiscussion, replyToPost, editPost and updateCourse. The HTML names each file by its name (<img src="shot.png">); the driver uploads it where Moodle's editor would and rewrites the reference, so the image shows to everyone. See Embedding files in rich text.
  • activityContent (pages, labels, books) and forum posts give the images they show.
  • Forum subscriptions (#53): subscribed in forumDiscussions and discussion; startDiscussion returns the resulting pinned and subscribed, and replyToPost subscribed when asked to subscribe; new setDiscussionSubscription (also moodle set-discussion-subscription).

Fixes

  • courseStructure: availableToMe is true for a teacher on a hidden or restricted activity they can open (#50).
  • setEnrolmentMethod switches a method on or off as Moodle's methods page does, and refuses with a clear EnrolmentError what Moodle doesn't let the person change (a teacher on the manual method), instead of failing on a missing form field; a request for the state a method already has succeeds (#51).

Changes a program may notice

  • uploadToDraft (internals) returns the draft URLs of the files it uploaded.
  • readImages (internals) and the ImageItem type are new.

Checked against the Moodle 5.2 sandbox: the courses, building, forums and participants files of the regression suite pass (88 tests).

v0.5.1

Choose a tag to compare

@fvarrui fvarrui released this 12 Oct 12:54

Changes

  • The Moodle object's methods are documented one by one. Each method (moodle.listCourses(), moodle.gradeSubmission()…) now shows its real signature and its function's doc comment, in the API reference and when an editor hovers over it. Before, the reference showed them as (...rest) => Promise with no description, and editors lost the doc comment. No behaviour changes: the types are the same, written out.
  • submitSurvey as a method now offers both its signatures, as the function does (it only offered the last one).
  • QuizChange, the result of the quiz structure functions (editQuizSlot, removeQuizSlot, setQuizPages, addQuizSection), is exported.

v0.5.0

Choose a tag to compare

@fvarrui fvarrui released this 12 Oct 12:28

Managing a course in full: everything a teacher does to run a course, beyond reading it.

New

People (participants)

  • Enrol, unenrol and set roles: potentialUsers, enrolUsers, unenrolUser, setUserRoles (#28).
  • Groups and groupings with their members: createGroup, updateGroup, deleteGroup, setGroupMembers, groupings, createGrouping, deleteGrouping, setGroupingGroups (#29).
  • Enrolment methods (self enrolment with a key, guest): enrolmentMethods, setEnrolmentMethod (#44).
  • One message to several participants: messageParticipants (#47).

Building a course (activities)

  • Replace, add or remove a file resource's or folder's files: updateActivity(id, { files: { add, remove, replace } }) (#31).
  • Sections' descriptions: summary in addSection, editSection and courseStructure({ details }) (#32).
  • Access restrictions and completion conditions in plain terms: restrictions and completion in createActivity, updateActivity, editSection and activitySettings (#33).
  • Copy, back up, restore and import courses: copyCourse, backupCourse, restoreCourse, importActivities (#42); reset one: resetCourse (#43).

Quizzes and question banks

  • Grade by hand (essays): questionsToGrade, gradeQuizQuestion (#30).
  • A quiz's structure: editQuizSlot, removeQuizSlot, setQuizPages, addQuizSection (#34).
  • Edit and delete bank questions: updateQuestion, deleteQuestions, setQuestionStatus (#35).

Grading and grades

  • The gradebook: setGrade (override, exclude, manual items), addGradeItem, updateGradeCategory, deleteGradeCategory, deleteGradeItem (#37); export: exportGrades (#38).
  • Whole-forum grading and ratings: forumGrades, gradeForumUser, rate (#46); workshops: workshopInfo, setWorkshopPhase, assessWorkshopSubmission (#48).
  • Assignments: feedback files (gradeSubmission's feedbackFiles) (#40); group submissions and the marking workflow: setWorkflowState, allocateMarker, workflowState/applyToGroup in gradeSubmission (#41).

Forums, calendar, reports

  • Edit and delete posts: editPost, deletePost (#39).
  • Change an event: updateEvent, calendarEvent (#36).
  • Logs and reports: courseLogs, activityReport, participationReport (#45).

The moodle command (command line)

  • moodle shell: commands typed one after another on one browser (#24).
  • moodle open / moodle close: a background browser separate commands share, behind a token (ADR-014); for programs, connectBrowserManager and backgroundBrowser (#25).
  • Every new function is also a command (132 in all).

New error classes, with their codes: EnrolmentError, ForumError, ReportError, BackupError, WorkshopError, and the generic background-browser and browser-busy.

Fixes

  • Security: when a request to Moodle timed out, the error's message carried the session cookie and the sesskey (Playwright lists the request's headers). Errors now say [redacted] instead, so they can be logged as they are.
  • setFields (and so every form with a rich text editor, such as forum posts) raced TinyMCE: content set while the editor was still starting threw. It now waits for the editor.
  • userById gives undefined for a deleted account, which Moodle still returns to an administrator, renamed.
  • createEvent saved a course event with the course's first group, which hid it from the teacher's upcoming events.
  • createActivity refused an H5P activity's package, and gave no activityId for a question bank.
  • h5pActivity said a student's tracked activity wasn't tracked before their first attempt.
  • getChoice, answerChoice and groupings took Moodle's error page for an empty result; they now throw with Moodle's reason.
  • importQuestions into a given category failed on a field Moodle disables.
  • rate found no rating menu for glossary entries and database records.
  • allocateMarker and setWorkflowState now refuse an assignment without marker allocation or the marking workflow; grantExtension and submitForGrading throw when Moodle refuses, instead of reporting success.

All found by a new regression suite that runs every exported function against a real Moodle 5.2 (#49): npm run test:sandbox, on demand.

Changes a program may notice

  • QuizSlot has slot (its place in the quiz), and quizStructure and addQuestionsToQuiz return sections.
  • dateSelectorValues takes an optional time zone; uploadToDraft takes { from, to } items and { overwrite }.
  • CourseChange may carry the activity's files; Question has comment; Submission has feedbackFiles, group, workflowState and markers.

v0.4.1

Choose a tag to compare

@fvarrui fvarrui released this 11 Oct 16:08

Fixes

  • conversation() gives as members everyone in the conversation but the person, whether they wrote in it or not, as conversations() does (the person alone in their own conversation). It used to give only those who had written one of the messages, so someone who had only received messages was missing (#27).

v0.4.0

Choose a tag to compare

@fvarrui fvarrui released this 11 Oct 15:21

New

SCORM activities (docs):

  • scorm(activityId): a SCORM as the person sees it: attempts allowed and made, grading method, the grade reported and each attempt's, and its SCOs with the person's status (not-attempted, incomplete, completed, passed, failed…) and score. Never starts an attempt. Also moodle scorm <id>.
  • openScorm(activityId, { scoId?, newAttempt? }): opens Moodle's player on the working tab, continuing the last attempt or starting a new one, and resolves once the SCO has loaded; returns the attempt, the SCO, the SCORM version and the URL.
  • createActivity({ type: "scorm", files: [package] }) creates a SCORM from its package.
  • ScormError, code scorm.

The driver never reports a SCORM's results for the person: they come only from doing it in the player.

Internal

  • npm publish runs typecheck, lint, the docs' examples and the tests in Docker first.

v0.3.1

Choose a tag to compare

@fvarrui fvarrui released this 11 Oct 11:11

A fix for the moodle command.

Install

npm i -g https://github.com/falkenslab/moodle-driver/releases/download/v0.3.1/falkenslab-moodle-driver-0.3.1.tgz   # the moodle command
npm install https://github.com/falkenslab/moodle-driver/releases/download/v0.3.1/falkenslab-moodle-driver-0.3.1.tgz playwright-core   # the library

Fixes

  • moodle public-site-info needs no profile: it asks what anyone can, without a browser, and 0.3.0 refused it on a site with no login yet.

No changes to the library's API. See v0.3.0 for what's new in 0.3.

v0.3.0

Choose a tag to compare

@fvarrui fvarrui released this 11 Oct 10:58

A command for the whole API, Moodle's services over plain HTTP, and a friendlier API. Several changes break 0.2 programs: see Breaking changes.

Install

Not on npm yet: the package is attached to this release, built.

npm i -g https://github.com/falkenslab/moodle-driver/releases/download/v0.3.0/falkenslab-moodle-driver-0.3.0.tgz   # the moodle command
npm install https://github.com/falkenslab/moodle-driver/releases/download/v0.3.0/falkenslab-moodle-driver-0.3.0.tgz playwright-core   # the library

New

  • The moodle command. Every method of the Moodle object from a terminal: moodle login --site … once, then moodle list-courses, moodle my-grades 3 --json, moodle grade-submission 35 7 --grade 8.5 --notify… 81 commands generated from the API's types, so none is missing; tables at a terminal, JSON for scripts, errors with their code and exit status, --allow to limit the data categories, --show to watch the browser. See the command line and the commands.
  • Sites and accounts. The command keeps everything in ~/.moodle-driver on every system: a folder per site, a named profile per account (moodle login --as student), the site's default in its site.json; a first login names its profile after the Moodle username. moodle sites lists them, moodle forget deletes one. siteProfileDir(site) lets a program share the command's sessions.
  • Moodle's services over HTTP. callService posts to lib/ajax/service.php with the profile's session and its sesskey, without a tab; the kept service tab is gone. Pages and forms stay in tabs: measured, Moodle's server takes most of their time (ADR-011). fetchSitePage, readMoodleConfig and htmlSessionFacts in /internals read a page over HTTP.
  • Friendlier calls: activityContent(activity) takes an activity as the course structure gives it; deadlines() and logIn() need no options object; gradeSubmission takes a numeric grade; the Moodle object works with await using.
  • Documentation: 17 complete, commented use-case examples (student, teacher, programs), a glossary of Moodle's terms, diagrams, a new logo, and every example compiled against the code (npm run docs:check, also in CI). The site now lives at https://falkenslab.github.io/moodle-driver/ (no /docs).

Breaking changes

Was (0.2) Now
MoodleDriverError MoodleError (same codes and subclasses)
moodle-driver-session.json in the profile moodle-session.json: profiles from 0.2 log in once more
courseStructure(id) → { course, sections } the sections, CourseSection[]
assignmentSubmissions(id) → { assignment, submissions } the submissions, Submission[]
callService from a kept tab over HTTP; nothing to change in calls

Fixes

  • The Moodle object's answerQuizQuestions is typed with changed, which the function returned already.
  • pause() no longer reports the same second twice when a timer fires a millisecond early.

Internal

  • ADR-011 (services over HTTP, pages in tabs, with the timings), ADR-012 (the command in the package), ADR-013 (one home for sites and sessions).
  • scripts/benchmark-reads.mts measures reads on any site; scripts/generate-cli-commands.mjs keeps the command table in step with the types.

Documentation: https://falkenslab.github.io/moodle-driver/

v0.2.0

Choose a tag to compare

@fvarrui fvarrui released this 10 Oct 10:06

A release that settles the shape of the API before more programs depend on it. Most of it is breaking: read Breaking changes before upgrading.

New

  • The Moodle object's defaults. createMoodle() takes trail, onProgress, loginTimeoutMs and downloadDir, so they aren't repeated on every call; any call can still give its own. logIn() needs no arguments (5 minutes by default).
  • Surveys find their module. getSurvey(activityId) and submitSurvey(activityId, responses) work out whether it's a feedback or a questionnaire when not told ({ type } still accepted).
  • submitForGrading(activityId, { acceptStatement }), an options object instead of a bare boolean (still accepted).
  • MoodleDriverError. Every error the driver throws extends it and carries a stable code (not-logged-in, survey, form…, and generic ones: invalid-input, not-found, not-available, outside-site, unexpected-page, logout-failed, chromium-install-failed). Match errors by class, name or code, never by message.
  • @falkenslab/moodle-driver/internals, a second entry point with the primitives the operations are built from: callService, readPageConfig, openSitePage, readSessionFacts, pageIsLoggedIn, moodleErrorMessage, openForm, formValue, setFields, submitForm, uploadToDraft, dateSelectorValues, dateSelectorDate, inPage, readTexts, readMedia, decodeEntities, readingSeconds, pause, editCourse, renameActivity, renameSection, courseState, activityDetails, readOwnProfile, readWwwroot, fileNameFor.
  • moodle.readAttempt(attemptId), now a method too.

Breaking changes

The main entry is curated. It exports the Moodle object, the operations, their types and errors. The primitives moved to @falkenslab/moodle-driver/internals (list above): change the import. The areas' parsers and single steps are no longer exported at all: readQuizView, readQuestions, readNavigation, startAttempt, saveAnswers, finishAttempt, readQuizReport, readQuizSlots, assignmentIds, readAssignmentIds, listParticipants, participantDates, gradingPanelHtml, fillGradingForm, panelGrade, submitGradingForm, readSubmissionContent, readGradingDefinition, readStudentAssignment, listDiscussions, discussionPosts, forumInstanceId, readDiscussionRows, structureOf, assignmentsOf, toCourse, isoDate, overviewField, readFormFields, surveyType, readSurveyPage, readChoice, readLessonPage, readH5PAttempts, readGradeRows, readGraderReport, readGradebookSetup, readBankQuestions, readParticipantRows, readCompletionReport, waitForLogin, followPages, parseServiceResponse, courseFormat, and the raw types MoodleCourse, MoodleParticipant, MoodleParticipantDates, AssignmentIds, DiscussionRow, MoodlePost, MoodleUser. Use the operations (startQuizAttempt, assignmentSubmissions, forumDiscussions, courseStructure…) or the primitives instead.

Activity ids are activityId. Every parameter taking an activity's course module id is named activityId, whatever the module (it was cmid, forumCmid, bankCmid or quizId). Positional calls don't change; code reading the names (wrappers, docs) does.

Results name ids one way: a thing's own id is id, the ids it refers to are <thing>Id.

Was Now
QuizSlot.slotId id
quizInfo().attempts[].attemptId id
QuizAttemptResult.attemptId id
H5PAttempt.attemptId id
GradeColumn.itemId id (grades still refer to it as itemId)
QuestionBank.cmid id
CourseChange.newId activityId (duplicateActivity) or sectionId (addSection)
discussion().discussion.forumId (the instance id) forumInstanceId

userById returns a User in the driver's terms (fullName, firstAccess and lastAccess as ISO 8601 dates, left out for never), no longer Moodle's raw record (fullname, Unix seconds).

Every write returns { changed, …state }:

Function Was Now
saveSubmission, submitForGrading StudentAssignment { changed, assignment }
answerChoice Choice { changed, choice }
submitSurvey { messages } { changed, messages }
startQuizAttempt, answerQuizQuestions, finishQuizAttempt, answerLessonPage their objects the same, with changed

Plain Errors are now MoodleDriverErrors with a code (relative paths, missing files, a forum without attachments, content for a module without it, a failed logout, a file outside the site…). instanceof Error still holds.

The session cookies file is moodle-driver-session.json (SESSION_COOKIES_FILE), no longer moodle-mcp-session-cookies.json. Profiles from before keep their persistent cookies but lose Moodle's session cookie: the person logs in once more.

The object is leaner: it no longer has the pieces the operations are built from (assignmentIds, gradingPanelHtml, startAttempt, courseState, editCourse and the like). It keeps callService, openSitePage and openForm.

Internal

  • The source is one folder per Moodle area (src/areas/<area>/), with src/core/ for what they're built on and src/browser/ for the browser; the tests mirror it.
  • ADR-010 records the curated entry and /internals; the conventions record the naming and result rules.

Documentation: https://falkenslab.github.io/moodle-driver/