Files
Orchestrator/bahn/project-audit/knowledge/db.py
T
ankn a5f8fb49ab Migrate all repos into monorepo context folders
Bahn: aisupport, Analyse-O2C-C2S, awesome-bahn-mcp-servers, beam-mcp,
      Confluence_Bot, db-planet-mcp-server, O2C-Harness, project-audit,
      Projekt-KIQ-HP, teamlandkarte-mcp
Dhive: Jury-Voting
Privat: CV, NoteGraph (NOTE: NoteGraph needs complete redo after consolidation)
Shared: AI-Orchestrator, OrgMyLife, power_skills_and_more
Shared/references: symphony (read-only)

Bahn repos remain available as independent remotes - this monorepo
pulls them in via subtree, the originals are untouched.
2026-06-30 20:39:52 +02:00

125 lines
4.8 KiB
Python

"""
Knowledge Graph DB - Basis-Modul
SQLite-basierte Graph-Datenbank fuer pathOS Audit
"""
import sqlite3
import json
import os
from pathlib import Path
DB_PATH = Path(__file__).parent / "pathos.db"
SCHEMA_PATH = Path(__file__).parent / "schema.sql"
def get_db():
"""Verbindung zur DB herstellen, Schema anlegen falls noetig."""
db = sqlite3.connect(str(DB_PATH))
db.row_factory = sqlite3.Row
db.execute("PRAGMA journal_mode=WAL")
db.execute("PRAGMA foreign_keys=ON")
# Schema anlegen
with open(SCHEMA_PATH, "r", encoding="utf-8") as f:
db.executescript(f.read())
return db
def upsert_node(db, node_id, node_type, name, summary=None, properties=None, source=None):
"""Node einfuegen oder aktualisieren."""
props_json = json.dumps(properties, ensure_ascii=False) if properties else None
db.execute("""
INSERT INTO nodes (id, type, name, summary, properties, source, updated_at)
VALUES (?, ?, ?, ?, ?, ?, datetime('now'))
ON CONFLICT(id) DO UPDATE SET
type=excluded.type, name=excluded.name, summary=excluded.summary,
properties=excluded.properties, source=excluded.source, updated_at=datetime('now')
""", (node_id, node_type, name, summary, props_json, source))
def add_edge(db, source_id, target_id, relation, properties=None, source=None):
"""Kante hinzufuegen (Duplikate werden vermieden)."""
props_json = json.dumps(properties, ensure_ascii=False) if properties else None
# Pruefen ob schon existiert
existing = db.execute(
"SELECT id FROM edges WHERE source_id=? AND target_id=? AND relation=?",
(source_id, target_id, relation)
).fetchone()
if existing:
db.execute(
"UPDATE edges SET properties=?, source=?, updated_at=datetime('now') WHERE id=?",
(props_json, source, existing["id"])
)
else:
db.execute(
"INSERT INTO edges (source_id, target_id, relation, properties, source, updated_at) VALUES (?, ?, ?, ?, ?, datetime('now'))",
(source_id, target_id, relation, props_json, source)
)
def upsert_summary(db, summary_id, topic, content, detail_path=None, tags=None):
"""Zusammenfassung einfuegen oder aktualisieren."""
db.execute("""
INSERT INTO summaries (id, topic, content, detail_path, tags, updated_at)
VALUES (?, ?, ?, ?, ?, datetime('now'))
ON CONFLICT(id) DO UPDATE SET
topic=excluded.topic, content=excluded.content,
detail_path=excluded.detail_path, tags=excluded.tags, updated_at=datetime('now')
""", (summary_id, topic, content, detail_path, tags))
# FTS aktualisieren
db.execute("INSERT INTO summaries_fts(summaries_fts) VALUES('rebuild')")
def add_fact(db, subject, predicate, value, confidence="confirmed", source=None, date=None):
"""Fakt hinzufuegen."""
db.execute(
"INSERT INTO facts (subject, predicate, value, confidence, source, date, updated_at) VALUES (?, ?, ?, ?, ?, ?, datetime('now'))",
(subject, predicate, value, confidence, source, date)
)
db.execute("INSERT INTO facts_fts(facts_fts) VALUES('rebuild')")
def search_summaries(db, query, limit=5):
"""Volltextsuche in Zusammenfassungen."""
# FTS5 Query escapen: Woerter mit Anfuehrungszeichen schuetzen
safe_query = " ".join(f'"{w}"' for w in query.split())
results = db.execute("""
SELECT s.id, s.topic, s.content, s.detail_path, s.tags
FROM summaries_fts fts
JOIN summaries s ON s.rowid = fts.rowid
WHERE summaries_fts MATCH ?
LIMIT ?
""", (safe_query, limit)).fetchall()
return [dict(r) for r in results]
def search_facts(db, query, limit=10):
"""Volltextsuche in Fakten."""
safe_query = " ".join(f'"{w}"' for w in query.split())
results = db.execute("""
SELECT f.subject, f.predicate, f.value, f.confidence, f.source, f.date
FROM facts_fts fts
JOIN facts f ON f.rowid = fts.rowid
WHERE facts_fts MATCH ?
LIMIT ?
""", (safe_query, limit)).fetchall()
return [dict(r) for r in results]
def get_node_context(db, node_id):
"""Alle Infos zu einem Node + seine Beziehungen."""
node = db.execute("SELECT * FROM nodes WHERE id=?", (node_id,)).fetchone()
if not node:
return None
edges_out = db.execute(
"SELECT e.relation, n.name, n.type, e.properties FROM edges e JOIN nodes n ON n.id=e.target_id WHERE e.source_id=?",
(node_id,)
).fetchall()
edges_in = db.execute(
"SELECT e.relation, n.name, n.type, e.properties FROM edges e JOIN nodes n ON n.id=e.source_id WHERE e.target_id=?",
(node_id,)
).fetchall()
return {
"node": dict(node),
"outgoing": [dict(e) for e in edges_out],
"incoming": [dict(e) for e in edges_in]
}