====== Classroom 50: Wie bewertet wird ====== Die Bewertung macht [[https://github.com/BZZ-Commons/pygrader50|pygrader50]] — pytest für die Funktion, pylint für den Stil. Beides zählt zur Note. ===== Die drei Konfigurationsdateien ===== Format wie bisher in den BZZ-Templates, es ändert sich nichts am Inhalt: ^ Datei ^ Inhalt ^ | ''unittests.json'' | welche Tests laufen, mit Timeout und Punkten | | ''lint.json'' | welche Dateien gelintet werden und wie viele Punkte das gibt | | ''pylintrc'' | pylint-Konfiguration | [ { "name": "test_ggt", "function": "test_ggt", "timeout": 10, "points": 2 } ] { "files": ["main.py"], "ignore": [], "max": 5 } Vorlage: [[https://github.com/BZZ-Commons/pygrader50/tree/main/examples/bundle|examples/bundle]] im pygrader50-Repository. ===== Wo die Dateien gesucht werden ===== **Pro Datei**, in dieser Reihenfolge: - ''/autograders//'' im Config-Repo (das //Bundle//). Von der Lehrperson kontrolliert, für Lernende nicht editierbar. - ''.github/autograding/'' im Studi-Repo. Der bisherige Ort, bleibt als Fallback. - ''$PYGRADER50_CONFIG_DIR'' — nur für lokale Entwicklung. Fehlt beides, wird eine **0/0**-Abgabe aufgezeichnet und im Log gewarnt. Der Job wird dabei absichtlich nicht rot: Eine Aufgabe ohne hinterlegte Bewertung ist ein Konfigurationsstand, kein Infrastrukturfehler. Der Fallback auf ''.github/autograding/'' erlaubt einen Rollout in Ruhe: Die Templates funktionieren unverändert weiter, während die Bundles nach und nach ins Config-Repo wandern. ==== Ein Bundle anlegen ==== classroom50/ └── / └── autograders/ └── / ├── unittests.json ├── lint.json └── pylintrc Nach dem Push bündelt der Workflow ''publish-pages'' den Ordner zu ''autograders/.tar.gz''; der Runner entpackt ihn und pygrader50 liest von dort. **Kein ''autograder.py'' und keine ''tests.json'' in diesen Ordner legen.** Beides hat Vorrang vor dem Klassen-Default und würde damit die BZZ-Bewertung — und insbesondere das Linting — für diese Aufgabe abschalten. ===== Punkte ===== ==== Unittests ==== Ein Eintrag pro Testfall. ''passed'' heisst: volle Punktzahl erreicht. Jeder Fall läuft als **eigener** pytest-Aufruf mit eigenem Timeout, damit ein hängender Test die übrigen nicht mitreisst. ==== Linting ==== Ein einziger Eintrag ''Linting'': Punkte = pylint-Note / 10 * max passed = Punkte > 0 Eine Konventionsmeldung kostet also **Punkte**, färbt den Commit-Status aber nicht rot. Ohne diese Regel wäre praktisch jeder Commit rot. Bei wenigen Statements und einem ''E…''-Fehler wird die pylint-Note negativ und auf 0 geklemmt. ''Linting: 0'' ist in dem Fall korrekt, kein Defekt. ==== Rundung ==== ''result.json'' verlangt **ganze Zahlen**, es wird gerundet. Der exakte Wert steht im Feedback-Text. ===== Beispiel-Feedback ===== ### classroom50 autograde: 0/7 ## Unittests | name | feedback | expected | actual | points | max | | -------- | --------------- | -------- | ------ | ------ | --- | | test_ggt | Assertion Error | 8 | None | 0 | 2 | **0.00/2.00 Points (0.00%)** Die Spalten //expected// und //actual// stammen aus dem pytest-Hook ''pytest_assertrepr_compare'', der vor dem Lauf ins Checkout kopiert wird. ===== Warum nicht die deklarativen Tests von Classroom 50 ===== Classroom 50 bringt mit ''tests.json'' einen deklarativen Weg mit. Die BZZ benutzt ihn bewusst nicht: * Der Runner löst eine aufgaben-eigene ''tests.json'' **vor** dem Klassen-Default auf. Eine Aufgabe mit deklarativen Tests bekommt also kein Linting mehr. * Im deklarativen Pfad wäre entweder die proportionale Lint-Note **oder** der grüne Commit-Status zu haben, nie beides: Der Interpreter setzt ''success'' nur, wenn //alle// Zeilen ''passed'' sind, und vergibt das Flag selbst. Pro Aufgabe lässt sich beides nicht mischen — pro Klasse schon. Deshalb stehen alle Aufgaben auf ''"autograder": "default"''. ===== Was pygrader50 bewusst nicht tut ===== * **Keine Zusatzfelder in ''result.json''.** CLI und Dashboard von Classroom 50 parsen strikt; alles Menschenlesbare gehört in den Release-Text. * **Kein ''$GITHUB_OUTPUT''.** Status und Zusammenfassung leitet der Runner selbst aus ''result.json'' ab — ein Kanal weniger, der auseinanderlaufen kann. * **Kein ''pip install -r requirements.txt''** aus dem Studi-Repo. pygrader50 bringt eigene, gepinnte Abhängigkeiten mit; das hält die Bewertung reproduzierbar. Ausserdem sind die alten Template-Pins auf Python 3.14 nicht lauffähig. ===== Lokal ausprobieren ===== cd /pfad/zum/studi-repo CLASSROOM= \ ASSIGNMENT= \ SUBMISSION_TAG=submit/2026-08-13T12-00-00Z-abc1234 \ OWNER=anna \ python -m pygrader50 cat release-body.md Ohne ''RUNNER_TEMP'' entfällt die Bundle-Suche, es zählt also ''.github/autograding/'' im Checkout. Details: [[.:cli|CLI-Referenz]].