Files
GuidoandClaude Opus 4.8 ff95bc5344 feat: Backup-Prüfung nennt die neueste vollständige Sicherung
Bei 35 Sicherungen hilft eine blosse Liste wenig. Das Skript zeigt jetzt je
Sicherung Zeitpunkt, Schlagwörter, Budget-Vorlagen und wie viele Buchungen eine
Kategorie tragen – und nennt am Ende die neueste noch vollständige samt der
fertigen Befehle zum Zurückspielen. Tabellen, die es zum Sicherungszeitpunkt
noch nicht gab, erscheinen als '–' statt als Fehler.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 14:25:40 +02:00

79 lines
2.9 KiB
Python
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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.")