"""EINMALIGES Bootstrap-Skript: erzeugt config/tools.yaml aus der Support-Seite. Findet alle "Tool "-Unterseiten von Seite 428909879 und schreibt pro Tool einen Katalogeintrag (Name OHNE "Tool "-Praefix). Tool-weiter Scope = intern (restriktiv). Danach wird config/tools.yaml MANUELL gepflegt. Aufruf: CONFLUENCE_URL=... CONFLUENCE_TOKEN=... python scripts/bootstrap_tools.py """ from __future__ import annotations import os import re from atlassian import Confluence SUPPORT_PAGE = "428909879" BASE = "https://arija-confluence.jaas.service.deutschebahn.com/spaces/BES/pages" HEADER = """\ # ============================================================================= # TOOL-KATALOG der Wissensdatenbank # Initial generiert aus 'Zentraler 1st Level Support' (428909879). # AB JETZT MANUELL PFLEGEN. Allgemeines Wissen steht in config/general.yaml. # # AUFBAU PRO TOOL: # - id / name / domain # scope: intern # tool-weit: intern | extern | allgemein | mixed # owners: ["a@db.de"] # intern Verantwortliche # contact: "team@db.de" # herausgebbarer Kontakt (Default einfachbahn@deutschebahn.com) # sources: # 1..n Quellen, beliebig mischbar # - url: "..." # strategy: confluence_tree # confluence_page|confluence_tree|confluence_faq| # # crawler|sitemap|gitlab_md|file|pdf # # scope/Optionen pro Quelle optional ueberschreibbar # # SCOPE: # intern -> alles intern (Default, restriktiv; z.B. CRM Salesforce) # extern -> alles extern # allgemein -> gilt fuer intern UND extern # mixed -> Tool hat Quellen mit unterschiedlichem Scope (KEINE Trennung pro Seite; # im Zweifel zwei Quellen). Source-scope kann "intern,extern" sein. # # confluence_tree-Tiefe: options.max_depth (-1=alle Unterseiten, 0=nur Seite) # ============================================================================= tools: """ def slug(s: str) -> str: s = re.sub(r"^tool\s+", "", s.strip(), flags=re.I) s = re.sub(r"[^a-z0-9]+", "-", s.lower()) return s.strip("-")[:40] def clean_name(s: str) -> str: return re.sub(r"^tool\s+", "", s.strip(), flags=re.I).strip() def walk(client, pid, out=None): out = out if out is not None else [] for ch in client.get_child_pages(pid) or []: out.append((ch.get("id"), ch.get("title"))) walk(client, ch.get("id"), out) return out def main() -> None: client = Confluence( url=os.environ["CONFLUENCE_URL"], token=os.environ["CONFLUENCE_TOKEN"], cloud=False ) tools = [(pid, t.strip()) for pid, t in walk(client, SUPPORT_PAGE) if t.strip().lower().startswith("tool")] lines = [HEADER.rstrip("\n")] seen = set() for pid, title in tools: sid = slug(title) if sid in seen: sid = f"{sid}-{pid}" seen.add(sid) url = f"{BASE}/{pid}/" + re.sub(r"[^A-Za-z0-9]+", "+", title).strip("+") lines += [ f" - id: {sid}", f' name: "{clean_name(title)}"', f" domain: {sid}", " scope: intern", " sources:", f' - url: "{url}"', " strategy: confluence_tree", f' tags: ["{sid}"]', ] with open("config/tools.yaml", "w", encoding="utf-8") as f: f.write("\n".join(lines) + "\n") print(f"config/tools.yaml geschrieben: {len(tools)} Tools (alle scope: intern)") if __name__ == "__main__": main()