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>
This commit is contained in:
Regular → Executable
+75
-19
@@ -1,22 +1,78 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Listet je Backup, wie viele Schlagwörter/Budget-Vorlagen/kategorisierte
|
"""Prüft die Datenbank-Sicherungen auf Vollständigkeit.
|
||||||
Buchungen darin stecken. So sieht man, welches Backup noch heil ist."""
|
|
||||||
import glob, os, sqlite3, sys
|
|
||||||
|
|
||||||
muster = sys.argv[1] if len(sys.argv) > 1 else "/var/lib/certain-finance/backups/*.db"
|
Listet je Sicherung, wie viele Schlagwörter, Budget-Vorlagen und kategorisierte
|
||||||
print(f"{'Datei':<34}{'Schlagw.':>9}{'Budget':>8}{'Buch.m.Kat':>12}")
|
Buchungen darin stecken, und nennt am Ende die NEUESTE noch vollständige. So
|
||||||
for f in sorted(glob.glob(muster)):
|
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:
|
try:
|
||||||
con = sqlite3.connect(f"file:{f}?mode=ro", uri=True)
|
con = sqlite3.connect(f"file:{pfad}?mode=ro", uri=True)
|
||||||
def z(sql):
|
except sqlite3.Error:
|
||||||
try:
|
return None
|
||||||
return con.execute(sql).fetchone()[0]
|
def z(sql: str):
|
||||||
except sqlite3.Error:
|
try:
|
||||||
return "-"
|
return con.execute(sql).fetchone()[0]
|
||||||
print(f"{os.path.basename(f):<34}"
|
except sqlite3.Error:
|
||||||
f"{z('SELECT COUNT(*) FROM categorization_rules'):>9}"
|
return None # Tabelle gab es zu dem Zeitpunkt noch nicht
|
||||||
f"{z('SELECT COUNT(*) FROM budget_template_lines'):>8}"
|
werte = {
|
||||||
f"{z('SELECT COUNT(*) FROM transactions WHERE category_id IS NOT NULL'):>12}")
|
"schlagwoerter": z("SELECT COUNT(*) FROM categorization_rules"),
|
||||||
con.close()
|
"budget": z("SELECT COUNT(*) FROM budget_template_lines"),
|
||||||
except Exception as e:
|
"kategorisiert": z("SELECT COUNT(*) FROM transactions WHERE category_id IS NOT NULL"),
|
||||||
print(f"{os.path.basename(f):<34} Fehler: {e}")
|
"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.")
|
||||||
|
|||||||
Reference in New Issue
Block a user