Files
Orchestrator/bahn/wissensdatenbank/scripts/issue_to_source.py

150 lines
5.1 KiB
Python

"""Wandelt ein ausgefuelltes 'Neues Wissen'-Issue in einen tools.yaml-Eintrag.
Kern der Automatisierung (Idee): Issue -> yaml-Eintrag -> MR (mit Vorschau).
Aufruf:
# Issue-Text aus Datei oder STDIN:
python scripts/issue_to_source.py < issue.md
glab issue view 42 | python scripts/issue_to_source.py
# direkt an config/tools.yaml anhaengen:
python scripts/issue_to_source.py --append < issue.md
Erwartet die Abschnitte der Vorlage (.gitlab/issue_templates/Neues_Wissen.md):
Tool/Domaene, Link(s), Verarbeitung (Strategie), Sichtbarkeit (Scope),
Ansprechpartner, Optionen, Hinweise.
"""
from __future__ import annotations
import argparse
import re
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from src.strategy_detect import detect_strategy, sniff_strategy # noqa: E402
FIELD_MAP = {
"tool": ["welches tool", "tool", "domaene", "domäne"],
"sources": ["quellen", "quelle", "link"],
"owners": ["verantwortlich", "owners"],
"contact": ["kontakt", "contact"],
"options": ["optionen"],
}
def _slug(s: str) -> str:
s = re.sub(r"[^a-z0-9]+", "-", s.lower())
return s.strip("-")[:40] or "neues-wissen"
def parse_issue(text: str) -> dict:
"""Zerlegt den Issue-Text in Felder (anhand der ###-Ueberschriften).
Quellen-Zeilen haben das Format: URL | Strategie | Scope
(Strategie/Scope optional). Fuehrendes "- " (Markdown-Liste) wird entfernt.
"""
sections: dict[str, str] = {}
cur = None
buf: list[str] = []
for line in text.splitlines():
m = re.match(r"^#{2,4}\s+(.*)", line.strip())
if m:
if cur:
sections[cur] = "\n".join(buf).strip()
cur = m.group(1).strip().lower()
buf = []
elif cur:
if line.strip().startswith("<!--") or line.strip().startswith("/label"):
continue
buf.append(line)
if cur:
sections[cur] = "\n".join(buf).strip()
def find(key: str) -> str:
for head, val in sections.items():
if any(alias in head for alias in FIELD_MAP[key]):
return val.strip()
return ""
sources = []
for raw in find("sources").splitlines():
ln = raw.strip().lstrip("-").strip()
if "http" not in ln:
continue
parts = [p.strip() for p in ln.split("|")]
url = parts[0].strip()
if not url.startswith("http"):
continue
strat = parts[1].strip() if len(parts) > 1 and parts[1].strip() else ""
scope = parts[2].strip() if len(parts) > 2 and parts[2].strip() else ""
sources.append({"url": url, "strategy": strat, "scope": scope})
owners = [o.strip() for o in re.split(r"[,\n]", find("owners")) if o.strip()]
contact = find("contact").splitlines()[0].strip() if find("contact") else ""
return {
"tool": find("tool").splitlines()[0].strip() if find("tool") else "",
"sources": sources,
"owners": owners,
"contact": contact,
}
def build_entry(data: dict, sniff: bool = False) -> str:
from src.strategy_detect import KNOWN
tool = data["tool"] or "neues-tool"
sid = _slug(tool)
owners = ", ".join(f'"{o}"' for o in data["owners"])
contact = data.get("contact", "").strip()
detect = sniff_strategy if sniff else detect_strategy
lines = [
f" - id: {sid}",
f' name: "{tool}"',
f" domain: {sid}",
" scope: mixed",
f" owners: [{owners}]",
]
if contact:
lines.append(f' contact: "{contact}"')
lines.append(" sources:")
unknown = []
for src in data["sources"] or [{"url": "<URL_FEHLT>", "strategy": "", "scope": ""}]:
url = src["url"]
strat = src["strategy"] if src["strategy"] in KNOWN else (detect(url) or "UNBEKANNT")
scope = src["scope"] or "intern"
if strat == "UNBEKANNT":
unknown.append(url)
lines.append(f" # !! Keine passende Strategie erkannt -> neue Strategie pruefen: {url}")
lines += [
f' - url: "{url}"',
f" strategy: {strat}",
f' scope: "{scope}"',
f' tags: ["{sid}"]',
]
out = "\n".join(lines) + "\n"
if unknown:
out += "\n# WARNUNG: " + str(len(unknown)) + " URL(s) ohne passende Strategie -> ggf. neue Strategie bauen.\n"
return out
def main() -> None:
parser = argparse.ArgumentParser(description="Issue -> tools.yaml-Eintrag")
parser.add_argument("--append", action="store_true", help="an config/tools.yaml anhaengen")
parser.add_argument("--sniff", action="store_true", help="Strategie per Netzwerk verfeinern (HEAD/GET)")
parser.add_argument("--config", default="config/tools.yaml")
args = parser.parse_args()
text = sys.stdin.read()
entry = build_entry(parse_issue(text), sniff=args.sniff)
if args.append:
with open(args.config, "a", encoding="utf-8") as f:
f.write("\n" + entry)
print(f"angehaengt an {args.config}:\n\n{entry}")
else:
print(entry)
if __name__ == "__main__":
main()