75 lines
2.0 KiB
Python
75 lines
2.0 KiB
Python
"""Feature-Dateien: eine .md je Feature, Rohtext wird verbatim gespeichert."""
|
|
|
|
import re
|
|
|
|
import parser
|
|
import projekte
|
|
|
|
_UMLAUTE = str.maketrans({"ä": "ae", "ö": "oe", "ü": "ue", "ß": "ss"})
|
|
|
|
|
|
def _slug(titel: str) -> str:
|
|
s = titel.lower().translate(_UMLAUTE)
|
|
s = re.sub(r"[^a-z0-9]+", "-", s).strip("-")
|
|
return s or "feature"
|
|
|
|
|
|
def _eintrag(datei) -> dict:
|
|
roh = datei.read_text()
|
|
return {"id": datei.stem, "roh": roh, "sektionen": parser.zerlege(roh)}
|
|
|
|
|
|
def _datei(projekt: str, id: str):
|
|
datei = projekte.wurzel(projekt) / f"{id}.md"
|
|
if not datei.is_file():
|
|
raise FileNotFoundError(f"Feature „{id}“ nicht gefunden")
|
|
return datei
|
|
|
|
|
|
def liste(projekt: str) -> list[dict]:
|
|
dateien = projekte.wurzel(projekt).glob("*.md")
|
|
eintraege = [_eintrag(d) for d in sorted(dateien)]
|
|
return sorted(eintraege, key=lambda e: e["sektionen"]["titel"].lower())
|
|
|
|
|
|
def anlegen(projekt: str, roh: str) -> dict:
|
|
ordner = projekte.wurzel(projekt)
|
|
titel = parser.zerlege(roh)["titel"]
|
|
if not titel:
|
|
raise ValueError("Titel fehlt")
|
|
slug = _slug(titel)
|
|
datei = ordner / f"{slug}.md"
|
|
n = 2
|
|
while datei.exists():
|
|
datei = ordner / f"{slug}-{n}.md"
|
|
n += 1
|
|
datei.write_text(roh)
|
|
return _eintrag(datei)
|
|
|
|
|
|
def _folge_titel(datei, titel: str):
|
|
"""Benennt die Datei um, wenn der Slug nicht mehr zum Titel passt."""
|
|
slug = _slug(titel)
|
|
if re.fullmatch(re.escape(slug) + r"(-\d+)?", datei.stem):
|
|
return datei # passt schon (inkl. Kollisions-Suffix)
|
|
ziel = datei.parent / f"{slug}.md"
|
|
n = 2
|
|
while ziel.exists():
|
|
ziel = datei.parent / f"{slug}-{n}.md"
|
|
n += 1
|
|
datei.rename(ziel)
|
|
return ziel
|
|
|
|
|
|
def speichern(projekt: str, id: str, roh: str) -> dict:
|
|
datei = _datei(projekt, id)
|
|
datei.write_text(roh)
|
|
titel = parser.zerlege(roh)["titel"]
|
|
if titel:
|
|
datei = _folge_titel(datei, titel)
|
|
return _eintrag(datei)
|
|
|
|
|
|
def loeschen(projekt: str, id: str):
|
|
_datei(projekt, id).unlink()
|