/** * Was Anwendungskern und Oberfläche über eine Lernstandssicherung austauschen. * * Reine Typen, plattformfrei wie alles unter `shared/`. Die Arbeit selbst * steht in `main/sicherung.ts` (Datei und Datenbank) und * `main/sicherung-dialoge.ts` (Reihenfolge und Dialoge). */ /** * Was in einem einzelnen Profil steht. * * Nötig für die Verlustanzeige: Ein Vergleich der Gesamtzahlen sagt nichts. * Wer hier zwei Profile mit zusammen 1.000 Antworten hat und die Datei eines * enthält mit 1.200, sieht „mehr“ – und verliert trotzdem eines seiner beiden. */ export interface Profilkennzahlen { readonly name: string; readonly antworten: number; readonly gemerkt: number; readonly pruefungslaeufe: number; /** Zeitpunkt der jüngsten Antwort dieses Profils, oder `null`. */ readonly letzteAntwort: string | null; } /** Was in einer Datenbank steht – für die Rückfrage vor dem Ersetzen. */ export interface Kennzahlen { readonly profilnamen: readonly string[]; /** Dieselben Profile, einzeln beziffert – Grundlage der Verlustanzeige. */ readonly jeProfil: readonly Profilkennzahlen[]; readonly antworten: number; /** Zeitpunkt der jüngsten Antwort als ISO-Zeichenkette, oder `null`. */ readonly letzteAntwort: string | null; readonly gemerkt: number; readonly pruefungslaeufe: number; /** * Ein unterbrochener Prüfungsbogen liegt vor. * * Er wird beim Einspielen ersetzt wie alles andere – aber er ist das * einzige, was sich nicht in einer Zahl ausdrücken lässt, und muss deshalb * in der Rückfrage als Verlust benannt werden. */ readonly offenerBogen: boolean; readonly schemafassung: number; } export interface Sicherungsergebnis { readonly gespeichert: boolean; readonly pfad: string | null; readonly bytes: number; /** * Kennung, unter der sich die Datei öffnen oder im Ordner zeigen lässt. * Einzelheiten am gleichnamigen Feld in `shared/ipc.ts`. */ readonly dateiKennung?: string; } export type Pruefergebnis = | { readonly art: 'abgebrochen' } | { readonly art: 'abgelehnt'; readonly grund: string } | { readonly art: 'brauchbar'; /** Kennung des Vorgangs; nur damit lässt sich einspielen. */ readonly vorgang: string; /** Nur der blosse Dateiname, nie ein Pfad. */ readonly dateiname: string; readonly ausDatei: Kennzahlen; readonly jetzt: Kennzahlen; }; export type Einspielergebnis = | { readonly art: 'abgelaufen' } | { readonly art: 'gescheitert'; readonly grund: string } | { readonly art: 'eingespielt'; /** * Ob die Anzeige- und Vorleseeinstellungen mitgereist sind. * * `false` bei Sicherungen aus Fassung 0.26.7 und davor – die enthalten * keine. Das ist kein Fehler, sondern der Normalfall der Vergangenheit, * und die Karte sagt dann schlicht nichts dazu. */ readonly einstellungenUebernommen: boolean; readonly profilzahl: number; /** * Das erste Profil der eingespielten Datenbank. * * Die Oberfläche **muss** dorthin wechseln: `useLernstand.ts` hält die * zuletzt gewählte Nummer in einem Ref, das ein Neuladen überlebt. Steht * dort eine Nummer, die es in der eingespielten Datenbank zufällig auch * gibt – bei Nummern ab 1 der Regelfall –, führte sie geradewegs in das * Profil einer anderen Person. */ readonly ersteProfilId: number; /** * **Verschiedene** Fragen mit mindestens einem Versuch – nicht Zeilen * im Antwortprotokoll. * * Das Feld hieß bis Fassung 0.19.1 `antworten` und hielt trotzdem * diese Zahl (`uebersicht.beantwortet`, gebildet über `versuche > 0`). * In derselben Karte stand darüber „39 Antworten“ aus der * Gegenüberstellung und darunter „24 bearbeitete Fragen“ – beides * richtig, und trotzdem las es sich wie ein Widerspruch. Der Name war * die Ursache, nicht die Anzeige. * * **Über alle Profile der Datei zusammen**, seit Fassung 0.24.2. Davor * zählte nur das erste – die Meldung nannte „3 Profile“ und daneben die * Zahl eines einzigen. */ readonly bearbeiteteFragen: number; /** Blosser Dateiname der selbsttätig angelegten Sicherheitskopie. */ readonly sicherheitskopie: string; }; /** * Ergebnis einer Profilübernahme. * * `profilId` ist die Nummer, die das dazugekommene Profil **hier** bekommen * hat – die Oberfläche wechselt dorthin. `name` kann von dem in der Datei * abweichen: Ein gleichnamiges Profil hier bleibt unberührt und behält * seinen Namen, das neue bekommt einen Zusatz. */ export type Uebernahmeergebnis = | { readonly art: 'abgelaufen' } | { readonly art: 'gescheitert'; readonly grund: string } | { readonly art: 'uebernommen'; readonly profilId: number; readonly name: string; /** Zeilen im Antwortprotokoll – Wiederholungen mitgezählt. */ readonly antworten: number; /** **Verschiedene** Fragen, zu denen ein Stand übernommen wurde. */ readonly bearbeiteteFragen: number; /** Blosser Dateiname der selbsttätig angelegten Sicherheitskopie. */ readonly sicherheitskopie: string; /** * Warnung, falls das Profil unter einem anderen Katalogstand gelernt * wurde. `null`, wenn die Stände übereinstimmen oder die Quelle keinen * führt (Sicherungen vor Schemafassung 9). * * **Warum das gemeldet und nicht verhindert wird.** Ein Lernstand aus * einer älteren Fassung ist nicht wertlos: Die allermeisten Fragen gibt * es weiterhin, und ihr Stand stimmt. Verwaist sind nur die Zeilen zu * Fragen, die der neue Katalog nicht mehr kennt – dieselbe Lage, die * der Lernstand beim Katalogwechsel auf diesem Rechner ohnehin behandelt * (`Katalogwechsel`). Die Übernahme zu verweigern nähme dem Nutzer * mehr, als sie ihm ersparte; ihm nichts zu sagen wäre die andere * Unehrlichkeit. */ readonly katalogstandDerQuelle: string | null; };