#!/usr/bin/env python3
import sqlite3
import os
import sys
def check_archiving_contradiction():
"""Check if archived cases are being modified after archiving."""
print("=== ARCHIVIERUNGSWIDERSPRUCH PRUEFUNG ===")
# Determine which cases.db to use
cases_db_paths = [
"/var/lib/sma-data/cases.db",
"/opt/struktur/social-media-agent/cases.db"
]
db_path = None
for path in cases_db_paths:
if os.path.exists(path):
db_path = path
break
if not db_path:
print("FEHLER: Keine cases.db gefunden")
return
print(f"Pruefe Datenbank: {db_path}")
try:
conn = sqlite3.connect(db_path)
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
# Get a few archived cases (limit to 5 to avoid too much output)
cursor.execute("""
SELECT case_id, title, status, created_at, updated_at
FROM cases
WHERE status = 'archiviert'
ORDER BY updated_at DESC
LIMIT 5
""")
archived_cases = cursor.fetchall()
if not archived_cases:
print("KEINE archivierten Faele gefunden.")
conn.close()
return
print(f"Gefundene archivierte Faelle: {len(archived_cases)}")
print()
for case in archived_cases:
case_id = case['case_id']
title = case['title']
created_at = case['created_at']
updated_at = case['updated_at']
print(f"Fall ID: {case_id}")
print(f"Titel: {title}")
print(f"Erstellt: {created_at}")
print(f"Letzte Aenderung: {updated_at}")
# Get audit log for this case, ordered by changed_at
cursor.execute("""
SELECT id, entity_type, action, from_value, to_value, source, note, changed_at
FROM audit_log
WHERE entity_type = 'case' AND entity_id = ?
ORDER BY changed_at
""", (case_id,))
audit_entries = cursor.fetchall()
if not audit_entries:
print(" Keine Audit-Eintraege gefunden.")
print()
continue
# Find when the case was archived (status change to 'archiviert')
archived_at = None
for entry in audit_entries:
if entry['action'] == 'status_change' and entry['to_value'] == 'archiviert':
archived_at = entry['changed_at']
break
if archived_at is None:
print(" WARNUNG: Kein Archivierungs-Eintrag im Audit-Log gefunden.")
print()
continue
print(f" Archiviert am: {archived_at}")
# Check for any entries after the archiving event
post_archive_entries = [
e for e in audit_entries
if e['changed_at'] > archived_at
]
if post_archive_entries:
print(f" WARNUNG: {len(post_archive_entries)} Audit-Eintraege NACH der Archivierung gefunden:")
for entry in post_archive_entries[:3]: # Show first 3
print(f" - [{entry['changed_at']}] {entry['action']}: {entry['from_value']} -> {entry['to_value']} (Quelle: {entry['source']})")
if len(post_archive_entries) > 3:
print(f" ... und {len(post_archive_entries) - 3} weitere")
else:
print(" OK: Keine Aenderungen nach der Archivierung entdeckt.")
print()
conn.close()
except Exception as e:
print(f"FEHLER bei der Prüfung: {e}")
def check_graphiti_classification():
"""Check Graphiti service for classification issues."""
print("=== GRAPH-KLASSIFIKATION PRUEFUNG ===")
# Check the Graphiti requests database
graphiti_db_path = "/opt/struktur/graphiti/service/graphiti_requests.db"
if not os.path.exists(graphiti_db_path):
print(f"FEHLER: Graphiti-Datenbank nicht gefunden unter {graphiti_db_path}")
return
print(f"Pruefe Graphiti-Datenbank: {graphiti_db_path}")
try:
conn = sqlite3.connect(graphiti_db_path)
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
# Get table info
cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
tables = cursor.fetchall()
print(f"Tabellen in Graphiti-DB: {[t['name'] for t in tables]}")
# Check the graphiti_requests table
if ('graphiti_requests',) in tables:
cursor.execute("""
SELECT COUNT(*) as total,
SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed,
SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed,
SUM(CASE WHEN status = 'timed_out' THEN 1 ELSE 0 END) as timed_out,
SUM(CASE WHEN status = 'rate_limited' THEN 1 ELSE 0 END) as rate_limited
FROM graphiti_requests
""")
stats = cursor.fetchone()
print(f"Anfragen-Statistik:")
print(f" Gesamt: {stats['total']}")
print(f" Erfolgreich: {stats['completed']}")
print(f" Fehlgeschlagen: {stats['failed']}")
print(f" Timeouts: {stats['timed_out']}")
print(f" Rate Limited: {stats['rate_limited']}")
# Check for any requests with error_phase that might indicate classification issues
cursor.execute("""
SELECT request_id, error_phase, error_message, created_at
FROM graphiti_requests
WHERE error_phase IS NOT NULL AND error_phase != ''
ORDER BY created_at DESC
LIMIT 5
""")
errors = cursor.fetchall()
if errors:
print(f" Gefundene Fehler mit error_phase (letzte 5):")
for err in errors:
print(f" - [{err['created_at']}] {err['error_phase']}: {err['error_message'][:100]}...")
else:
print(" Keine Fehler mit error_phase gefunden.")
# Check for any classification-related fields or patterns
# We don't have a specific classification table, but we can look at the episodes or other data if available
# However, the main classification might happen in the Neo4j graph, which we cannot directly query here.
# So we'll note that limitation.
print()
print("HINWEIS: Die eigentliche Graph-Klassifikation erfolgt im Neo4j-Graphen,")
print(" der hier nicht direkt abgefragt werden kann.")
print(" Wir koennen nur die Request-Datenbank und die Service-Logs prüfen.")
conn.close()
except Exception as e:
print(f"FEHLER bei der Graphiti-Prüfung: {e}")
def main():
print("Unabhaengige Prüfung der offenen Themen:")
print("1. Archivierungswiderspruch / Graphiti")
print("2. Graph-Klassifikation")
print()
check_archiving_contradiction()
print()
check_graphiti_classification()
if __name__ == "__main__":
main()