#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ FILE: scripts/parse_validate_notes.py VERSION: 2.1.0 (2025-12-15) STATUS: Active COMPATIBILITY: v2.9.1 (Post-WP14/WP-15b) Zweck: ------- Validiert alle Markdown-Dateien in einem Vault auf strukturelle Korrektheit. Prüft Frontmatter-Validität und Note-Payload-Erzeugung ohne Datenbank-Upsert. Funktionsweise: --------------- 1. Scannt rekursiv alle .md-Dateien im angegebenen Vault-Verzeichnis 2. Für jede Datei: - Liest und parst Markdown mit Frontmatter - Validiert erforderliche Frontmatter-Felder (id, title, type) - Erzeugt Note-Payload (validiert durch JSON-Serialisierung) 3. Gibt Statistik aus: Anzahl valider vs. invalider Dateien Ergebnis-Interpretation: ------------------------ - Exit-Code 0: Alle Dateien sind valide - Exit-Code 1: Mindestens eine Datei ist invalide - Ausgabe: "✅ Valid: X | ❌ Invalid: Y" - Fehlerdetails werden auf stderr ausgegeben Häufige Fehler: -------------- - Fehlende oder ungültige Frontmatter-Felder (id, title, type) - JSON-Serialisierungsfehler im Payload (z.B. ungültige Datentypen) - Encoding-Probleme beim Lesen der Datei Aufruf: ------- python3 -m scripts.parse_validate_notes --vault ./vault python3 -m scripts.parse_validate_notes --vault ./vault --include "**/*.md" --exclude /.obsidian/ /_imported/ Parameter: ---------- --vault PATH Pfad zum Vault-Verzeichnis (erforderlich) --include PATTERN Glob-Pattern für Dateien (Default: "**/*.md") --exclude PATH Pfade zum Ausschließen (Default: /.obsidian/, /_imported/) Änderungen: ----------- v2.1.0 (2025-12-15): Kompatibilität mit WP-14 Modularisierung - Entfernt: app.core.note_payload (ersetzt durch ingestion_note_payload) - Entfernt: app.core.validate_note (Validierung in make_note_payload integriert) - Entfernt: jsonschema.ValidationError (nicht mehr benötigt) v1.0.0: Erster Release """ from __future__ import annotations import argparse import os import sys import glob from app.core.parser import read_markdown, validate_required_frontmatter, normalize_frontmatter from app.core.ingestion.ingestion_note_payload import make_note_payload def iter_md_files(root: str, include: str, exclude: list[str]) -> list[str]: # include z.B. "**/*.md" all_files = glob.glob(os.path.join(root, include), recursive=True) def is_excluded(p: str) -> bool: p_norm = p.replace("\\", "/") return any(x in p_norm for x in exclude) return [p for p in all_files if p.lower().endswith(".md") and not is_excluded(p)] def main(): ap = argparse.ArgumentParser() ap.add_argument("--vault", required=True, help="Pfad zum Obsidian Vault") ap.add_argument("--include", default="**/*.md") ap.add_argument("--exclude", nargs="*", default=["/.obsidian/", "/_imported/"]) args = ap.parse_args() root = os.path.abspath(args.vault) files = iter_md_files(root, args.include, args.exclude) if not files: print("Keine Markdown-Dateien gefunden.", file=sys.stderr) sys.exit(2) ok, failed = 0, 0 for path in files: try: parsed = read_markdown(path) fm = normalize_frontmatter(parsed.frontmatter) validate_required_frontmatter(fm) # Note-Payload wird in make_note_payload bereits validiert (JSON-Serialisierung) payload = make_note_payload(parsed, vault_root=root, file_path=path) ok += 1 except (ValueError, TypeError, KeyError, AttributeError) as e: failed += 1 print(f"❌ {os.path.relpath(path, root)} :: {e}", file=sys.stderr) print(f"✅ Valid: {ok} | ❌ Invalid: {failed}") if failed: sys.exit(1) if __name__ == "__main__": main()