#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ make_test_vault.py — erzeugt einen minimalen, nachvollziehbaren Test-Vault Version: 1.2 (2025-09-09) Änderungen ggü. 1.1: - Fügt **externe Links** (http/mailto) in `exp-two.md` hinzu, damit der Chunk-Payload-Exporter `external_links` realistisch testen kann. - Kleine Textanpassungen zur stabilen Chunk-Bildung (Absatzstruktur). Zweck - Kleiner Obsidian-ähnlicher Vault zum Durchspielen des Importers (Chunks/Edges). - Szenarien: leere Links, spätere Anlage fehlender Noten, Chunk-Neuaufteilung, externe Links. Struktur (Default: ./test_vault) - 40_concepts/concept-alpha.md - 20_experiences/exp-one.md (verlinkt auf [[concept-alpha]] und [[missing-note]]) - 20_experiences/exp-two.md (verlinkt auf [[concept-alpha]] + externe Links) - 30_projects/project-demo.md (verlinkt auf [[concept-alpha]] und [[exp-one]]) Voraussetzungen - Keine. Rein Dateigenerierung. Aufruf python3 -m scripts.make_test_vault [--out ./test_vault] [--force] [--with-missing] Parameter --out Zielverzeichnis (Standard: ./test_vault) --force Bestehenden Ordner löschen und neu anlegen. --with-missing Auch die 'missing-note' direkt anlegen (Standard: erst im 2. Lauf) Hinweise für Tests 1) Erster Import (dry-run oder --apply): Es gibt einen „leeren“ Link [[missing-note]]. 2) Lege danach eine Datei für „missing-note“ an (mit gleicher ID im YAML) und importiere erneut: -> Erwartung: Edges für ehemals leeren Link werden korrekt nachgezogen (references + backlink). 3) Ändere den Body von exp-one.md so, dass andere Chunk-Grenzen entstehen und importiere erneut: -> Erwartung: Chunks/Edges/Note werden für betroffene Noten aktualisiert. 4) Prüfe externe Links (verify_chunk_texts.py betrifft nur Text; externe Links siehst du in chunk_payloads). Kompatibel mit: - note.schema.json: 'created'/'updated' müssen strings sein. (Wichtig!) """ from __future__ import annotations import argparse import os import shutil from datetime import datetime, timezone def iso_now() -> str: # ISO 8601 mit Offset; als String zurückgeben return datetime.now(timezone.utc).astimezone().isoformat(timespec="seconds") def q(s: str) -> str: # Sicheres Quoten für YAML-Frontmatter return f"\"{s}\"" TEMPLATE_CONCEPT = """--- title: "Concept Alpha" id: "concept-alpha" type: "concept" status: "active" created: {created} updated: {updated} tags: ["area/mindnet","type/concept","topic/test"] --- ## Beschreibung Dies ist ein einfaches Konzept für Testzwecke. Es wird von anderen Notizen referenziert. ## Mögliche Verbindungen - [[exp-one]] - [[project-demo]] """ TEMPLATE_EXP_ONE = """--- title: "Experience One" id: "exp-one" type: "experience" status: "active" created: {created} updated: {updated} tags: ["area/mindnet","type/experience","topic/test"] --- ## Kontext Diese Notiz verlinkt auf ein existierendes Konzept und auf eine noch **fehlende** Note. ## Beobachtung - Bezug zu [[concept-alpha]] - Offener Verweis auf [[missing-note]] ## Interpretation Wenn später eine Datei für 'missing-note' angelegt wird, sollte der Importer die Kanten nachziehen. ## Mögliche Verbindungen - [[concept-alpha]] - [[missing-note]] """ TEMPLATE_EXP_TWO = """--- title: "Experience Two" id: "exp-two" type: "experience" status: "active" created: {created} updated: {updated} tags: ["area/mindnet","type/experience","topic/test"] --- ## Kontext Zweite Experience, die auf dasselbe Konzept verweist, ergänzt um **externe Links**. ## Beobachtung Verweis auf [[concept-alpha]]. Zusätzliche Ressourcen: - Link: [Beispielseite](https://example.com "Referenz") - Kontakt: [E-Mail](mailto:info@example.com) ## Mögliche Verbindungen - [[concept-alpha]] """ TEMPLATE_PROJECT = """--- title: "Project Demo" id: "project-demo" type: "project" status: "active" created: {created} updated: {updated} tags: ["area/mindnet","type/project","topic/test"] --- ## Scope Ein kleines Demo-Projekt, das andere Notizen referenziert. ## Arbeitspakete - AP-1: Zusammenhang erklären (siehe [[concept-alpha]]) - AP-2: Ereignis berücksichtigen (siehe [[exp-one]]) ## Mögliche Verbindungen - [[concept-alpha]] - [[exp-one]] """ TEMPLATE_MISSING = """--- title: "Missing Note (jetzt angelegt)" id: "missing-note" type: "concept" status: "active" created: {created} updated: {updated} tags: ["area/mindnet","type/concept","topic/test"] --- ## Kontext Diese Notiz wurde **nachträglich** erstellt, um einen zuvor offenen Link zu schließen. ## Mögliche Verbindungen - [[exp-one]] """ def write_file(path: str, content: str) -> None: os.makedirs(os.path.dirname(path), exist_ok=True) with open(path, "w", encoding="utf-8") as f: f.write(content) def build_vault(base: str, include_missing_note: bool) -> None: created = q(iso_now()) updated = q(iso_now()) # 40_concepts write_file( os.path.join(base, "40_concepts", "concept-alpha.md"), TEMPLATE_CONCEPT.format(created=created, updated=updated), ) # 20_experiences write_file( os.path.join(base, "20_experiences", "exp-one.md"), TEMPLATE_EXP_ONE.format(created=created, updated=updated), ) write_file( os.path.join(base, "20_experiences", "exp-two.md"), TEMPLATE_EXP_TWO.format(created=created, updated=updated), ) # 30_projects write_file( os.path.join(base, "30_projects", "project-demo.md"), TEMPLATE_PROJECT.format(created=created, updated=updated), ) # Optional: zunächst NICHT anlegen (Szenario „leerer Link“), # später für den 2. Testlauf anlegen. if include_missing_note: write_file( os.path.join(base, "40_concepts", "missing-note.md"), TEMPLATE_MISSING.format(created=created, updated=updated), ) def main(): ap = argparse.ArgumentParser() ap.add_argument("--out", default="./test_vault", help="Zielverzeichnis für den Test-Vault") ap.add_argument("--force", action="store_true", help="Falls vorhanden: Verzeichnis löschen und neu anlegen") ap.add_argument("--with-missing", action="store_true", help="Auch die 'missing-note' direkt anlegen (Standard: erst im 2. Lauf)") args = ap.parse_args() base = os.path.abspath(args.out) if os.path.exists(base): if args.force: shutil.rmtree(base) else: print(f"Ziel existiert bereits: {base}\nNutze --force zum Überschreiben.") return build_vault(base, include_missing_note=args.with_missing) print(f"Test-Vault erstellt unter: {base}") print("Hinweis:") print(" 1) Zuerst ohne missing-note anlegen (Standard), importieren, Edges prüfen.") print(" 2) Dann erneut ausführen mit --force --with-missing (missing-note wird erzeugt),") print(" und Import wiederholen → Edges/Backlinks sollten nachgezogen werden.") print(" 3) In exp-two.md sind **externe Links** enthalten → prüfe `external_links` in Chunk-Payloads.") if __name__ == "__main__": main()