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>
79 lines
2.9 KiB
Python
Executable File
79 lines
2.9 KiB
Python
Executable File
#!/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.")
|