158 lines
4.9 KiB
Python
158 lines
4.9 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
FILE: scripts/edges_dryrun.py
|
|
VERSION: 2.1.0 (2025-12-15)
|
|
STATUS: Active
|
|
COMPATIBILITY: v2.9.1 (Post-WP14/WP-15b)
|
|
|
|
Zweck:
|
|
-------
|
|
Simuliert die Edge-Erzeugung aus Markdown-Dateien ohne Datenbank-Upsert.
|
|
Nützlich zur Analyse, welche Kanten aus einem Vault generiert würden.
|
|
|
|
Funktionsweise:
|
|
---------------
|
|
1. Scannt alle .md-Dateien im Vault
|
|
2. Für jede Datei:
|
|
- Parst Markdown mit Frontmatter
|
|
- Erstellt einfache Absatz-basierte Chunks (vereinfachtes Chunking)
|
|
- Extrahiert Edge-Kandidaten via build_edges_for_note():
|
|
* Wikilinks [[...]]
|
|
* Typed Relations [[rel:KIND|Target]]
|
|
* Callout Relations [!edge] KIND: [[Target]]
|
|
* Struktur-Kanten (belongs_to, next, prev)
|
|
- Zählt Edges nach Typ (kind/relation)
|
|
3. Gibt JSON-Report aus mit Edge-Statistiken pro Note
|
|
|
|
Ergebnis-Interpretation:
|
|
------------------------
|
|
- Ausgabe: JSON-Array mit einem Objekt pro Note
|
|
- Jedes Objekt enthält:
|
|
* path: Dateipfad
|
|
* note_id: Note-Identifier
|
|
* type: Note-Typ
|
|
* chunks: Anzahl der Chunks
|
|
* edges_total: Gesamtanzahl der Edges
|
|
* edges_by_kind: Dictionary mit Edge-Anzahl pro Typ
|
|
* samples: Erste 3 Edges als Beispiele
|
|
|
|
Verwendung:
|
|
-----------
|
|
- Analyse der Graph-Struktur vor dem Import
|
|
- Debugging von fehlenden oder unerwarteten Edges
|
|
- Validierung der Edge-Extraktion-Logik
|
|
|
|
Hinweise:
|
|
---------
|
|
- Verwendet vereinfachtes Chunking (Absatz-basiert), nicht das vollständige Chunking-System
|
|
- Edge-Extraktion entspricht der Produktions-Logik (app.core.derive_edges)
|
|
- Keine Datenbank-Operationen, rein analytisch
|
|
|
|
Aufruf:
|
|
-------
|
|
python3 -m scripts.edges_dryrun --vault ./vault
|
|
python3 -m scripts.edges_dryrun --vault ./vault --include-note-scope-refs
|
|
|
|
Parameter:
|
|
----------
|
|
--vault PATH Pfad zum Vault-Verzeichnis (erforderlich)
|
|
--include-note-scope-refs Berücksichtigt auch Frontmatter-Links (links[].target_id)
|
|
|
|
Änderungen:
|
|
-----------
|
|
v2.1.0 (2025-12-15): Kompatibilität mit WP-14 Modularisierung
|
|
- Geändert: app.core.edges → app.core.derive_edges
|
|
- Parameter korrigiert: chunk_payloads → chunks, note_level_refs → note_level_references
|
|
v1.0.0: Erster Release
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import os
|
|
import re
|
|
from pathlib import Path
|
|
from typing import Dict, List, Optional
|
|
|
|
from app.core.parser import read_markdown, normalize_frontmatter
|
|
from app.core.graph.graph_derive_edges import build_edges_for_note
|
|
|
|
def _iter_markdown(vault: str):
|
|
for p in Path(vault).rglob("*.md"):
|
|
if p.name.startswith("."): # ignore hidden
|
|
continue
|
|
yield p
|
|
|
|
def _simple_chunker(body: str, note_id: str, note_type: str) -> List[Dict]:
|
|
# Absatzbasiert, minimal ausreichend für Edges (window/text, chunk_id, ord, note_id, type)
|
|
paras = [s.strip() for s in re.split(r"\n{2,}", body or "") if s.strip()]
|
|
chunks = []
|
|
for i, t in enumerate(paras):
|
|
chunks.append({
|
|
"chunk_id": f"{note_id}#c{i:04d}",
|
|
"ord": i,
|
|
"text": t,
|
|
"note_id": note_id,
|
|
"type": note_type,
|
|
})
|
|
return chunks
|
|
|
|
def _fm_note_refs(fm: Dict) -> List[str]:
|
|
out = []
|
|
links = fm.get("links") or []
|
|
if isinstance(links, list):
|
|
for e in links:
|
|
if isinstance(e, dict):
|
|
tid = e.get("target_id")
|
|
if isinstance(tid, str) and tid.strip():
|
|
out.append(tid.strip())
|
|
return out
|
|
|
|
def main():
|
|
ap = argparse.ArgumentParser()
|
|
ap.add_argument("--vault", required=True)
|
|
ap.add_argument("--include-note-scope-refs", action="store_true")
|
|
args = ap.parse_args()
|
|
|
|
vault = args.vault
|
|
include_note_scope = args.include_note_scope_refs
|
|
|
|
report = []
|
|
for p in _iter_markdown(vault):
|
|
parsed = read_markdown(str(p))
|
|
if not parsed:
|
|
continue
|
|
fm = normalize_frontmatter(parsed.frontmatter or {})
|
|
note_id = fm.get("id") or p.stem
|
|
note_type = (fm.get("type") or "concept").lower()
|
|
chunks = _simple_chunker(parsed.body, note_id, note_type)
|
|
note_refs = _fm_note_refs(fm)
|
|
|
|
# WP-24c v4.2.0: Übergabe des Markdown-Bodys für Note-Scope Zonen
|
|
edges = build_edges_for_note(
|
|
note_id=note_id,
|
|
chunks=chunks,
|
|
note_level_references=note_refs,
|
|
include_note_scope_refs=include_note_scope,
|
|
markdown_body=parsed.body if parsed else None,
|
|
)
|
|
kinds = {}
|
|
for e in edges:
|
|
key = (e.get("relation") or e.get("kind") or "edge")
|
|
kinds[key] = kinds.get(key, 0) + 1
|
|
report.append({
|
|
"path": str(p),
|
|
"note_id": note_id,
|
|
"type": note_type,
|
|
"chunks": len(chunks),
|
|
"edges_total": len(edges),
|
|
"edges_by_kind": kinds,
|
|
"samples": edges[:3],
|
|
})
|
|
|
|
print(json.dumps(report, ensure_ascii=False, indent=2))
|
|
|
|
if __name__ == "__main__":
|
|
main()
|