8e268f044d
Doku-Drift behoben: die Statistik-Seite rechnet laengst echt (Behauptung stand an drei Stellen), INSTALL.md nannte in Schritt 4 vier Variablennamen, die es nie gab, README erwaehnte die entfernte 97-Prozent-Volltankungsregel und liess fuenf pyscript-Dateien in der Uebersicht aus. Obsoleter TODO-Kommentar in belegverarbeitung.py entfernt. profil_lesen() gibt bei fehlender oder beschaedigter Profildatei None zurueck statt zu werfen; alle sieben Aufrufstellen fangen den Fall ab. An der Testinstanz geprueft: Datei entfernt, es folgt eine verstaendliche Fehlermeldung mit Verweis auf INSTALL.md statt eines Tracebacks pro Trigger-Durchlauf, Home Assistant laeuft normal weiter.
97 lines
3.7 KiB
Python
97 lines
3.7 KiB
Python
"""Backup-Funktion für Fahrzeugprofil, Fahrten und Tankvorgänge.
|
|
|
|
Manuell über "Backup jetzt erstellen" in den Einstellungen oder automatisch
|
|
je nach einstellungen.backup_intervall im Fahrzeugprofil (aus/täglich/
|
|
wöchentlich/monatlich) - immer um 04:00 Uhr, wenn eingestellt. Ein einzelner
|
|
täglicher Trigger um 04:00 genügt für alle drei Intervalle: wöchentlich läuft
|
|
nur montags, monatlich nur am Monatsersten, täglich jeden Tag - so reicht eine
|
|
einzige Trigger-Registrierung statt drei.
|
|
|
|
Jedes Backup ist ein eigener Zeitstempel-Unterordner mit Kopien der drei
|
|
Bestände (siehe profil.py), keine ZIP-Datei - das würde den vorhandenen
|
|
task.executor(io.open, ...)-Weg (siehe dessen Kopfkommentar in profil.py)
|
|
unnötig verkomplizieren. shutil.copy() ist wie io.open eine echte externe
|
|
Funktion und läuft deshalb über denselben task.executor-Weg.
|
|
|
|
"Backup exportieren"/"importieren" in der Oberfläche laufen dagegen rein
|
|
über den Browser (aktuell geladenes Profil + Fahrten + Tankvorgänge als eine
|
|
JSON-Datei, siehe audi-dashboard-app.js) - audi_dashboard_backup_wiederherstellen()
|
|
hier ist nur das Gegenstück zum Import, das die drei Bestände wieder
|
|
komplett überschreibt.
|
|
"""
|
|
|
|
import datetime
|
|
import json
|
|
import os
|
|
import shutil
|
|
|
|
import frontend_veroeffentlichung
|
|
import profil
|
|
|
|
BACKUP_ORDNER = f"{profil.BASIS}/backups"
|
|
_DATEIEN = ["fahrzeugprofil.json", "fahrten.jsonl", "tankvorgaenge.jsonl"]
|
|
|
|
|
|
def _backup_jetzt():
|
|
ziel = f"{BACKUP_ORDNER}/{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
|
os.makedirs(ziel, exist_ok=True)
|
|
for name in _DATEIEN:
|
|
quelle = f"{profil.BASIS}/{name}"
|
|
if os.path.exists(quelle):
|
|
task.executor(shutil.copy, quelle, f"{ziel}/{name}")
|
|
return ziel
|
|
|
|
|
|
def _letztes_backup_vermerken():
|
|
p = profil.profil_lesen()
|
|
if p is None:
|
|
return
|
|
p.setdefault("einstellungen", {})["letztes_backup"] = datetime.datetime.now().isoformat()
|
|
profil.profil_schreiben(p)
|
|
frontend_veroeffentlichung.profil_veroeffentlichen()
|
|
|
|
|
|
@service
|
|
def audi_dashboard_backup_jetzt():
|
|
"""Erstellt sofort ein Backup. Aufruf als pyscript.audi_dashboard_backup_jetzt."""
|
|
ziel = _backup_jetzt()
|
|
_letztes_backup_vermerken()
|
|
log.info(f"audi_dashboard: Backup erstellt unter {ziel}")
|
|
|
|
|
|
@time_trigger("cron(0 4 * * *)")
|
|
def _automatisches_backup():
|
|
p = profil.profil_lesen()
|
|
if p is None:
|
|
return
|
|
intervall = p.get("einstellungen", {}).get("backup_intervall", "aus")
|
|
heute = datetime.date.today()
|
|
faellig = (
|
|
intervall == "taeglich"
|
|
or (intervall == "woechentlich" and heute.weekday() == 0)
|
|
or (intervall == "monatlich" and heute.day == 1)
|
|
)
|
|
if not faellig:
|
|
return
|
|
ziel = _backup_jetzt()
|
|
_letztes_backup_vermerken()
|
|
log.info(f"audi_dashboard: automatisches Backup ({intervall}) erstellt unter {ziel}")
|
|
|
|
|
|
@service
|
|
def audi_dashboard_backup_wiederherstellen(profil_json=None, fahrten_json=None, tankvorgaenge_json=None):
|
|
"""Stellt ein über "Backup exportieren" heruntergeladenes Backup wieder
|
|
her - überschreibt Profil, Fahrten und Tankvorgänge komplett. Aufruf als
|
|
pyscript.audi_dashboard_backup_wiederherstellen."""
|
|
if profil_json:
|
|
p = json.loads(profil_json) if isinstance(profil_json, str) else profil_json
|
|
profil.profil_schreiben(p)
|
|
if fahrten_json is not None:
|
|
fahrten = json.loads(fahrten_json) if isinstance(fahrten_json, str) else fahrten_json
|
|
profil.fahrten_schreiben(fahrten)
|
|
if tankvorgaenge_json is not None:
|
|
tankvorgaenge = json.loads(tankvorgaenge_json) if isinstance(tankvorgaenge_json, str) else tankvorgaenge_json
|
|
profil.tankvorgaenge_schreiben(tankvorgaenge)
|
|
frontend_veroeffentlichung.alles_veroeffentlichen()
|
|
log.info("audi_dashboard: Backup wiederhergestellt")
|