Python Academy – VS Code Extension
Eine Lernplattform ähnlich JetBrains Academy, direkt in VS Code: Python-Kurse
werden aus GitHub-Repositories geladen, Schüler bearbeiten Aufgaben, führen
automatische Tests (pytest) aus und speichern ihren Fortschritt.
Inhalt
Features
- 📚 Sidebar "Python Academy" mit zwei Views: einer Baumansicht
(Kurs → Kapitel → Lektion → Aufgabe, ☑ abgeschlossen / ☐ verfügbar, plus
Fortschrittsanzeige je Kurs) und einer zweiten View "Aufgabe" direkt
darunter, die Beschreibung und Aktionen der ausgewählten Aufgabe zeigt
(kein separater Editor-Tab mehr).
- 🔐 Login ausschließlich per Microsoft-SSO (
AuthService, VS Codes
eingebauter Microsoft-Authentication-Provider) – Schüler:innen melden sich
mit ihrem Schul-Microsoft-Konto an, ein eigenes Passwort gibt es nicht.
Nicht eingeloggte Benutzer sehen keinerlei Kursinhalte – nur einen
Login-Hinweis in der Sidebar.
- 🏫 Klassen-/Roster-basierte Sichtbarkeit: Ein optionales Backend
(
GET /roster) legt fest, welche Kurse aus dem Katalog eine Klasse sehen
darf – z.B. um unterschiedlichen Klassen unterschiedliche Kurse zu zeigen.
- 🐙 Mehrere Kurse gleichzeitig aus GitHub-Repositories ladbar (
GitHubService
CourseService, Katalog-Einstellung pythonAcademy.courses).
- 📝 Aufgabenansicht als eigene Sidebar-Webview (
TaskDetailViewProvider):
Beschreibung, Starter-Datei, Testanzahl, Buttons "Open Task" / "Run Tests"
(bzw. "Als erledigt markieren") / "Submit" / optional "Run Turtle".
- ✅ Automatischer Test-Runner (
TestRunner) führt pytest per child_process
aus und wertet die Ausgabe aus.
- 🧪 Native VS Code Testing-Integration (
PytestTestExplorer): sobald eine
Aufgabe gestartet wurde, erscheint sie im Testing-Panel (Reagenzglas-Symbol
in der Activity Bar) mit einem Testfall pro pytest-Testfunktion. Fehler
werden inkl. vollständigem Traceback direkt in der nativen Testergebnis-
Ansicht angezeigt (kein eigenes Text-Rendering mehr im Webview nötig).
- 📤 Submit-Funktion: Nach erfolgreichen Tests wird die Lösung per GitHub
Contents API in ein Schüler-Repository committet.
- 📈 Fortschritt (
ProgressService): lokal über globalState, optional über
eine Backend-API (GET/POST /progress) synchronisiert, aufgabengenau
(nicht nur pro Lektion).
- 🐢 Optionale Turtle-Unterstützung: Aufgaben mit
"turtle": true können über
"Run Turtle" ausgeführt werden – startet main.py mit dem echten,
eingebauten turtle-Modul (tkinter) in einem separaten nativen Fenster.
- 🔓 Kein Sperren/Freischalten: Jede Aufgabe ist von Anfang an anklickbar,
unabhängig vom Fortschritt bei anderen Aufgaben.
- ☁️ Auf Vercel deploybares Beispiel-Backend
(
example/backend-vercel-supabase):
Login läuft wie überall über Microsoft-SSO, Supabase dient hier nur noch
als Postgres-Datenbank für den Fortschritt statt einer In-Memory-Map –
siehe "Backend auf Vercel deployen" unten.
Schnellstart (MVP-Ablauf)
- Öffne einen leeren Ordner in VS Code (hier werden die Aufgaben-Dateien
angelegt:
Datei > Ordner öffnen).
- Trage einen Kurs im Katalog
pythonAcademy.courses ein, z.B. das
mitgelieferte Beispiel unter example/python-course
(in ein eigenes GitHub-Repo kopieren und Setting entsprechend anpassen).
- Login ausführen (ohne Login sind keine Kurse sichtbar).
- Öffne die Seitenleiste Python Academy (Activity Bar) – die Baumansicht
lädt automatisch die für dich sichtbaren Kurse; alternativ
Python Academy: Kurse neu laden ausführen.
- Klicke auf eine Aufgabe im Baum → die Beschreibung erscheint in der
Aufgabe-View direkt darunter,
main.py wird automatisch angelegt und
im Editor geöffnet.
- Bearbeite
main.py, klicke Run Tests.
- Nach erfolgreichem Testlauf: optional Submit, um die Lösung ins eigene
GitHub-Repository zu pushen (erfordert
pythonAcademy.studentRepo sowie
ein gespeichertes GitHub-Token).
Kurs-Hierarchie
Course (Kurs, z.B. "Python Basics")
└── Chapter (Kapitel, z.B. "Grundlagen")
└── Lesson (Lektion, z.B. "Variablen")
└── Task (Aufgabe, kleinste Einheit, z.B. "Erste Variable")
Eine Task ist die kleinste gradierbare Einheit: eigene Starter-Datei
(starter.py), eigene Beschreibung (README.md) und optionale eigene Tests
(tests/*.py). Eine Lektion kann mehrere Aufgaben enthalten, ein Kapitel
mehrere Lektionen, ein Kurs mehrere Kapitel. Mehrere Kurse können gleichzeitig
im Katalog (pythonAcademy.courses) konfiguriert und geladen sein (z.B.
"Einführung in Programmieren" und weitere Kurse parallel).
Es gibt bewusst kein Sperren/Freischalten: jede Aufgabe ist immer
anklickbar. Der Fortschritt wird aufgabengenau anhand eines zusammengesetzten
Schlüssels <chapterId>/<lessonId>/<taskId> (taskKey()) gespeichert.
Klassen/Roster-Zugriffssteuerung
Nicht eingeloggte Benutzer sehen keine Kursinhalte – die Sidebar zeigt nur
einen Login-Hinweis. Nach dem Login gilt:
- Ist
pythonAcademy.backendApiUrl nicht konfiguriert: alle Kurse aus dem
Katalog pythonAcademy.courses werden geladen (Demo-/Offline-Modus ohne
Klassenverwaltung).
- Ist
pythonAcademy.backendApiUrl konfiguriert: die Extension ruft
GET {backendApiUrl}/roster mit dem Access-Token auf. Die Antwort
{ "classId": "...", "courses": [{"id":..., "repo":..., "branch":...}], "studentRepo": "owner/repo" }
wird automatisch in die globalen Einstellungen pythonAcademy.courses
bzw. pythonAcademy.studentRepo übernommen – Schüler:innen müssen weder
den Kurskatalog noch ihr eigenes Abgabe-Repository manuell konfigurieren.
Schlägt die Anfrage fehl (Netzwerkfehler, kein Zugriff), werden keine
Kurse angezeigt (fail-closed), statt versehentlich alle Kurse offenzulegen.
So können verschiedene Klassen (laut Klassen-Roster im Backend)
unterschiedliche Kursinhalte sehen und automatisch das richtige
Abgabe-Repository zugewiesen bekommen, ohne dass die Extension selbst eine
Klassenverwaltung implementieren muss. Die Namenskonvention
<org>/<klasse>-<schüler-slug> für studentRepo legt
roster.js fest (genutzt
vom Provisionierungs-Tool und dem Vercel-Backend selbst).
Backend-Setup (eigenes Roster provisionieren, GitHub App für automatische
Token-Vergabe, Vercel-Deployment) ist in CONTRIBUTING.md
beschrieben.
Konfiguration (VS Code Settings)
| Setting |
Standard |
Beschreibung |
pythonAcademy.courses |
[{"id":"python-basics","repo":"python-academy/python-course-example","branch":"main"}] |
Katalog verfügbarer Kurse ({id, repo, branch}) – wird bei konfiguriertem Backend nach dem Login automatisch aus GET /roster überschrieben |
pythonAcademy.studentRepo |
"" |
Ziel-Repository für Submit (owner/repo) – wird bei konfiguriertem Backend automatisch aus GET /roster gesetzt |
pythonAcademy.allowedEmailDomains |
["kswe.ch", "stud.kswe.ch"] |
Erlaubte E-Mail-Domains für den Microsoft-SSO-Login (client-seitige Frühwarnung – die eigentliche Autorisierung erfolgt serverseitig über roster.config.json) |
pythonAcademy.backendApiUrl |
"" |
Backend-URL für GET/POST /progress und GET /roster (leer = nur lokal, keine Klassen-Einschränkung) |
pythonAcademy.pythonPath |
python |
Python-Interpreter für pytest/Turtle (braucht tkinter) |
Commands
| Command |
Beschreibung |
Python Academy: Login |
Login per Microsoft-SSO (Microsoft-Konto-Auswahl über VS Code) |
Python Academy: Logout |
Lokal abmelden, Kurse/Aufgabenansicht ausblenden |
Python Academy: Kurse neu laden |
Roster + course.json aller sichtbaren Kurse erneut von GitHub laden |
Python Academy: GitHub Token setzen |
Personal Access Token in SecretStorage speichern |
Python Academy: Aufgabe öffnen |
Öffnet die Aufgabenansicht (Sidebar) einer Aufgabe |
Python Academy: Run Tests |
Führt pytest für die aktuelle Aufgabe aus |
Python Academy: Submit |
Tests ausführen + Lösung ins Schüler-Repo pushen |
Python Academy: Run Turtle |
main.py mit dem echten turtle-Modul in einem eigenen Fenster starten |
Ohne Auswahl in der Baumansicht fragen Run Tests/Submit/Run Turtle/
Aufgabe öffnen die Aufgabe über eine Auswahlliste (QuickPick) ab, die alle
sichtbaren Kurse/Kapitel/Lektionen/Aufgaben durchsucht.
python-course/
├── course.json
├── <chapterId>/
│ └── <lessonId>/
│ └── <taskId>/
│ ├── README.md # Aufgabenbeschreibung (Markdown)
│ ├── starter.py # wird zu workspace/<courseId>/<chapterId>/<lessonId>/<taskId>/main.py kopiert
│ └── tests/ # optional
│ └── test_solution.py
Das tests/-Verzeichnis ist optional: Fehlt es (0 Testfunktionen gefunden),
wird die Aufgabe nicht per pytest geprüft, sondern über den Button
"Als erledigt markieren" direkt abgeschlossen. Das ist z.B. für
Turtle-Aufgaben sinnvoll, deren Ergebnis ein gezeichnetes Bild statt eines
automatisiert prüfbaren Werts ist.
{
"id": "python-basics",
"name": "Python Basics",
"chapters": [
{
"id": "chapter01-grundlagen",
"title": "Grundlagen",
"lessons": [
{
"id": "lesson01",
"title": "Variablen",
"tasks": [{ "id": "task01", "title": "Variablen" }]
}
]
},
{
"id": "chapter03-grafik",
"title": "Grafik",
"lessons": [
{
"id": "lesson01",
"title": "Turtle",
"tasks": [
{ "id": "task01", "title": "Quadrat zeichnen", "turtle": true }
]
}
]
}
]
}
Ein vollständiges Beispiel liegt unter example/python-course.
Beim Öffnen einer Aufgabe legt die Extension automatisch an (namespaced nach
Kurs-ID, um Kollisionen zwischen mehreren geladenen Kursen zu vermeiden):
workspace/
├── <courseId>/
│ └── <chapterId>/<lessonId>/<taskId>/
│ └── main.py # aus starter.py, wird nicht überschrieben
└── tests/
└── <courseId>/
└── <chapterId>/<lessonId>/<taskId>/ # aus tests/*, immer aktuell synchronisiert (falls vorhanden)
Sicherheit
- Login läuft ausschließlich über VS Codes eingebauten
Microsoft-Authentication-Provider (
vscode.authentication) – die
Extension implementiert kein eigenes Passwort-/Token-Handling, VS Code
verwaltet Speicherung und automatischen Refresh der Microsoft-Session
selbst. Nur das manuell gesetzte GitHub-Token wird über
vscode.ExtensionContext.secrets (VS Code SecretStorage) gespeichert.
- Backends validieren das Microsoft-Access-Token bei jedem Request direkt
gegen Microsoft Graph (
GET /me) statt es lokal zu verifizieren – analog
zur bestehenden Praxis, GitHub-Tokens gegen api.github.com zu prüfen.
- Nicht eingeloggte Benutzer erhalten keine Kurs- oder Roster-Daten; die
Sidebar zeigt ausschließlich einen Login-Hinweis.
- Schlägt die Roster-Abfrage fehl (Netzwerkfehler, ungültiges Token), werden
keine Kurse angezeigt (fail-closed), statt versehentlich den vollen
Katalog offenzulegen.
- Alle Fehler werden gefangen, geloggt (
OutputChannel "Python Academy") und
dem Benutzer als Warnung/Fehlermeldung angezeigt statt die Extension
abstürzen zu lassen.
- Markdown aus README.md wird vor dem Rendern in der Webview HTML-escaped und
über eine strikte Content-Security-Policy (nonce-only
script-src)
ausgeliefert, um XSS aus einem manipulierten Kurs-Repository zu verhindern.
- Das Beispiel-Backend bindet
GET/POST /progress sowie GET /roster an
den Benutzer aus dem Bearer-Token (nicht an einen frei wählbaren
Query-Parameter), um IDOR zu vermeiden.
- Die
pythonAcademy.allowedEmailDomains-Einstellung ist eine reine
Client-seitige Frühwarnung; die eigentliche Autorisierungsgrenze bleibt der
serverseitige E-Mail-Abgleich gegen roster.config.json (fail-closed).
Turtle-Unterstützung
Aufgaben mit "turtle": true in course.json zeigen einen zusätzlichen
Button Run Turtle. Die Extension startet main.py direkt mit dem in
pythonAcademy.pythonPath konfigurierten Interpreter (kein Shim, kein
eigenes Rendering) - der Student-Code importiert ganz normal das
eingebaute turtle-Modul, das ein natives, tkinter-basiertes Fenster
außerhalb von VS Code öffnet.
Voraussetzungen:
- Eine lokale Display-Umgebung (funktioniert nicht in Codespaces oder
anderen Remote-/Headless-Umgebungen ohne X-Server).
- Ein Python mit installiertem
tkinter (unter vielen Linux-Distributionen
ein separates Paket, z.B. sudo apt install python3-tk; bei Windows/macOS
in der Standard-Installation meist enthalten).
Stürzt das Skript sofort ab (z.B. fehlendes tkinter, Syntaxfehler), wird
das erkannt und als Fehlermeldung angezeigt. Läuft der Prozess länger, geht
die Extension davon aus, dass das Turtle-Fenster erfolgreich geöffnet wurde,
und wartet nicht weiter darauf (das Fenster bleibt offen, bis der Student es
schließt).
Geplante Erweiterung: alternative Ausführung in Jupyter-Notebooks/-Lab über
jupyturtle/jupyturtle2 oder ColabTurtlePlus als Notebook-freundliche
Turtle-Implementierung ohne tkinter-Fenster.
Weiterentwicklung & Backend-Setup
Dieses Repository enthält außerdem ein Beispiel-Backend
(example/backend-vercel-supabase) sowie
ein Beispiel-Kursrepo (example/python-course). Anleitungen zum lokalen
Build/Debuggen der Extension, zur Projekt-Architektur, zum Deployen eines
eigenen Backends (Vercel/Supabase, GitHub App) und zur Roadmap stehen in
CONTRIBUTING.md.