diff --git a/scripts/make_test_vault.py b/scripts/make_test_vault.py new file mode 100644 index 0000000..3043116 --- /dev/null +++ b/scripts/make_test_vault.py @@ -0,0 +1,245 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +make_test_vault.py — Erzeugt einen kleinen, reproduzierbaren Test-Vault für den Markdown-Importer. + +Version: 1.0.0 (2025-09-05) +Änderungen: +- Initiale Version mit 4 Szenarien (base, add-targets, change-chunking, cleanup) +- Frontmatter entspricht Parser-Anforderungen: title, id, type, status, created (+ optionale Felder) (vgl. parser.validate_required_frontmatter) +- Inhalte decken Wikilinks, zunächst fehlende Ziele (unresolved), spätere Zielanlage und Re-Chunking ab. + +Beschreibung: +Erstellt unter ./test_vault/ eine kleine Ordnerstruktur mit Markdown-Dateien: +- base: Legt 4 Notizen an. Zwei haben Wikilinks, wovon einige zunächst ins Leere zeigen (Ziel-Notizen existieren noch nicht). +- add-targets: Legt die vorher fehlenden Ziel-Notizen an, so dass der Importer Kanten (references/backlink und references_at) auflösen kann. +- change-chunking: Ändert eine bestehende Notiz so, dass sich die Chunk-Grenzen deutlich verschieben (Überschriften + Textblöcke). Dient zum Test, ob der Importer Notes/Chunks/Edges sauber nachzieht. +- cleanup: Löscht den gesamten test_vault wieder. + +Aufruf: + python3 -m scripts.make_test_vault --scenario base + python3 -m scripts.make_test_vault --scenario add-targets + python3 -m scripts.make_test_vault --scenario change-chunking + python3 -m scripts.make_test_vault --scenario cleanup + +Hinweise: +- Dieses Skript erzeugt KEINE Qdrant-Daten; es schreibt nur Markdown-Dateien. +- Zum Testen: + 1) base erzeugen → Importer (dry-run) → Audit vergleichen. + 2) add-targets erzeugen → Importer erneut laufen lassen → prüfen, ob vormals „unresolved“ jetzt aufgelöst wurden (edges & edges_at). + 3) change-chunking ausführen → Importer mit --apply → prüfen, ob Chunks/Edges umgezogen/aktualisiert wurden. +- Verwende beim Importer denselben Vault-Pfad: --vault ./test_vault +- Virtuelle Umgebung: Für dieses Skript selbst nicht nötig; für den Importer natürlich weiterhin in deinem venv laufen. + +""" + +from __future__ import annotations +import argparse +import os +import shutil +from pathlib import Path +from datetime import datetime + +ROOT = Path(__file__).resolve().parents[1] # Projektwurzel (.. /mindnet) +VAULT = ROOT / "test_vault" + +# ---------- Hilfsfunktionen ---------- + +def write_file(path: Path, content: str): + path.parent.mkdir(parents=True, exist_ok=True) + with open(path, "w", encoding="utf-8") as f: + f.write(content) + +def iso_now(): + return datetime.now().isoformat(timespec="seconds") + +# ---------- Inhalte (Szenario-Varianten) ---------- + +def file_index_base() -> str: + # Enthält Links auf bestehende und noch NICHT bestehende Ziele + return f"""--- +title: "Index – Mini-Testvault" +id: "test-index" +type: "concept" +status: "draft" +created: {iso_now()} +tags: ["area/test","type/concept"] +--- + +## Überblick +Dies ist der kleine Test-Vault. + +## Verweise +- [[test-alpha]] (existiert) +- [[test-beta]] (existiert) +- [[test-gamma]] (existiert) +- [[test-delta]] (FEHLT zunächst — wird erst in add-targets angelegt) +""" + +def file_alpha_base() -> str: + # Hat Wikilinks auf beta (existiert) und epsilon (fehlt zunächst) + return f"""--- +title: "Alpha" +id: "test-alpha" +type: "concept" +status: "draft" +created: {iso_now()} +tags: ["area/test","type/concept"] +--- + +## Abschnitt A +Alpha verweist auf [[test-beta]] und auf [[test-epsilon]] (FEHLT zunächst). + +## Abschnitt B +Noch etwas Text, damit beim Re-Chunking später Grenzen sichtbar werden. +""" + +def file_beta_base() -> str: + # Kurze Notiz ohne Links + return f"""--- +title: "Beta" +id: "test-beta" +type: "concept" +status: "draft" +created: {iso_now()} +tags: ["area/test","type/concept"] +--- + +Beta hat zunächst keine Links. +""" + +def file_gamma_base() -> str: + # Viele Abschnitte → erzeugt mehrere Chunks + return f"""--- +title: "Gamma" +id: "test-gamma" +type: "concept" +status: "draft" +created: {iso_now()} +tags: ["area/test","type/concept"] +--- + +## Einleitung +Dieser Abschnitt enthält einen Link zu [[test-index]]. + +## Details +Gamma liefert Details. Hier noch ein Link zu [[test-beta]]. + +## Weitere Hinweise +Hier steht Text, damit die Chunker-Logik genug Material hat. +""" + +def file_delta_added() -> str: + # Wird erst im Szenario add-targets erstellt (auflösen von vormals unresolved [[test-delta]]) + return f"""--- +title: "Delta (neu angelegt)" +id: "test-delta" +type: "concept" +status: "draft" +created: {iso_now()} +tags: ["area/test","type/concept"] +--- + +Delta wurde nachträglich angelegt, um ehemals leere Links aufzulösen. +""" + +def file_epsilon_added() -> str: + # Wird erst im Szenario add-targets erstellt (auflösen von [[test-epsilon]] aus Alpha) + return f"""--- +title: "Epsilon (neu angelegt)" +id: "test-epsilon" +type: "concept" +status: "draft" +created: {iso_now()} +tags: ["area/test","type/concept"] +--- + +Epsilon existiert nun und kann referenziert werden. +""" + +def file_gamma_changed_chunking() -> str: + # Ersetzt die ursprüngliche Gamma-Datei mit deutlich mehr (und anders geschnittenen) Abschnitten, + # so dass sich die Chunk-Grenzen ändern. + return f"""--- +title: "Gamma (rechunked)" +id: "test-gamma" +type: "concept" +status: "draft" +created: {iso_now()} +tags: ["area/test","type/concept"] +--- + +# Einleitung (H1 statt H2) +Kurzer Teaser. Verweis auf [[test-index]] bleibt bestehen. + +## Teil 1 – Kontext +Mehr Text. Dieser Abschnitt ist länger und soll den ursprünglichen „Details“-Block ersetzen. +Außerdem Verweis auf [[test-beta]]. + +## Teil 2 – Beispiele +- Beispiel 1: Ein etwas längerer Listenpunkt mit Beschreibung. +- Beispiel 2: Noch ein Punkt. +- Beispiel 3: Und noch einer. + +## Teil 3 – Fazit +Zusammenfassung und ggf. ein weiterer Verweis auf [[test-alpha]]. +""" + +# ---------- Szenarien ausführen ---------- + +def scenario_base(): + # Frisch anlegen / überschreiben + (VAULT / "notes").mkdir(parents=True, exist_ok=True) + write_file(VAULT / "notes" / "index.md", file_index_base()) + write_file(VAULT / "notes" / "alpha.md", file_alpha_base()) + write_file(VAULT / "notes" / "beta.md", file_beta_base()) + write_file(VAULT / "notes" / "gamma.md", file_gamma_base()) + print(f"[base] Angelegt: {VAULT}") + +def scenario_add_targets(): + # Nur neue Ziele hinzufügen (delta, epsilon) + if not VAULT.exists(): + print("test_vault fehlt – bitte erst --scenario base ausführen.") + return + write_file(VAULT / "notes" / "delta.md", file_delta_added()) + write_file(VAULT / "notes" / "epsilon.md", file_epsilon_added()) + print("[add-targets] Neue Zielnotizen delta/epsilon angelegt.") + +def scenario_change_chunking(): + # Gamma ersetzen, um Chunk-Grenzen zu verschieben + if not VAULT.exists(): + print("test_vault fehlt – bitte erst --scenario base ausführen.") + return + write_file(VAULT / "notes" / "gamma.md", file_gamma_changed_chunking()) + print("[change-chunking] gamma.md geändert (deutlich andere Abschnittsstruktur).") + +def scenario_cleanup(): + if VAULT.exists(): + shutil.rmtree(VAULT) + print("[cleanup] test_vault gelöscht.") + else: + print("[cleanup] nichts zu tun – test_vault existiert nicht.") + +# ---------- CLI ---------- + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument( + "--scenario", + required=True, + choices=["base", "add-targets", "change-chunking", "cleanup"], + help="Welches Setup erzeugt werden soll." + ) + args = ap.parse_args() + + if args.scenario == "base": + scenario_base() + elif args.scenario == "add-targets": + scenario_add_targets() + elif args.scenario == "change-chunking": + scenario_change_chunking() + elif args.scenario == "cleanup": + scenario_cleanup() + +if __name__ == "__main__": + main()