64f457dd87
Drei Befunde aus REVIEW_main_2026-08-13.md, alle an der Testinstanz geprueft. Zuruecksetzen (Befund 2 und 5): overrides_anwenden() uebersprang leere Werte und setzte nur belegte. Weil setattr das Modul-Attribut dauerhaft veraendert, blieb ein einmal gesetzter Wert danach fuer immer stehen - bei 15 von 17 Feldern hatte Zuruecksetzen keine Wirkung, und die Oberflaeche zeigte wieder den alten Wert, als sei das Speichern fehlgeschlagen. Umgekehrt wurde eine Liste aus leeren Eintraegen gesetzt statt uebersprungen, was den Sicherheitscheck mit zwoelf unbekannt-Zeilen fuellte. Jetzt wird fuer jedes bekannte Feld geschrieben: der Override, wenn belegt, sonst der eingebaute Standardwert. Damit ist der Vorgang zugleich wiederholbar. Sicherung (Befund 6): entitaeten.json war weder in backup.py noch in der Wiederherstellung noch im Browser-Export enthalten - die gesamte Zuordnung waere nach einem Rueckspielen still weg gewesen. Jetzt ueberall dabei; fehlt der Abschnitt in aelteren Sicherungen, bleibt die aktuelle Zuordnung stehen. Absicherung (Befund 7): overrides_lesen() fing kaputtes JSON nicht ab. Der Aufruf steht in beim_start() vor alles_veroeffentlichen() - eine unlesbare Zeile liess das Panel komplett leer bleiben. Geprueft: es folgt jetzt eine verstaendliche Meldung, alle sechs Entitaeten werden trotzdem veroeffentlicht. Dabei ein pyscript-Fallstrick gefunden und im Code vermerkt: Generator- ausdruecke sind nicht implementiert (not implemented ast ast_generatorexp), Mengen-, Listen- und Dict-Comprehensions dagegen schon.
110 lines
4.4 KiB
Python
110 lines
4.4 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 entitaeten
|
|
import frontend_veroeffentlichung
|
|
import profil
|
|
|
|
BACKUP_ORDNER = f"{profil.BASIS}/backups"
|
|
# entitaeten.json gehört dazu: Ohne sie wäre nach einer Wiederherstellung die
|
|
# komplette Sensor-Zuordnung aus dem Setup-Menü verloren, und weil sie sonst
|
|
# nirgends abgelegt ist, gäbe es nichts zurückzuholen.
|
|
_DATEIEN = [
|
|
"fahrzeugprofil.json",
|
|
"fahrten.jsonl",
|
|
"tankvorgaenge.jsonl",
|
|
"entitaeten.json",
|
|
]
|
|
|
|
|
|
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()
|
|
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()
|
|
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, entitaeten_json=None):
|
|
"""Stellt ein über "Backup exportieren" heruntergeladenes Backup wieder
|
|
her - überschreibt Profil, Fahrten, Tankvorgänge und die Sensor-Zuordnung
|
|
komplett. Aufruf als pyscript.audi_dashboard_backup_wiederherstellen.
|
|
|
|
entitaeten_json ist optional: ältere Sicherungen, die vor dem Setup-Menü
|
|
entstanden sind, enthalten den Abschnitt nicht - dann bleibt die aktuelle
|
|
Zuordnung unangetastet, statt sie zu leeren."""
|
|
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)
|
|
if entitaeten_json:
|
|
zuordnung = json.loads(entitaeten_json) if isinstance(entitaeten_json, str) else entitaeten_json
|
|
entitaeten.overrides_schreiben(zuordnung)
|
|
entitaeten.overrides_anwenden()
|
|
frontend_veroeffentlichung.alles_veroeffentlichen()
|
|
log.info("audi_dashboard: Backup wiederhergestellt")
|