mindnet/scripts/make_test_vault.py
Lars 9362064ad1
Some checks failed
Deploy mindnet to llm-node / deploy (push) Failing after 2s
scripts/make_test_vault.py aktualisiert
2025-09-09 16:17:27 +02:00

228 lines
6.9 KiB
Python

#!/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()