waffensachkunde

Waffensachkunde – Lernsoftware für die Sachkundeprüfung nach § 7 WaffG. Barrierefrei, offline, EUPL-1.2.

/ app src shared meldetext.ts

4,5 KB Rohdatei
app/src/shared/meldetext.ts — 128 Zeilen
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 }