waffensachkunde
Waffensachkunde – Lernsoftware für die Sachkundeprüfung nach § 7 WaffG. Barrierefrei, offline, EUPL-1.2.
| 1 | /** |
| 2 | * Der Meldetext zu einer Frage. |
| 3 | * |
| 4 | * ## Wozu |
| 5 | * |
| 6 | * Fehler im amtlichen Fragenkatalog oder in einer Erklärung findet, wer |
| 7 | * davorsitzt. Der Rückmeldeweg steht seit 0.10.0 unter „Über diese Software“ |
| 8 | * und im Handbuch – aber wer mitten in einer Sitzung einen Fehler bemerkt, |
| 9 | * müsste ihn sich merken, die Ansicht verlassen, die Fragennummer nachschlagen |
| 10 | * und die Fassung heraussuchen. Diese Datei stellt den Block zusammen, der |
| 11 | * das abnimmt. |
| 12 | * |
| 13 | * ## Warum die Kennung mitläuft und nicht nur die amtliche Nummer |
| 14 | * |
| 15 | * Die amtliche Nummer ist **nicht eindeutig**: Nachgezählt über den Katalog |
| 16 | * teilen 227 der 575 Fragen ihre Nummer mit mindestens einer anderen – „01“ |
| 17 | * gibt es als `II-01`, `III-01` und `IV-01`. Wer nur „Frage 01“ meldet, meldet |
| 18 | * dreideutig. Die interne Kennung `I.1-01` ist eindeutig und steht deshalb |
| 19 | * daneben, in Klammern und ohne Erklärungsbedarf. |
| 20 | * |
| 21 | * ## Was hier ausdrücklich **nicht** hineingehört |
| 22 | * |
| 23 | * Der Text verlässt später das Gerät – von Hand, aber er verlässt es. Also |
| 24 | * gilt dieselbe Strenge wie für alles andere in dieser Anwendung: **kein |
| 25 | * Personenbezug.** Kein Profilname, kein Lernstand, keine gegebene Antwort, |
| 26 | * kein Dateipfad, kein Rechnername, kein Zeitstempel. Was hier steht, muss |
| 27 | * einen Zweck für die Bearbeitung der Meldung haben; alles andere fliegt |
| 28 | * raus. Die Bauart sichert das: Diese Funktion nimmt **einzelne Felder** |
| 29 | * entgegen und keine ganzen Zustandsobjekte, aus denen versehentlich mehr |
| 30 | * mitreist, als jemand wollte. |
| 31 | * |
| 32 | * ## Warum kein `mailto:` |
| 33 | * |
| 34 | * Die Anwendung öffnet von sich aus keine Verbindung nach außen – siehe |
| 35 | * `shared/kontakt.ts`. Der Text wird kopiert; verschickt wird er vom Menschen. |
| 36 | */ |
| 37 | |
| 38 | import { KONTAKT } from './kontakt'; |
| 39 | |
| 40 | /** Die Angaben zur Frage, die in den Text gehören. */ |
| 41 | export interface MeldeFrage { |
| 42 | /** Eindeutige Kennung, z. B. „I.1-01“. */ |
| 43 | readonly id: string; |
| 44 | /** Amtliche Nummer, z. B. „1.01“ – nicht eindeutig, siehe Modulkopf. */ |
| 45 | readonly amtliche_nummer: string; |
| 46 | /** Seite in der amtlichen Vorlage. */ |
| 47 | readonly seite: number; |
| 48 | } |
| 49 | |
| 50 | /** Herkunft des Katalogs, aus `katalog.meta`. */ |
| 51 | export interface MeldeKatalog { |
| 52 | readonly herausgeber: string; |
| 53 | /** ISO-Datum, z. B. „2024-12-16“. */ |
| 54 | readonly stand: string; |
| 55 | readonly quelldatei_sha256: string; |
| 56 | } |
| 57 | |
| 58 | /** |
| 59 | * Angaben zum Programm. |
| 60 | * |
| 61 | * Bewusst drei einzelne Zeichenketten und **nicht** `AnwendungsInfo`: Dort |
| 62 | * hängt `datenbank.meldung` mit dran, die im Fehlerfall einen Dateipfad |
| 63 | * enthalten kann. Ein Typ, der das gar nicht erst annimmt, ist die |
| 64 | * verlässlichere Sperre als ein Vorsatz. |
| 65 | */ |
| 66 | export interface MeldeProgramm { |
| 67 | readonly fassung: string; |
| 68 | readonly baukennung: string; |
| 69 | /** ISO-Datum des Baus, darf leer sein. */ |
| 70 | readonly baustand: string; |
| 71 | } |
| 72 | |
| 73 | /** ISO-Datum als deutsches Datum; unlesbare Werte bleiben, wie sie sind. */ |
| 74 | function deutschesDatum(iso: string): string { |
| 75 | const zeit = Date.parse(iso); |
| 76 | if (Number.isNaN(zeit)) { |
| 77 | return iso; |
| 78 | } |
| 79 | return new Date(zeit).toLocaleDateString('de-DE', { |
| 80 | day: '2-digit', |
| 81 | month: '2-digit', |
| 82 | year: 'numeric', |
| 83 | }); |
| 84 | } |
| 85 | |
| 86 | /** |
| 87 | * Wie viele Zeichen der Prüfsumme in den Text kommen. |
| 88 | * |
| 89 | * Zwölf genügen, um zwei Katalogfassungen auseinanderzuhalten, und lassen die |
| 90 | * Zeile lesbar. Die vollständige Summe steht unter „Über diese Software“. |
| 91 | */ |
| 92 | const PRUEFSUMME_ZEICHEN = 12; |
| 93 | |
| 94 | /** |
| 95 | * Stellt den Meldeblock zusammen. |
| 96 | * |
| 97 | * Zeilenenden immer `\n`, kein Markup, keine Spaltenausrichtung durch |
| 98 | * Leerzeichen: Der Text soll in einer Nur-Text-Mail genauso aussehen wie in |
| 99 | * einem Forum, und ausgerichtete Spalten halten nur in Festbreitenschrift. |
| 100 | */ |
| 101 | export function meldetextBauen( |
| 102 | frage: MeldeFrage, |
| 103 | katalog: MeldeKatalog, |
| 104 | programm: MeldeProgramm, |
| 105 | ): string { |
| 106 | const bau = |
| 107 | programm.baukennung === '' |
| 108 | ? '' |
| 109 | : `, Bau ${programm.baukennung}${ |
| 110 | programm.baustand === '' ? '' : ` vom ${deutschesDatum(programm.baustand)}` |
| 111 | }`; |
| 112 | |
| 113 | return [ |
| 114 | 'Fehlermeldung zur Waffensachkunde-Lernsoftware', |
| 115 | `Senden an: ${KONTAKT.epost}`, |
| 116 | '', |
| 117 | `Frage: ${frage.amtliche_nummer} (Kennung ${frage.id}), Seite ${String(frage.seite)} der Vorlage`, |
| 118 | `Fragenkatalog: ${katalog.herausgeber}, Stand ${deutschesDatum(katalog.stand)}, ` + |
| 119 | `Prüfsumme ${katalog.quelldatei_sha256.slice(0, PRUEFSUMME_ZEICHEN)} (gekürzt)`, |
| 120 | `Programm: Fassung ${programm.fassung}${bau}`, |
| 121 | '', |
| 122 | 'Diese Angaben hat die Lernsoftware zusammengestellt. Sie enthalten nichts über die', |
| 123 | 'meldende Person und nichts über ihren Lernstand.', |
| 124 | '', |
| 125 | 'Was nicht stimmt (bitte hier beschreiben):', |
| 126 | '', |
| 127 | ].join('\n'); |
| 128 | } |