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.
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
"""
|
||||
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]
|
||||
}
|
||||
Reference in New Issue
Block a user