Recipes¶
This page collects task-oriented workflows for common python-moodle jobs.
Use these recipes when you want a copy/paste starting point instead of a full
command reference.
Verify your environment and login¶
Use this recipe first when setting up a new .env profile or switching to a
different Moodle environment.
If the command succeeds, your credentials, session bootstrap, and base URL are working together.
If it fails:
- confirm the selected environment name matches your
.envkeys - verify
MOODLE_<ENV>_URL,MOODLE_<ENV>_USERNAME, andMOODLE_<ENV>_PASSWORD - review the Troubleshooting guide for common login and session failures
Diagnosing a broken environment¶
Use doctor when site info (or any other command) fails and you need to
know exactly which part of the environment is misconfigured: base URL,
login, sesskey, webservice token, webservice reachability, upload endpoint,
and a few other derived facts.
Machine-readable output is useful in CI or before running bulk operations:
Each row is one independent check with a pass, warn, or fail status:
failmeans a critical check did not pass (base URL unreachable, login failed, or no sesskey available).doctorexits with code1.warnmeans an optional check could not be completed (for example, no webservice token configured) but the environment is still usable for the operations that do not depend on it.doctorstill exits with code0.- An unknown or misconfigured
--env(missing requiredMOODLE_<ENV>_*variables) is reported before any check runs, anddoctorexits with code2.
doctor never prints raw secret values (password, webservice token, or
sesskey); check messages only report presence, length, or other
non-sensitive derived facts.
Inspect a course before changing it¶
Use these commands together when you need to confirm IDs and current state before creating or deleting content.
# Find the course ID you want to work with.
python-moodle courses list
# Inspect one course in detail.
python-moodle courses show 2
# List its sections before adding modules.
python-moodle sections list 2
This flow is useful for scripts and manual operations because it reduces the chance of targeting the wrong course or section.
Export data for spreadsheets or shell pipelines¶
courses, categories, sections, and users list commands accept
--output csv, in addition to the existing table, json, and yaml
formats. CSV output is written to stdout only (no banners or extra text), so
it can be redirected straight into a file or another tool.
# Save the course list as a CSV file for a spreadsheet.
python-moodle courses list --output csv > courses.csv
# Pipe user emails into another shell tool.
python-moodle users list --course-id 2 --output csv | cut -d, -f3
Select only the fields you need with --fields¶
The courses, categories, sections, and users list commands accept
--fields field1,field2,... to project the machine-readable output
(--output json/yaml/csv) down to just the fields you want, in the order
you list them. This avoids a post-processing step with jq/cut and gives
you a stable, minimal shape for automation.
# Only the id and shortname of every course, as JSON, in that order.
python-moodle courses list --output json --fields id,shortname
# The same projection as CSV (the selected fields become the columns).
python-moodle courses list --output csv --fields id,shortname > courses.csv
# Field order follows what you pass, so this puts shortname first.
python-moodle courses list --output json --fields shortname,id
Notes:
--fieldsonly affects machine-readable output; it is ignored for the default--output table.- An unknown field name is a hard error (non-zero exit, the offending field and the available fields are printed to stderr), so a typo fails loudly instead of silently returning empty columns.
- Passing an empty value (
--fields "") is treated as "no filtering", identical to omitting the flag.
Automate python-moodle in scripts and CI jobs¶
Combine --quiet, --no-color, and --output csv/--output json to keep
automated output clean and machine-readable. --quiet suppresses incidental
status/confirmation messages (the command's result and any errors are always
shown); --no-color (also respected via the NO_COLOR environment variable)
strips ANSI styling from table output.
# A CI-friendly invocation: no banners, no color, CSV on stdout.
python-moodle --quiet --no-color courses list --output csv
# Diagnose a failing login without leaking secrets into logs: tokens,
# session keys, and passwords are always redacted from --verbose/--debug
# output.
python-moodle --debug site info
Errors are always written to stderr, never mixed into --output json/csv
stdout, so courses=$(python-moodle courses list --output json) is safe to use
even when the command fails.
Create a course and add a welcome label¶
This is a minimal end-to-end content bootstrap workflow.
# 1. Create the course.
python-moodle courses create \
--fullname "Automation Demo" \
--shortname "automation-demo"
# 2. Create a section if you need one beyond the default course layout.
python-moodle sections create 2 --name "Getting Started"
# 3. Add a welcome label to the first section.
python-moodle modules add label \
--course-id 2 \
--section-id 1 \
--name "Welcome" \
--intro "<p>Welcome to the course.</p>"
Replace the example course ID with the ID returned by the create command in your environment.
Equivalent recipe using MoodleClient¶
The same workflow, scripted with the MoodleClient facade
instead of the CLI:
from py_moodle import MoodleClient
with MoodleClient.from_env("prod") as moodle:
course = moodle.courses.create(
fullname="Automation Demo",
shortname="automation-demo",
)
section = moodle.sections.create(course["id"])
moodle.labels.add(
course_id=course["id"],
section_id=section.get("section", 1),
name="Welcome",
html="<p>Welcome to the course.</p>",
)
Upload materials into a folder¶
Use the dedicated folder commands when you want to manage a reusable course materials area.
# Create a folder activity in the course.
python-moodle folders add \
--course-id 2 \
--section-id 1 \
--name "Course Materials"
# Upload a file into the folder activity.
python-moodle folders add-file 15 ./docs/syllabus.pdf
# Confirm the folder contents.
python-moodle folders list-content 15
In this recipe, 15 is the folder module ID returned by the add command.
Preview a mutating command with --dry-run¶
courses create, courses delete, and modules add scorm support
--dry-run to preview the action's plan without touching Moodle. Combine it
with --output json for scripting or CI pipelines that want to validate a
plan before running it for real.
# Preview a course creation without calling Moodle.
python-moodle courses create \
--fullname "Automation Demo" \
--shortname "automation-demo" \
--dry-run --output json
# Preview a deletion; no confirmation prompt is shown in dry-run mode.
python-moodle courses delete 42 --dry-run --output json
# Preview a SCORM upload without uploading the package.
python-moodle modules add scorm \
--course-id 2 \
--section-id 1 \
--name "SCORM 1" \
--file ./package.zip \
--dry-run --output json
Each command prints a plan dict with action, dry_run, target, and
parameters fields. Fields that cannot be known before contacting Moodle
(such as the course_id assigned by courses create) are marked with a
placeholder value instead of a real ID.
Idempotent provisioning: ensure a course exists¶
Use courses ensure in CI pipelines or onboarding scripts that need to
provision a course without failing (or creating duplicates) on repeat runs.
# Safe to run every time: creates the course only if it is missing.
python-moodle courses ensure \
--shortname "ci-smoke-test" \
--fullname "CI Smoke Test" \
--category-id 1
Re-running the same command reports status: reused instead of failing with
a "shortname already in use" error. If the course already exists with a
different --fullname/--category-id, the command reports
status: conflict (and exits with code 1) without changing anything,
letting you inspect the differences before deciding what to do:
python-moodle courses ensure \
--shortname "ci-smoke-test" \
--fullname "Renamed CI Smoke Test" \
--category-id 1 \
--output json
Pass --update to have the command bring fullname/--category-id in line
with the request instead of reporting a conflict:
python-moodle courses ensure \
--shortname "ci-smoke-test" \
--fullname "Renamed CI Smoke Test" \
--category-id 1 \
--update
--update only ever touches fullname and category membership; it never
overwrites the course summary, visibility, or its sections/modules.
Idempotent content provisioning¶
The py_moodle.ensure module extends the same create-or-reuse pattern to
sections and content modules, so a provisioning script can be re-run safely
without creating duplicate sections, labels, resources, or folders. Each
helper keys on a human-readable natural key: sections on their name, and
modules on the (name, modname) pair within the course.
from py_moodle import MoodleSession
from py_moodle.course import create_or_update_course
from py_moodle.ensure import ensure_label, ensure_section
ms = MoodleSession.get()
session, url, sesskey = ms.session, ms.settings.url, ms.sesskey
# Create the course if missing, or bring its fullname/category in line.
course = create_or_update_course(
session,
url,
sesskey,
shortname="ci-smoke-test",
fullname="CI Smoke Test",
category_id=1,
).course
# Ensure a named section exists (created and renamed only when missing).
section = ensure_section(
session, url, sesskey, course["id"], name="Welcome"
)
# Ensure a welcome label exists in that section; a second run reuses it.
label = ensure_label(
session,
url,
sesskey,
course["id"],
section.section["id"],
name="Welcome message",
html="<p>Welcome to the course!</p>",
)
print(section.status, label.status) # e.g. "created created", then "reused reused"
ensure_resource and ensure_folder work the same way, forwarding their
entity-specific arguments (file_path, files_itemid, ...) to the underlying
add_* primitives only when a matching module does not already exist. Because
the idempotency keys are human names, renaming a section or module in Moodle
makes the next run treat it as absent and re-create it -- pick stable names.
Get IDE-friendly typed models from raw dicts (optional)¶
The library functions keep returning plain dict/list[dict] values, but
py_moodle.models offers opt-in typed wrappers if you want autocompletion
and static-typing safety in your own scripts.
from py_moodle import MoodleSession
from py_moodle.course import list_courses
from py_moodle.models import Course
ms = MoodleSession.get()
for raw_course in list_courses(ms.session, ms.settings.url, token=ms.token):
typed_course = Course.from_moodle(raw_course)
print(typed_course.id, typed_course.fullname)
Course.from_moodle() (and the other from_moodle() classmethods) tolerate
missing optional fields and ignore unknown/extra keys, so they are safe to
use even as the underlying Moodle payload shape drifts across versions.
Run the fastest contributor validation loop¶
When you are changing code or documentation, this sequence gives the quickest feedback with the existing repository tooling.
# Fast smoke tests with no live Moodle requirement.
make test-unit
# Static checks used by CI.
make lint
# Rebuild the documentation site, including generated CLI docs.
make docs
Use make test-local only when you need Docker-backed integration coverage
against the local Moodle environment.