| | 3 | == Überblick |
| | 4 | |
| | 5 | Dieses Toolkit stellt eine einfache, vollständig offline-fähige Lösung zur Erstellung, Durchführung und Auswertung von Prüfungen oder Selbsttests bereit – speziell geeignet für *Erwachsenenschulungen, Fortbildungen und Testverfahren in klassifizierten oder netzabgeschotteten Umgebungen*. |
| | 6 | |
| | 7 | Alle Komponenten sind leichtgewichtig, transparent und portabel. |
| | 8 | |
| | 9 | == Komponenten |
| | 10 | |
| | 11 | Das System besteht aus drei unabhängig nutzbaren Werkzeugen: |
| | 12 | |
| | 13 | || Werkzeug || Zweck || Technologie || |
| | 14 | || ~builder~ || Erstellen und Exportieren von Prüfungsfragen || Python (Tkinter) || |
| | 15 | || ~generator~ || Generierte statisches HTML anhand einer json/Beschreibung zur Durchführung der Prüfung im Browser (offline) || PYTHON/JINJA2/HTML/CSS/JS || |
| | 16 | | ~evaluator~ | Auswertung der Antworten & Statistiken | Python (CLI/GUI) | |
| | 17 | |
| | 18 | Alle Werkzeuge arbeiten lokal, ohne Netzwerkanbindung oder zentrale Server. |
| | 19 | |
| | 20 | * builder: Frageneditor & Export |
| | 21 | |
| | 22 | Das Python-GUI-Tool erlaubt Prüfern: |
| | 23 | |
| | 24 | - Aufbau eines Fragenkatalogs zur Wiederverwendung |
| | 25 | - Zuweisung von Metadaten pro Frage: |
| | 26 | - ~domain~ (z. B. „Elektronik“) |
| | 27 | - ~topic~ (z. B. „Spannungsteiler“) |
| | 28 | - ~difficulty~ (1 = leicht, 5 = schwer) |
| | 29 | - Einbindung von Bildern, Video- oder Audiodateien |
| | 30 | - Export ausgewählter Fragen als Archiv mit: |
| | 31 | - ~exam.json~ |
| | 32 | - zugehörigen Medien (~/media/images/~, ~/media/audio/~, ~/media/video/~) |
| | 33 | |
| | 34 | * generator: Generiert offline-Prüfungsumgebung im Browser |
| | 35 | |
| | 36 | Erzeugt statischen HTML-/JavaScript-Prüfungsclient, lauffähig ohne Server: |
| | 37 | |
| | 38 | - Startbar direkt im Browser (USB-Stick, CD, Airgap-Systeme) |
| | 39 | - Keine Internetverbindung oder Anmeldung erforderlich |
| | 40 | |
| | 41 | **Funktionen:** |
| | 42 | |
| | 43 | - Seitennavigation („Zurück“ / „Weiter“) |
| | 44 | - Unterstützte Fragetypen: |
| | 45 | - Einfachauswahl (Radio) |
| | 46 | - Mehrfachauswahl (Checkbox) |
| | 47 | - Freitextantwort |
| | 48 | - optional Bild-, Video- oder Audiowiedergabe pro Frage |
| | 49 | - Eingabe von PersDat |
| | 50 | - Antworten werden am Ende des Tests im json-Format heruntergeladen |
| | 51 | |
| | 52 | * Beispiel: Antwortdatei |
| | 53 | |
| | 54 | #+begin_src json |
| | 55 | { |
| | 56 | "meta": { |
| | 57 | "test_id": "ET-2025-09-001", |
| | 58 | "test_name": "Elektrotechnik", |
| | 59 | "created_on": "2025-09-17T08:30:00Z", |
| | 60 | "student_name": "Enrico Schwass", |
| | 61 | "student_id": "12345678", |
| | 62 | "submitted_at": "2025-09-23T15:55:51.158Z" |
| | 63 | }, |
| | 64 | "answers": [ |
| | 65 | { |
| | 66 | "id": "candidate_id", |
| | 67 | "fields": [ |
| | 68 | { |
| | 69 | "name": "last_name", |
| | 70 | "value": "Schwass" |
| | 71 | }, |
| | 72 | { |
| | 73 | "name": "first_name", |
| | 74 | "value": "Enrico" |
| | 75 | }, |
| | 76 | { |
| | 77 | "name": "personal_id", |
| | 78 | "value": "12345678" |
| | 79 | } |
| | 80 | ] |
| | 81 | }, |
| | 82 | { |
| | 83 | "id": "TB801", |
| | 84 | "value": "A" |
| | 85 | }, |
| | 86 | { |
| | 87 | "id": "TB802", |
| | 88 | "value": "D" |
| | 89 | }, |
| | 90 | { |
| | 91 | "id": "TB803", |
| | 92 | "value": "D" |
| | 93 | }, |
| | 94 | { |
| | 95 | "id": "TB804", |
| | 96 | "value": [ |
| | 97 | "C", |
| | 98 | "B" |
| | 99 | ] |
| | 100 | }, |
| | 101 | { |
| | 102 | "id": "TB805", |
| | 103 | "value": "A" |
| | 104 | }, |
| | 105 | { |
| | 106 | "id": "TB806", |
| | 107 | "value": "B" |
| | 108 | }, |
| | 109 | { |
| | 110 | "id": "TB807", |
| | 111 | "value": "B" |
| | 112 | } |
| | 113 | ] |
| | 114 | } |
| | 115 | #+end_src |
| | 116 | |
| | 117 | * evaluator: Auswertung & Statistik |
| | 118 | |
| | 119 | Ein Python-Tool zur Auswertung eingereichter Antworten. |
| | 120 | |
| | 121 | **Funktionen:** |
| | 122 | |
| | 123 | - Vergleicht Antworten mit ~exam.json~ |
| | 124 | - Pro Teilnehmer: |
| | 125 | - Punkteübersicht |
| | 126 | - Stärken/Schwächen nach Themengebiet |
| | 127 | - Gesamtstatistik: |
| | 128 | - Fehlerquoten pro Frage |
| | 129 | - Durchschnittliche Schwierigkeit |
| | 130 | - Fragen mit hoher Fehlerhäufigkeit |
| | 131 | - Exportformate: |
| | 132 | - Text |
| | 133 | - HTML |
| | 134 | - PDF als Zertifikat |
| | 135 | |
| | 136 | * Datenstruktur: Examen (~exam.json~) |
| | 137 | |
| | 138 | #+begin_src json |
| | 139 | { |
| | 140 | "meta": { |
| | 141 | "title": "Elektrotechnik", |
| | 142 | "duration": "30 Minuten", |
| | 143 | "allowed_aids": "keine", |
| | 144 | "headline": "DATAV Schutzbereich 2, 10039691S", |
| | 145 | "intro_note": "Bitte lesen Sie jede Frage sorgfältig und beachten Sie die Hinweise.", |
| | 146 | "submit_note": "Sie können Ihre Antworten jetzt einreichen. Eine spätere Änderung ist nicht mehr möglich.", |
| | 147 | "exam_id": "ET-2025-09-001", |
| | 148 | "created_on": "2025-09-17T08:30:00Z", |
| | 149 | "created_by": "OSF Schwass" |
| | 150 | }, |
| | 151 | "pages": [ |
| | 152 | { |
| | 153 | "title": "Personaldaten", |
| | 154 | "questions": [ |
| | 155 | { |
| | 156 | "id": "candidate_id", |
| | 157 | "type": "candidate_id", |
| | 158 | "domain": "management", |
| | 159 | "topic": "identification", |
| | 160 | "text": "Bitte geben Sie Ihre Personaldaten ein:", |
| | 161 | "fields": [ |
| | 162 | { |
| | 163 | "id": "last_name", |
| | 164 | "label": "Nachname", |
| | 165 | "validation": { |
| | 166 | "required": true, |
| | 167 | "maxlength": 30 |
| | 168 | } |
| | 169 | }, |
| | 170 | { |
| | 171 | "id": "first_name", |
| | 172 | "label": "Vorname", |
| | 173 | "validation": { |
| | 174 | "required": true, |
| | 175 | "maxlength": 30 |
| | 176 | } |
| | 177 | }, |
| | 178 | { |
| | 179 | "id": "personal_id", |
| | 180 | "label": "Personalnummer", |
| | 181 | "validation": { |
| | 182 | "required": true, |
| | 183 | "pattern": "^\\d{8}", |
| | 184 | "maxlength": 8 |
| | 185 | } |
| | 186 | } |
| | 187 | ] |
| | 188 | } |
| | 189 | ] |
| | 190 | }, |
| | 191 | { |
| | 192 | "title": "Seitenband", |
| | 193 | "questions": [ |
| | 194 | { |
| | 195 | "id": "TB801", |
| | 196 | "type": "radio", |
| | 197 | "domain": "signals", |
| | 198 | "topic": "forms", |
| | 199 | "text": "Was ist der Unterschied zwischen AM und SSB?", |
| | 200 | "options": [ |
| | 201 | {"id": "A", "points": 1, "text": "AM hat einen Träger und zwei Seitenbänder, SSB arbeitet mit Trägerunterdrückung und einem Seitenband."}, |
| | 202 | {"id": "B", "points": 0, "text": "AM hat einen Träger und ein Seitenband, SSB arbeitet mit Trägerunterdrückung und hat zwei Seitenbänder."}, |
| | 203 | {"id": "C", "points": 0, "text": "AM hat keinen Träger und zwei Seitenbänder, SSB arbeitet mit Trägerunterdrückung und einem Seitenband."}, |
| | 204 | {"id": "D", "points": 0, "text": "AM hat keinen Träger und zwei Seitenbänder, SSB arbeitet mit Träger und einem Seitenband."} |
| | 205 | ] |
| | 206 | }, |
| | 207 | { |
| | 208 | "id": "TB802", |
| | 209 | "type": "radio", |
| | 210 | "domain": "signals", |
| | 211 | "topic": "forms", |
| | 212 | "text": "Was ist der Unterschied zwischen LSB und USB?", |
| | 213 | "options": [ |
| | 214 | {"id": "A", "points": 1, "text": "LSB arbeitet mit Trägerunterdrückung und dem unteren Seitenband, USB arbeitet mit Trägerunterdrückung und dem oberen Seitenband."}, |
| | 215 | {"id": "B", "points": 0, "text": "LSB arbeitet mit Träger und zwei Seitenbändern, USB arbeitet mit Trägerunterdrückung und einem Seitenband."}, |
| | 216 | {"id": "C", "points": 0, "text": "LSB arbeitet mit Träger und einem Seitenband, USB arbeitet mit Trägerunterdrückung und beiden Seitenbändern."}, |
| | 217 | {"id": "D", "points": 0, "text": "LSB arbeitet mit Trägerunterdrückung und dem oberen Seitenband, USB arbeitet mit Trägerunterdrückung und dem unteren Seitenband."} |
| | 218 | ] |
| | 219 | } |
| | 220 | ] |
| | 221 | }, |
| | 222 | { |
| | 223 | "title": "Modulation", |
| | 224 | "questions": [ |
| | 225 | { |
| | 226 | "id": "TB803", |
| | 227 | "type": "radio", |
| | 228 | "domain": "signals", |
| | 229 | "topic": "forms", |
| | 230 | "text": "Welche Aussage über modulierte Signale ist richtig?", |
| | 231 | "options": [ |
| | 232 | {"id": "A", "points": 1, "text": "Bei FM ändert sich die Amplitude des Sendesignals bei Modulation nicht."}, |
| | 233 | {"id": "B", "points": 0, "text": "Bei SSB ändert sich die Amplitude des Sendesignals bei Modulation nicht."}, |
| | 234 | {"id": "C", "points": 0, "text": "Bei FM ändert sich die Amplitude des Sendesignals bei Modulation im Rhythmus der Sprache."}, |
| | 235 | {"id": "D", "points": 0, "text": "Bei AM ändert sich die Amplitude des Sendesignals bei Modulation nicht."} |
| | 236 | ] |
| | 237 | }, |
| | 238 | { |
| | 239 | "id": "TB804", |
| | 240 | "type": "checkbox", |
| | 241 | "domain": "signals", |
| | 242 | "topic": "forms", |
| | 243 | "text": "Welche der folgenden Antworten beschreiben Modulationsverfahren?", |
| | 244 | "options": [ |
| | 245 | {"id": "A", "points": 1, "text": "FM"}, |
| | 246 | {"id": "B", "points": 1, "text": "AM"}, |
| | 247 | {"id": "C", "points": 1, "text": "QAM"}, |
| | 248 | {"id": "D", "points": 0, "text": "PSK"} |
| | 249 | ] |
| | 250 | } |
| | 251 | ] |
| | 252 | }, |
| | 253 | { |
| | 254 | "title": "Strassenverkehr", |
| | 255 | "questions": [ |
| | 256 | { |
| | 257 | "id": "TB805", |
| | 258 | "type": "radio", |
| | 259 | "domain": "traffic", |
| | 260 | "topic": "right of way", |
| | 261 | "text": "Welches Fahrzeug verhaelt sich falsch??", |
| | 262 | "options": [ |
| | 263 | {"id": "A", "points": 1, "text": "Das rote"}, |
| | 264 | {"id": "B", "points": 0, "text": "Das blaue"} |
| | 265 | ], |
| | 266 | "media": [{"type": "image", "src": "media/Vorfahrt.png"}] |
| | 267 | } |
| | 268 | ] |
| | 269 | }, |
| | 270 | { |
| | 271 | "title": "Signale", |
| | 272 | "questions": [ |
| | 273 | { |
| | 274 | "id": "TB806", |
| | 275 | "type": "radio", |
| | 276 | "domain": "signals", |
| | 277 | "topic": "sound", |
| | 278 | "text": "Was hoeren Sie?", |
| | 279 | "options": [ |
| | 280 | {"id": "A", "points": 0, "text": "Fernschreiber"}, |
| | 281 | {"id": "B", "points": 1, "text": "Tastfunk"} |
| | 282 | ], |
| | 283 | "media": [{"type": "audio", "src": "media/Code_40.mp3"}] |
| | 284 | }, |
| | 285 | { |
| | 286 | "id": "TB807", |
| | 287 | "type": "radio", |
| | 288 | "domain": "signals", |
| | 289 | "topic": "visual", |
| | 290 | "text": "Was sehen Sie?", |
| | 291 | "options": [ |
| | 292 | {"id": "A", "points": 0, "text": "Fernschreiber"}, |
| | 293 | {"id": "B", "points": 1, "text": "Stoerungen"} |
| | 294 | ], |
| | 295 | "media": [{"type": "video", "src": "media/spectrum.mp4"}] |
| | 296 | } |
| | 297 | ] |
| | 298 | } |
| | 299 | ] |
| | 300 | } |
| | 301 | #+end_src |
| | 302 | |
| | 303 | * Projektziele |
| | 304 | |
| | 305 | - Minimale, auditierbare JavaScript-Logik |
| | 306 | - ~jinja2~-basierte HTML-Erzeugung aus JSON |
| | 307 | - Vollständig portable und selbstenthaltende Prüfungen |
| | 308 | - Keine Framework-Abhängigkeiten |
| | 309 | |
| | 310 | * Optionale Erweiterungen |
| | 311 | |
| | 312 | - Git-Versionierung von Fragen |
| | 313 | - Feedback-Auswertung pro Thema |
| | 314 | - Anpassbares Layout/CSS |
| | 315 | |
| | 316 | * Lizenz |
| | 317 | |
| | 318 | Internes Projekt (Lizenzierung noch offen, eher MIT) |
| | 319 | |
| | 320 | |
| | 321 | |