From 81c1400ef4f2ccf4e7374dff16e7ec16641a0ccf Mon Sep 17 00:00:00 2001 From: Lars Date: Tue, 9 Sep 2025 16:55:36 +0200 Subject: [PATCH] app/core/note_payload.py aktualisiert --- app/core/note_payload.py | 129 ++++++++++++++++++++++++++++----------- 1 file changed, 95 insertions(+), 34 deletions(-) diff --git a/app/core/note_payload.py b/app/core/note_payload.py index 7f5c5e0..a66ddea 100644 --- a/app/core/note_payload.py +++ b/app/core/note_payload.py @@ -1,47 +1,36 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- # Modul: app/core/note_payload.py -# Version: 1.6.1 +# Version: 1.6.3 # Datum: 2025-09-09 # -# Kurzbeschreibung -# ---------------- +# Zweck +# ----- # Erzeugt den Qdrant-Payload für Notes inkl. robuster Hash-Bildung zur -# Änderungserkennung. Der vollständige Body wird unter "fulltext" persistiert; -# der Pfad ist relativ (für verlustfreien Export). +# Änderungserkennung. Der vollständige (parsed) Body wird unter "fulltext" +# persistiert; der Pfad ist relativ (für verlustfreien Export). # -# Wichtige Punkte -# --------------- -# - Nur Inhalte fließen in den Hash ein (keine FS-Zeitstempel). -# - Vergleichsarten: -# Body -> nur Body -# Frontmatter -> nur Frontmatter -# Full -> Body + Frontmatter -# Steuerbar per CLI/ENV: +# Steuerung (CLI/ENV, vom Importer durchgereicht) +# ----------------------------------------------- +# - Vergleichsmodus: # --hash-mode body|frontmatter|full -# MINDNET_HASH_MODE / MINDNET_HASH_COMPARE (Body|Frontmatter|Full) +# ENV: MINDNET_HASH_MODE oder MINDNET_HASH_COMPARE (Body|Frontmatter|Full) +# "full" ist Alias für "body+frontmatter". # - Hash-Quelle: -# parsed (Default) -> Parser-Body -# raw -> Rohdatei-Body (Frontmatter via Regex entfernt) -# Steuerbar per: -# --hash-source parsed|raw -# MINDNET_HASH_SOURCE +# --hash-source parsed|raw (ENV: MINDNET_HASH_SOURCE) # - Normalisierung: -# canonical (Default) -> \r\n->\n, trailing spaces pro Zeile entfernt -# none -> keine Normalisierung (jede Kleinigkeit zählt) -# Steuerbar per: -# --hash-normalize canonical|none -# MINDNET_HASH_NORMALIZE +# --hash-normalize canonical|none (ENV: MINDNET_HASH_NORMALIZE) # -# Neu in v1.6.x -# ------------- -# - "hash_signature" im Payload, z. B. "body:raw:none:". -# - Optional (ENV MINDNET_HASH_RECORD_ALL=true): zusätzliches Hash-Set für Debug: -# payload["hashes"] = { -# "body_parsed": "...", "body_raw": "...", -# "frontmatter": "...", -# "full_parsed": "...", "full_raw": "..." -# } +# Payload-Felder (Auszug) +# ----------------------- +# note_id, title, type, status, created, updated, path, tags, +# fulltext, references (Note-Level-Wikilinks), +# hash_fulltext (Primärhash), hash_signature (z. B. "body:raw:none:") +# +# Hinweise +# -------- +# - Keine Abhängigkeit von FS-Zeitstempeln; nur Inhalte fließen in den Hash ein. +# - Abwärtskompatibel: Feldernamen bleiben stabil; zusätzliche Felder stören nicht. from __future__ import annotations @@ -245,4 +234,76 @@ def make_note_payload( except Exception: pass - # Note-Level-Wikilinks + # Note-Level-Wikilinks (Fallback, wenn Chunks nicht geliefert werden) + note_level_refs = list(dict.fromkeys(extract_wikilinks(body_parsed))) if body_parsed else [] + + payload: Dict[str, Any] = { + "note_id": fm.get("id") or fm.get("note_id"), + "title": fm.get("title"), + "type": fm.get("type"), + "status": fm.get("status"), + "created": fm.get("created"), + "updated": fm.get("updated"), + "path": rel_path or fm.get("path"), + "tags": fm.get("tags"), + # Primärer Hash + Signatur (für Vergleich) + "hash_fulltext": primary_hash, + "hash_signature": hash_signature, + # Volltext persistieren (verlustfreie Rekonstruktion) – parsed Body + "fulltext": body_parsed, + # Fallback-Refs auf Note-Ebene + "references": note_level_refs, + } + + for k in ("area", "project", "source", "lang", "slug", "aliases"): + if k in fm: + payload[k] = fm[k] + + # Optional: gesamtes Hash-Set persistieren (Debug/Monitoring) + if os.environ.get("MINDNET_HASH_RECORD_ALL", "false").strip().lower() == "true": + payload["hashes"] = compute_hash_set( + body_parsed=body_parsed, body_raw=raw_body, fm=fm, normalize=normalize + ) + + # Optional: Roh-Body-Hash separat (historische Kompatibilität) + if os.environ.get("MINDNET_HASH_STORE_RAW", "false").strip().lower() == "true" and src == "raw": + try: + payload["hash_raw_body"] = compute_hash( + body=raw_body, frontmatter=fm, mode="body", normalize="none" + ) + except Exception: + pass + + return payload + + +# --------------------------------------------------------------------------- +# CLI – Sichtprüfung +# --------------------------------------------------------------------------- + +def _cli() -> None: + ap = argparse.ArgumentParser(description="Note-Payload aus Markdown erzeugen und anzeigen") + ap.add_argument("--from-file", dest="src", required=True, help="Pfad zur Markdown-Datei") + ap.add_argument("--vault-root", dest="vault_root", default=None, help="Vault-Wurzel zur Pfad-Relativierung") + ap.add_argument("--print", dest="do_print", action="store_true", help="Payload auf stdout ausgeben") + ap.add_argument("--hash-mode", choices=["body", "frontmatter", "body+frontmatter", "full"], default=None) + ap.add_argument("--hash-normalize", choices=["canonical", "none"], default=None) + ap.add_argument("--hash-source", choices=["parsed", "raw"], default=None) + args = ap.parse_args() + + parsed = read_markdown(args.src) + payload = make_note_payload( + parsed, + vault_root=args.vault_root, + hash_mode=args.hash_mode, + hash_normalize=args.hash_normalize, + hash_source=args.hash_source, + file_path=args.src, + ) + + if args.do_print: + print(json.dumps(payload, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": # pragma: no cover + _cli()