Classroom 50: CLI-Referenz
Zwei Kommandos aus pygrader50: eines bewertet, eines überträgt. Dazu ein Migrationsskript.
python -m pygrader50 — bewerten
Wird vom Classroom-50-Runner im Studi-Checkout aufgerufen. Keine Argumente — alles kommt aus der Umgebung.
Umgebung
| Variable | Bedeutung | Pflicht |
|---|---|---|
CLASSROOM | Kurzname des Klassenzimmers | ja |
ASSIGNMENT | Slug der Aufgabe | ja |
SUBMISSION_TAG | submit/<Zeitstempel>-<Kurz-SHA> | ja |
OWNER / USERNAME | GitHub-Login der besitzenden Person | ersatzweise GITHUB_ACTOR |
ASSIGNMENT_TYPE | individual oder group | Vorgabe individual |
COMMIT_URL, RELEASE_URL, REVIEW_URL | Links fürs Payload | ersatzweise aus GITHUB_SERVER_URL / GITHUB_REPOSITORY / GITHUB_SHA |
RUNNER_TEMP | Wurzel für das entpackte Bundle | ohne die Variable entfällt die Bundle-Suche |
PYGRADER50_CONFIG_DIR | zusätzlicher Config-Ort, nur lokal | nein |
Ausgabe
| Datei | Inhalt |
|---|---|
./result.json | classroom50/result/v1, vom Runner ans Release gehängt |
./release-body.md | Feedback-Tabellen, Release-Text und Job-Summary |
Exit-Codes
| Code | Bedeutung |
|---|---|
0 | Bewertung abgeschlossen — auch wenn alle Tests scheitern |
1 | Infrastrukturfehler: Umgebung fehlt, Konfiguration kaputt, Absturz. Der Runner zeichnet die Abgabe als error auf |
Eine fehlende Bewertungs-Konfiguration ist kein Fehler: 0/0 mit Warnung im Log, Exit 0.
Lokal ausprobieren
cd /pfad/zum/studi-repo CLASSROOM=m320-ix25 \ ASSIGNMENT=m320-lu04-a4-objektkommunikation \ SUBMISSION_TAG=submit/2026-08-13T12-00-00Z-abc1234 \ OWNER=anna \ python -m pygrader50 cat release-body.md
python -m pygrader50.moodle — übertragen
Liest scores.json aus dem Config-Repo und schickt die Punkte an Moodle. Je
Kombination aus Aufgabe und Person geht die neueste Abgabe raus.
python -m pygrader50.moodle SCORES [Optionen]
Argumente und Optionen
| Option | Wirkung |
|---|---|
SCORES | Pfad zu <CLASSROOM>/scores.json (Pflicht) |
–assignment SLUG | nur diese Aufgabe |
–user LOGIN | nur diesen GitHub-Login |
–state PFAD | Zustandsfile; ohne Angabe wird jedes Mal alles übertragen |
–force | auch Unverändertes erneut senden |
–dry-run | nur anzeigen, nichts senden; funktioniert ohne Zugangsdaten |
–no-feedback | ohne Feedback-Text (spart einen API-Aufruf pro Abgabe) |
Der bei –user angegebene Login muss im Moodle-Kurs eingeschrieben sein. Der
eigene Lehrer-Account ist es meist nicht — Moodle antwortet dann
No matching assignment found, obwohl die Aktivität existiert.
Umgebung
| Variable | Bedeutung |
|---|---|
MOODLE_URL | Basis-URL der Moodle-Instanz, z.B. https://moodle.bzz.ch |
MOODLE_TOKEN | Webservice-Token |
MOODLE_FUNCTION | Vorgabe mod_externalassignment_update_grade |
GH_TOKEN / GITHUB_TOKEN | optional, liest die Release-Bodies für den Feedback-Text |
Ohne –dry-run sind MOODLE_URL und MOODLE_TOKEN Pflicht.
Zustandsfile
{ "schema": "pygrader50/moodle-state/v1", "entries": { "m323-lu01-a02-imperativer-ggt/graphics80": { "submission": "submit/2026-08-13T08-41-09Z-35bdcb2", "score": 5, "max-score": 7 } } }
Übersprungen wird nur, wenn Abgabe und Punktzahl identisch sind — eine Nachbewertung derselben Abgabe geht also erneut raus. Nur erfolgreiche Übertragungen werden vermerkt; gescheiterte versucht der nächste Lauf wieder.
Exit-Codes
| Code | Bedeutung |
|---|---|
0 | alles übertragen oder übersprungen |
1 | mindestens eine Übertragung scheiterte, oder Zugangsdaten/Datei fehlen |
Ein Fehler bricht den Lauf nicht ab: die übrigen Abgaben gehen trotzdem raus.
Beispiele
# Trockenlauf über alles, ohne Zugangsdaten python -m pygrader50.moodle <CLASSROOM>/scores.json --dry-run --no-feedback # Eine einzelne Person nachtragen python -m pygrader50.moodle <CLASSROOM>/scores.json --user anna # Eine Aufgabe nach einer Nachbewertung komplett neu schicken python -m pygrader50.moodle <CLASSROOM>/scores.json --assignment <SLUG> --force # Wie der Nachtlauf python -m pygrader50.moodle <CLASSROOM>/scores.json \ --state <CLASSROOM>/moodle-state.json
Ohne lokalen Klon des Config-Repos reicht die Datei allein:
gh api repos/<ORG>/classroom50/contents/<CLASSROOM>/scores.json \ -H 'Accept: application/vnd.github.raw' > scores.json GH_TOKEN=$(gh auth token) python -m pygrader50.moodle scores.json --dry-run
Von Hand auslösen
gh workflow run moodle-sync.yaml --repo <ORG>/classroom50 \ -f classroom=<CLASSROOM> -f dry_run=true
Oder im Config-Repo unter Actions → Moodle Sync → Run workflow. Bleibt das
Klassenzimmer leer, laufen alle Ordner mit einer scores.json.
scripts/remove-legacy-classroom-yml.sh — migrieren
Entfernt den alten GitHub-Classroom-Workflow aus den Template-Repos einer migrierten Klasse und aus den bereits angenommenen Studi-Repos.
scripts/remove-legacy-classroom-yml.sh <ORG> <CLASSROOM> [--apply]
| Aufruf | Wirkung |
|---|---|
ohne –apply | Trockenlauf, listet jedes Ziel als would rm oder absent |
mit –apply | löscht .github/workflows/classroom.yml, ein Commit pro Repo |
Die Zielliste kommt aus dem template-Block von
<CLASSROOM>/assignments.json, nicht aus dem Repo-Listing der
Template-Organisation. Wiederholbar; fehlende Dateien sind absent, kein
Fehler. Exit 1, sobald ein Repo scheitert.
Hintergrund und die nötige Token-Rotation: Migration.
gh teacher — Klassenzimmer verwalten
gh extension install foundation50/gh-teacher gh auth refresh -h github.com -s admin:org,read:org,repo,workflow gh teacher classroom list <ORG> gh teacher autograder show <ORG> <CLASSROOM> gh teacher autograder list <ORG> <CLASSROOM> gh teacher autograder set-default <ORG> <CLASSROOM> --from bootstrap/autograder.py gh teacher rotate-service-token <ORG>
Vollständige Referenz: Wiki von foundation50/classroom50.