Skip to content

Instantly share code, notes, and snippets.

@jtuttas
Last active July 10, 2026 19:36
Show Gist options
  • Select an option

  • Save jtuttas/02316603a5209e7cb99688a0c7debf9e to your computer and use it in GitHub Desktop.

Select an option

Save jtuttas/02316603a5209e7cb99688a0c7debf9e to your computer and use it in GitHub Desktop.
Skills
name moodle-aktivitaetsbericht
description Erstellt einen detaillierten Aktivitäts- und Lernbericht für einen einzelnen Moodle-Schüler als formatiertes Word-Dokument (.docx) und speichert ihn im Unterordner Berichte/ des Projektverzeichnisses. Verwende diesen Skill IMMER wenn der Benutzer nach einem Lernbericht, Aktivitätsbericht, Fortschrittsbericht oder einer Lernzusammenfassung für einen Schüler fragt – auch bei Formulierungen wie "erstelle einen Bericht für [Name]", "Lernstand von [Schüler]", "wie kommt [Name] voran", "zeig mir den Bericht zu [Schüler]", "Bericht [Schüler]", "Aktivitätsbericht [Name]", "was hat [Name] bisher gemacht". Auch verwenden wenn der Benutzer einen Schüler und ein Lernprotokoll in einem Atemzug nennt.

Moodle Aktivitätsbericht

Dieses Skill liest das gespeicherte Lernprotokoll eines Schülers und erstellt daraus ein professionell formatiertes Word-Dokument (.docx). Die Datei wird immer im Unterordner Berichte/ des Projektverzeichnisses abgelegt – nie direkt im Wurzelverzeichnis.

Der Bericht enthält immer diese 7 Abschnitte:

  1. Übersicht
  2. Lernfortschritt nach Lernsituation
  3. Nachgewiesene Kompetenzen
  4. Lernverlauf
  5. Erteiltes Feedback (pro bewerteter Aufgabe)
  6. Kommunikationsprotokoll
  7. Empfehlungen & Nächste Schritte

Schritt 1 – Lernprotokoll finden

Lernprotokolle liegen unter Lernprotokolle/. Dateinamen: {userid}_{Vorname}_{Nachname}.md.

ls <projektordner>/Lernprotokolle/

Wenn der Benutzer einen Namen nennt, suche die passende Datei (Groß-/Kleinschreibung ignorieren). Lies die gefundene Datei vollständig.


Schritt 1b – Fehlende Stammdaten aus Moodle nachladen

Falls E-Mail-Adresse, Benutzername oder letzter Zugriff im Lernprotokoll fehlen, lade sie aus Moodle nach:

moodle_get_enrolled_students(courseid=<kurs_id>)

Suche den Schüler anhand von Name oder userid und ergänze die fehlenden Felder.

Letzter Zugriff im Kurs – wichtige Regel:

  • Nur einen echten Kurszugriff eintragen, der aus Moodle-Daten (z.B. lastaccess-Feld) stammt
  • Das Erstellungsdatum des Lernprotokolls ist kein Kurszugriff und darf hier nicht eingetragen werden
  • Hat der Schüler noch nie auf den Kurs zugegriffen: "letzterZugriff": "Noch kein Zugriff"
  • Ist kein Zugriffsdatum aus Moodle abrufbar: "letzterZugriff": "–"

Schritt 2 – Berichte-Ordner sicherstellen

mkdir -p <projektordner>/Berichte

Schritt 3 – Daten aus dem Lernprotokoll extrahieren

Erzeuge /tmp/bericht_data.json per Python-Skript (kein direktes Schreiben der JSON-Datei – das vermeidet Encoding-Probleme mit Sonderzeichen und Emojis):

import json

data = {
  # ── PFLICHT: Stammdaten als eigenes Top-Level-Objekt ──────────────────────────
  # Das Script liest diese Felder aus stammdaten, NICHT aus student!
  "stammdaten": {
    "vollstaendigerName": "Vorname Nachname",      # Pflicht
    "benutzername":       "moodle-login",          # Pflicht
    "benutzerId":         786,                     # Pflicht (int)
    "email":              "vorname@example.de",    # Pflicht – aus Moodle laden falls nötig
    "rolle":              "Teilnehmer/in (Student)",
    "letzterZugriff":     "Noch kein Zugriff",   # aus Moodle lesen; nie Protokolldatum verwenden!
    "kurs":               "KI-Test (Kurs 86)",     # Pflicht – vollständiger Kursname
    "pruefenderLehrer":   "Joerg Tuttas (TU), MMBBS Hannover"
  },

  # ── PFLICHT: kursname als Top-Level-Feld (wird im Kopfbereich angezeigt) ──────
  "kursname": "KI-Test (Kurs 86)",

  # ── student wird nur noch für reportDate-Kontext gebraucht ───────────────────
  "student": {
    "name":   "Vorname Nachname",
    "userid": 786
  },

  "reportDate": "2026-06-05",

  "overview": {
    "gesamtfortschritt":    "6%",
    "aktivitaetsabschluesse": "3/24 (12,5 %)",
    "abgaben":              "1/16 (6,25 %)",
    "gesamtnote":           "32,27 / 100",
    "aktiveLernsituation":  "LS 7.1 - Durchfuehren / Abschluss",
    "letzteAktivitaet":     "04.06.2026, 10:38 Uhr",
    "kategorie":            "Kritisch"
  },

  "lernfortschrittNachLS": [
    {
      "ls":          "LS 7.1 - IoT-Einstieg: LED-Steuerung mit dem ESP32",
      "status":      "Begonnen",
      "aktivitaeten": [
        { "name": "ESP32-Grundlagen Infoblatt",            "status": "Abgeschlossen",       "ergebnis": "Manuell markiert" },
        { "name": "Analysebogen: ESP32 und Kundenauftrag", "status": "Abgegeben & bewertet","ergebnis": "35/100 (35%)" },
        { "name": "Frequenzberechnung und Schaltplan",     "status": "Nicht abgegeben",     "ergebnis": "-" },
        { "name": "Arduino-Implementierungsleitfaden",     "status": "Aufgerufen",          "ergebnis": "Gelesen" },
        { "name": "Quellcode main.cpp einreichen",         "status": "Nicht abgegeben",     "ergebnis": "-" },
        { "name": "Messprotokoll und Soll-Ist-Vergleich",  "status": "Nicht abgegeben",     "ergebnis": "-" },
        { "name": "Reflexion und Praesentation",           "status": "Nicht abgegeben",     "ergebnis": "-" },
        { "name": "Abschlusstest 7.1 (Quiz)",              "status": "Versucht (20 Sek.)",  "ergebnis": "0,5/10 (5%)" }
      ],
      "hinweis": "Abschlusstest in ca. 20 Sekunden absolviert - gilt als Diagnose, nicht als Kompetenznachweis."
    },
    {
      "ls":          "LS 7.2 - IoT-Webserver: LED-Frequenzsteuerung mit dem ESP32",
      "status":      "Nicht begonnen",
      "aktivitaeten": [],
      "hinweis":     "Alle 8 Aktivitaeten stehen aus."
    },
    {
      "ls":          "LS 7.3 - Smarte Beleuchtungssteuerung: ESP32 LED-Dimmer",
      "status":      "Nicht begonnen",
      "aktivitaeten": [],
      "hinweis":     "Alle 10 Aktivitaeten stehen aus."
    }
  ],

  "kompetenzen": [
    "Kann ESP32-Grundlagen und PlatformIO benennen. (LS 7.1, Phase Informieren, 04.06.2026)",
    "Kann setup() und loop() korrekt abgrenzen. (LS 7.1, Phase Informieren, 04.06.2026)"
  ],

  "lernverlauf": [
    {
      "datum":                 "04.06.2026",
      "kurs":                  "KI-Test (86)",
      "abgaben":               "1/16",
      "aktivitaetsabschluesse":"3/24",
      "fortschritt":           "6%",
      "ls":                    "LS 7.1 - Informieren/Durchfuehren",
      "kategorie":             "kritisch",
      "kompetenzen":           "Infoblatt gelesen, setup()/loop() abgrenzen, Vorwiderstand, Leitfaden gesichtet."
    }
  ],

  "feedback": [
    {
      "aufgabe":     "Analysebogen: ESP32 und Kundenauftrag",
      "datum":       "2026-06-04",
      "note":        "35/100 (35%)",
      "positiv":     [
        "Den Unterschied zwischen setup() und loop() korrekt beschrieben.",
        "Hinweis zur LED-Schutzschaltung geht in die richtige Richtung."
      ],
      "verbesserung": [
        "Fragen 1 und 5 nicht beantwortet - Materialien erneut durcharbeiten.",
        "Frage 2: Unterschied INPUT- vs. OUTPUT-Modus fehlt.",
        "Frage 4: Technische Begruendung fuer Vorwiderstand fehlt."
      ],
      "empfehlung": "Antworten anhand der bereitgestellten Materialien ueberarbeiten und neu einreichen."
    }
  ],

  "kommunikation": [
    {
      "datum":  "04.06.2026",
      "tenor":  "Klar mahnend",
      "inhalt": "Engagement anerkannt, auf Reihenfolge hingewiesen: erst Analysebogen ueberarbeiten, dann Frequenzberechnung + main.cpp."
    }
  ],

  "chatDialog": [
    {
      "datum":    "04.06.2026 13:30",
      "schueler": "Ich glaube so etwas brauche ich nie wieder im Leben!",
      "tutor":    "Validierung der Frustration; Erinnerung an Kompetenzen; konkreter naechster Schritt.",
      "tenor":    "Emotionale Unterstuetzung - Hilfe zur Selbsthilfe."
    }
  ],

  "empfehlungen": [
    { "prio": "1", "massnahme": "Analysebogen LS 7.1 ueberarbeiten und neu einreichen.",      "ziel": "Note auf >70 % steigern; Analysephase abschliessen." },
    { "prio": "2", "massnahme": "Frequenzberechnung und Schaltplan einreichen.",               "ziel": "Planungsphase vollstaendig abschliessen." },
    { "prio": "3", "massnahme": "Quellcode main.cpp implementieren und einreichen.",           "ziel": "Durchfuehrungsphase abschliessen." },
    { "prio": "4", "massnahme": "Messprotokoll + Reflexion einreichen, Abschlusstest ablegen.","ziel": "LS 7.1 vollstaendig abschliessen." },
    { "prio": "5", "massnahme": "LS 7.2 und LS 7.3 aufnehmen.",                               "ziel": "Kursfortschritt auf Kursniveau bringen." }
  ],

  "outputDir":      "/pfad/zu/Berichte",
  "outputFilename": "Aktivitaetsbericht_Nachname_Vorname_YYYY-MM-DD.docx"
}

with open('/tmp/bericht_data.json', 'w', encoding='utf-8') as f:
    json.dump(data, f, ensure_ascii=True, indent=2)

Wichtige Regeln für die JSON-Erzeugung

Stammdaten (Pflichtfelder – alle müssen befüllt sein):

  • stammdaten ist ein eigenes Top-Level-Objekt – nicht innerhalb von student verschachteln
  • kursname ist ein eigenes Top-Level-Feld (String) – zusätzlich zu stammdaten.kurs
  • email: Wenn im Lernprotokoll nicht vorhanden → moodle_get_enrolled_students aufrufen (Schritt 1b)
  • benutzername: Moodle-Login (aus Protokoll oder Moodle-Daten)
  • Kein Feld darf leer bleiben – notfalls "–" als Platzhalter

Encoding:

  • Umlaute in outputFilename ersetzen: ä→ae, ö→oe, ü→ue, ß→ss
  • JSON immer per Python mit ensure_ascii=True schreiben (vermeidet Encoding-Fehler)
  • In allen anderen Feldern (Freitext) dürfen Umlaute/Emojis stehen

outputFilename: Aktivitaetsbericht_{Nachname}_{Vorname}_{YYYY-MM-DD}.docx

lernfortschrittNachLS (Pflicht):

  • Aktivitätsnamen aus dem Protokoll übernehmen; sonst generische Namen
  • status der LS: „Nicht begonnen" / „Begonnen" / „Abgeschlossen"
  • hinweis für besondere Beobachtungen

feedback: Leeres Array [] wenn keine Noten/Rückmeldungen im Protokoll.

empfehlungen: Mindestens 2–5 Einträge, aus dem Gesamtbild abgeleitet.


Schritt 4 – Word-Dokument erzeugen

Das Script liegt im Skill-Verzeichnis unter scripts/generate_bericht.js. Da das Skill-Verzeichnis read-only ist, in /tmp arbeiten:

# Skill-Scripts in beschreibbares Verzeichnis kopieren (falls noch nicht vorhanden)
[ -d /tmp/bericht_work ] || cp -r <skill-verzeichnis>/scripts /tmp/bericht_work

# Abhängigkeiten installieren (falls noch nicht vorhanden)
[ -d /tmp/bericht_work/node_modules ] || (cd /tmp/bericht_work && npm install docx)

# Bericht generieren – gibt Ausgabepfad auf stdout aus
cd /tmp/bericht_work && node scripts/generate_bericht.js /tmp/bericht_data.json

Wenn das Script die Ausgabedatei direkt nach outputDir schreiben kann (Schreibrechte vorhanden), ist alles fertig. Andernfalls Datei manuell kopieren:

cp /tmp/<outputFilename> <projektordner>/Berichte/

Das Script gibt den vollständigen Ausgabepfad auf stdout aus.


Schritt 5 – Datei präsentieren

Präsentiere die fertige Datei mit present_files und nenne kurz:

  • Speicherort (`B
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment