Erster Stand des öffentlichen Ausschnitts aus dem Vault. Die .gitignore arbeitet als Positivliste: ausgeschlossen ist alles, eingeschlossen nur, was ausdrücklich wieder aufgenommen wird. Notizen, Projekte, Finanzen, das Sitzungsgedächtnis und settings.local.json bleiben damit außen vor. Der Kundenname in AI/Regelwerk/seo.md ist durch eine neutrale Bezeichnung ersetzt; die Regeln selbst sind unverändert. Co-Authored-By: Claude Opus 5 <[email protected]>
78 lines
2.5 KiB
Python
78 lines
2.5 KiB
Python
"""Der Stand des letzten Laufs — die Basis des Drei-Wege-Vergleichs."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
from .abgleich import Basiseintrag
|
|
|
|
DATEINAME = ".plane-sync.json"
|
|
VERSION = 1
|
|
|
|
|
|
class Zustand:
|
|
def __init__(self, pfad: Path, roh: dict, warnung: str | None = None):
|
|
self.pfad = pfad
|
|
self.warnung = warnung
|
|
self.plane_projekt: str | None = roh.get("plane_project")
|
|
self.eintraege: dict[str, Basiseintrag] = {
|
|
marker: Basiseintrag(
|
|
marker=marker,
|
|
work_item_id=werte["work_item_id"],
|
|
titel=werte["titel"],
|
|
erledigt=bool(werte["erledigt"]),
|
|
)
|
|
for marker, werte in (roh.get("eintraege") or {}).items()
|
|
}
|
|
self._letzter_lauf: str | None = roh.get("letzter_lauf")
|
|
|
|
def setze(self, eintrag: Basiseintrag) -> None:
|
|
"""Trägt einen Eintrag ein und speichert sofort — das macht den Lauf wiederaufsetzbar."""
|
|
self.eintraege[eintrag.marker] = eintrag
|
|
self.speichere()
|
|
|
|
def entferne_marker(self, marker: str) -> None:
|
|
self.eintraege.pop(marker, None)
|
|
self.speichere()
|
|
|
|
def setze_lauf_zeit(self) -> None:
|
|
self._letzter_lauf = datetime.now().astimezone().isoformat(timespec="seconds")
|
|
self.speichere()
|
|
|
|
def speichere(self) -> None:
|
|
inhalt = {
|
|
"version": VERSION,
|
|
"plane_project": self.plane_projekt,
|
|
"letzter_lauf": self._letzter_lauf,
|
|
"eintraege": {
|
|
marker: {
|
|
"work_item_id": e.work_item_id,
|
|
"titel": e.titel,
|
|
"erledigt": e.erledigt,
|
|
}
|
|
for marker, e in sorted(self.eintraege.items())
|
|
},
|
|
}
|
|
self.pfad.write_text(
|
|
json.dumps(inhalt, ensure_ascii=False, indent=2) + "\n", encoding="utf-8"
|
|
)
|
|
|
|
|
|
def lies(pfad: Path) -> Zustand:
|
|
pfad = Path(pfad)
|
|
if not pfad.exists():
|
|
return Zustand(pfad, {})
|
|
try:
|
|
return Zustand(pfad, json.loads(pfad.read_text(encoding="utf-8")))
|
|
except (json.JSONDecodeError, KeyError, TypeError) as fehler:
|
|
return Zustand(
|
|
pfad,
|
|
{},
|
|
warnung=(
|
|
f"{pfad} ist unlesbar ({fehler}). Der Lauf behandelt sie als leer; "
|
|
"dadurch erscheinen bereits abgeglichene Aufgaben erneut."
|
|
),
|
|
)
|