Classroom 50: CLI-Referenz

Zwei Kommandos aus pygrader50: eines bewertet, eines überträgt. Dazu ein Migrationsskript.

Wird vom Classroom-50-Runner im Studi-Checkout aufgerufen. Keine Argumente — alles kommt aus der 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
Datei Inhalt
./result.json classroom50/result/v1, vom Runner ans Release gehängt
./release-body.md Feedback-Tabellen, Release-Text und Job-Summary
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.

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

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]
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.

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.

{
  "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.

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.

# 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
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.

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.

Hebt die Werkzeug-Versionen in den Template-Repos einer Klasse und legt .python-version an.

scripts/sync-template-pins.py <ORG> <CLASSROOM> [--apply]
Was Verhalten
pylint, pytest werden überall gesetzt, fehlende Zeilen angehängt
httpx, pytest-asyncio nur dort gehoben, wo sie schon stehen
alles andere bleibt Zeile für Zeile erhalten, inkl. Kommentaren
.python-version wird auf die konfigurierte Version gesetzt

Die Versionen stehen als Block am Kopf des Skripts und werden einmal pro Semester angefasst. Wiederholbar: ein aktuelles Template meldet ok.

pytest-asyncio muss mit pytest mitziehen — 0.23.8 und pytest 9 lassen sich nicht gemeinsam auflösen. Pakete, welche die Lernenden im Rahmen der Aufgabe selbst eintragen sollen (Flask in den lu06-Aufgaben), gehören nicht in die Pin-Liste.

Spiegelt den Ordner wiki/ aus dem Repo hierher. Pfad = Seiten-ID: wiki/howto/git/classroom50/start.txt wird zu howto:git:classroom50:start.

scripts/publish-wiki.py [--apply] [--summary "Text"]

Geschrieben wird nur, was abweicht — ein erneuter Lauf meldet alles als gleich und hinterlässt keine leeren Versionen. Zugangsdaten kommen aus der Umgebung: DOKUWIKI_TOKEN, sonst DOKUWIKI_USER und DOKUWIKI_PASSWORD.

Ein untauglicher API-Token wird nicht abgelehnt, sondern ignoriert — die Aufrufe laufen dann als Gast weiter und scheitern erst beim Schreiben mit einer 401, die wie ein falsches Passwort aussieht. Das Skript prüft deshalb vor dem Schreiben per core.whoAmI, als wer es angemeldet ist, gibt das aus und fällt bei totem Token auf die Anmeldung per core.login zurück.

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.

  • howto/git/classroom50/cli.txt
  • Zuletzt geändert: 2026/08/17 08:54
  • von admin