#!/usr/bin/env python3 """Prüft die Datenbank-Sicherungen auf Vollständigkeit. Listet je Sicherung, wie viele Schlagwörter, Budget-Vorlagen und kategorisierte Buchungen darin stecken, und nennt am Ende die NEUESTE noch vollständige. So muss man nach einem Datenverlust nicht nach Zeitstempel raten. Aufruf: pruefe_backups.py [muster] """ import glob import os import sqlite3 import sys from datetime import datetime MUSTER = sys.argv[1] if len(sys.argv) > 1 else "/var/lib/certain-finance/backups/*.db" def zaehle(pfad: str) -> dict | None: """Kennzahlen einer Sicherung. None, wenn die Datei unlesbar ist.""" try: con = sqlite3.connect(f"file:{pfad}?mode=ro", uri=True) except sqlite3.Error: return None def z(sql: str): try: return con.execute(sql).fetchone()[0] except sqlite3.Error: return None # Tabelle gab es zu dem Zeitpunkt noch nicht werte = { "schlagwoerter": z("SELECT COUNT(*) FROM categorization_rules"), "budget": z("SELECT COUNT(*) FROM budget_template_lines"), "kategorisiert": z("SELECT COUNT(*) FROM transactions WHERE category_id IS NOT NULL"), "buchungen": z("SELECT COUNT(*) FROM transactions"), } con.close() return werte def zahl(v) -> str: return "–" if v is None else str(v) dateien = sorted(glob.glob(MUSTER), key=os.path.getmtime) if not dateien: print(f"Keine Sicherungen gefunden unter: {MUSTER}") raise SystemExit(1) print(f"{'Sicherung':<32}{'Zeitpunkt':<18}{'Schlagw.':>9}{'Budget':>8}{'Buch.':>7}{'davon m. Kat.':>15}") print("-" * 91) neueste_heil = None for f in dateien: w = zaehle(f) zeit = datetime.fromtimestamp(os.path.getmtime(f)).strftime("%d.%m. %H:%M") if w is None: print(f"{os.path.basename(f):<32}{zeit:<18} nicht lesbar") continue print(f"{os.path.basename(f):<32}{zeit:<18}{zahl(w['schlagwoerter']):>9}" f"{zahl(w['budget']):>8}{zahl(w['buchungen']):>7}{zahl(w['kategorisiert']):>15}") if (w["schlagwoerter"] or 0) > 0: neueste_heil = (f, w, zeit) print() if neueste_heil: f, w, zeit = neueste_heil print(f"NEUESTE Sicherung mit Schlagwörtern: {os.path.basename(f)} ({zeit})") print(f" {w['schlagwoerter']} Schlagwörter, {zahl(w['budget'])} Budget-Vorlagen, " f"{zahl(w['kategorisiert'])} von {zahl(w['buchungen'])} Buchungen kategorisiert") print() print("Zurückspielen (Dienst stoppen, sichern, ersetzen, starten):") print(" sudo systemctl stop certain-finance") print(" sudo cp /var/lib/certain-finance/app.db /var/lib/certain-finance/app.db.kaputt") print(f" sudo cp {f} /var/lib/certain-finance/app.db") print(" sudo systemctl start certain-finance") else: print("Keine Sicherung enthält Schlagwörter.") print("Dann waren zum Sicherungszeitpunkt noch keine angelegt – die Zuordnung") print("lässt sich stattdessen nachträglich über die Schlagwörter neu erzeugen.")