{
"event": "PreToolUse",
"tool_name": "mcp__mcp-docs__docs_update",
"tool_input": {
"id": 154,
"content": "<h1>db.py Refactoring Plan<\/h1>\n\n<h2>Status<\/h2>\n<ul>\n<li><strong>Task ID<\/strong>: 505<\/li>\n<li><strong>Erstellt<\/strong>: 2025-12-28<\/li>\n<li><strong>Status<\/strong>: Plan erstellt<\/li>\n<\/ul>\n\n<h2>Ausgangslage<\/h2>\n\n<p><strong>Datei<\/strong>: <code>\/var\/www\/scripts\/pipeline\/db.py<\/code><br>\n<strong>Zeilen<\/strong>: 834<br>\n<strong>Klassen<\/strong>: 2 (Database: 707 Zeilen, PipelineProgress: 109 Zeilen)<br>\n<strong>Problem<\/strong>: 14 verschiedene Verantwortlichkeiten in der Database-Klasse (SRP-Verletzung)<\/p>\n\n<h2>Ziele<\/h2>\n\n<ol>\n<li>Jedes Modul unter 500 Zeilen (Code Hygiene Limit)<\/li>\n<li>Strikte Einhaltung von SRP (Single Responsibility Principle)<\/li>\n<li><strong>100% Rückwärtskompatibilität<\/strong> - alle bestehenden Imports funktionieren weiterhin<\/li>\n<li>DRY, KISS, SOLID Prinzipien<\/li>\n<\/ol>\n\n<h2>Architektur: Mixin-Pattern<\/h2>\n\n<p>Python Mixins ermöglichen das Aufteilen einer Klasse in logische Einheiten bei gleichzeitiger Beibehaltung der Abwärtskompatibilität.<\/p>\n\n<pre><code>db_core.py ─┐\ndb_documents.py │\ndb_queue.py ├── Mixins ──► db.py (Database erbt von allen)\ndb_logging.py │\ndb_semantic.py │\ndb_prompts.py ─┘<\/code><\/pre>\n\n<h2>Modul-Aufteilung<\/h2>\n\n<h3>1. db_core.py (~100 Zeilen)<\/h3>\n<p><strong>Verantwortung<\/strong>: Connection Management<\/p>\n\n<pre><code class=\"language-python\">class DatabaseCore:\n def __init__(self): ...\n def connect(self): ...\n def disconnect(self): ...\n def execute(self, query, params=None): ...\n def commit(self): ...<\/code><\/pre>\n\n<h3>2. db_documents.py (~150 Zeilen)<\/h3>\n<p><strong>Verantwortung<\/strong>: Document, Page, Chunk CRUD<\/p>\n\n<pre><code class=\"language-python\">class DocumentsMixin:\n # Documents\n def document_exists(self, file_path): ...\n def document_is_done(self, file_path): ...\n def insert_document(self, file_path, title, file_type, file_size, file_hash): ...\n def update_document_status(self, doc_id, status, error_message=None): ...\n \n # Pages\n def insert_page(self, doc_id, page_number, text_content, token_count=None): ...\n def get_page_id(self, doc_id, page_number): ...\n \n # Chunks\n def insert_chunk(self, doc_id, chunk_index, content, heading_path, ...): ...\n def get_chunks_for_embedding(self, limit=DEFAULT_LIMIT): ...\n def update_chunk_qdrant_id(self, chunk_id, qdrant_id): ...<\/code><\/pre>\n\n<h3>3. db_queue.py (~60 Zeilen)<\/h3>\n<p><strong>Verantwortung<\/strong>: Pipeline Queue Operations<\/p>\n\n<pre><code class=\"language-python\">class QueueMixin:\n def add_to_queue(self, file_path, action=\"process\"): ...\n def get_pending_queue_items(self, limit=10): ...\n def update_queue_status(self, queue_id, status, error_message=None): ...<\/code><\/pre>\n\n<h3>4. db_logging.py (~180 Zeilen)<\/h3>\n<p><strong>Verantwortung<\/strong>: Alle Logging-Operationen<\/p>\n\n<pre><code class=\"language-python\">class LoggingMixin:\n def log(self, level, message, context=None): ...\n def log_to_protokoll(self, client_name, request, response=None, ...): ...\n def log_provenance(self, artifact_type, artifact_id, source_type, ...): ...<\/code><\/pre>\n\n<h3>5. db_semantic.py (~250 Zeilen)<\/h3>\n<p><strong>Verantwortung<\/strong>: Entity Types, Stopwords, Taxonomy, Synonyms<\/p>\n\n<pre><code class=\"language-python\">class SemanticMixin:\n # Entity Types\n def get_entity_types(self, active_only=True): ...\n def get_entity_type_codes(self): ...\n def build_entity_prompt_categories(self): ...\n \n # Stopwords\n def get_stopwords(self, active_only=True): ...\n def is_stopword(self, word): ...\n def _normalize_stopword(self, word): ...\n \n # Synonyms (internal, nicht extern genutzt)\n def find_entity_by_synonym(self, synonym): ...\n def add_synonym(self, entity_id, synonym, ...): ...\n \n # Chunk Taxonomy\n def add_chunk_taxonomy(self, chunk_id, term_id, ...): ...\n def get_chunk_taxonomies(self, chunk_id): ...\n \n # Entity Taxonomy\n def add_entity_taxonomy(self, entity_id, term_id, ...): ...\n def get_entity_taxonomies(self, entity_id): ...\n def get_taxonomy_terms(self): ...<\/code><\/pre>\n\n<h3>6. db_prompts.py (~70 Zeilen)<\/h3>\n<p><strong>Verantwortung<\/strong>: Prompt-Verwaltung<\/p>\n\n<pre><code class=\"language-python\">class PromptsMixin:\n def get_prompt(self, name, version=None): ...\n def get_prompt_by_use_case(self, use_case, version=None): ...<\/code><\/pre>\n\n<h3>7. db.py (~100 Zeilen - Kompositions-Layer)<\/h3>\n<p><strong>Verantwortung<\/strong>: Rückwärtskompatibilität<\/p>\n\n<pre><code class=\"language-python\">from db_core import DatabaseCore\nfrom db_documents import DocumentsMixin\nfrom db_queue import QueueMixin\nfrom db_logging import LoggingMixin\nfrom db_semantic import SemanticMixin\nfrom db_prompts import PromptsMixin\n\nclass Database(\n DatabaseCore,\n DocumentsMixin,\n QueueMixin,\n LoggingMixin,\n SemanticMixin,\n PromptsMixin\n):\n '''Vollständige Database-Klasse mit allen Operationen.'''\n pass\n\nclass PipelineProgress:\n # ... (unverändert, eigene Klasse)\n\n# Globale Instanz für Rückwärtskompatibilität\ndb = Database()<\/code><\/pre>\n\n<h2>Zeilen-Verteilung<\/h2>\n\n<table>\n<tr><th>Modul<\/th><th>Zeilen<\/th><th>Verantwortlichkeiten<\/th><\/tr>\n<tr><td>db_core.py<\/td><td>~100<\/td><td>Connection<\/td><\/tr>\n<tr><td>db_documents.py<\/td><td>~150<\/td><td>Documents, Pages, Chunks<\/td><\/tr>\n<tr><td>db_queue.py<\/td><td>~60<\/td><td>Queue<\/td><\/tr>\n<tr><td>db_logging.py<\/td><td>~180<\/td><td>Log, Protokoll, Provenance<\/td><\/tr>\n<tr><td>db_semantic.py<\/td><td>~250<\/td><td>Types, Stopwords, Taxonomy<\/td><\/tr>\n<tr><td>db_prompts.py<\/td><td>~70<\/td><td>Prompts<\/td><\/tr>\n<tr><td>db.py<\/td><td>~100<\/td><td>Komposition + PipelineProgress<\/td><\/tr>\n<tr><td><strong>Total<\/strong><\/td><td>~910<\/td><td>(verteilt auf 7 Dateien)<\/td><\/tr>\n<\/table>\n\n<h2>Rückwärtskompatibilität<\/h2>\n\n<p>Alle existierenden Imports funktionieren weiterhin:<\/p>\n\n<pre><code class=\"language-python\"># Diese Imports bleiben gültig:\nfrom db import db\nfrom db import Database\nfrom db import PipelineProgress\nfrom db import db, PipelineProgress<\/code><\/pre>\n\n<h2>Implementierungs-Reihenfolge<\/h2>\n\n<ol>\n<li><strong>db_core.py<\/strong> erstellen (Basis für alle anderen)<\/li>\n<li><strong>db_logging.py<\/strong> erstellen (wird von vielen benötigt)<\/li>\n<li><strong>db_prompts.py<\/strong> erstellen (eigenständig)<\/li>\n<li><strong>db_documents.py<\/strong> erstellen (eigenständig)<\/li>\n<li><strong>db_queue.py<\/strong> erstellen (eigenständig)<\/li>\n<li><strong>db_semantic.py<\/strong> erstellen (eigenständig)<\/li>\n<li><strong>db.py<\/strong> umschreiben (Komposition)<\/li>\n<li><strong>Tests<\/strong> durchführen (alle Pipeline-Scripts testen)<\/li>\n<\/ol>\n\n<h2>Risiken & Mitigationen<\/h2>\n\n<table>\n<tr><th>Risiko<\/th><th>Mitigation<\/th><\/tr>\n<tr><td>Import-Fehler nach Refactoring<\/td><td>Kompositions-Layer in db.py exportiert alles<\/td><\/tr>\n<tr><td>Circular Imports<\/td><td>Jedes Modul importiert nur db_core.py<\/td><\/tr>\n<tr><td>Connection-Sharing<\/td><td>Mixins nutzen self.connection von DatabaseCore<\/td><\/tr>\n<tr><td>Tests fehlschlagen<\/td><td>Schrittweise Tests nach jedem Modul<\/td><\/tr>\n<\/table>\n\n<h2>Cleanup-Möglichkeiten<\/h2>\n\n<ul>\n<li><strong>Entfernen<\/strong>: <code>find_entity_by_synonym<\/code>, <code>add_synonym<\/code> (nicht extern genutzt)<\/li>\n<li><strong>Vereinfachen<\/strong>: <code>get_page_id<\/code> könnte in <code>insert_page<\/code> integriert werden<\/li>\n<li><strong>Optimieren<\/strong>: Redundante Cursor-Handling kann vereinheitlicht werden<\/li>\n<\/ul>"
}
}
{
"tool_response": [
{
"type": "text",
"text": "{\n \"success\": true,\n \"doc\": {\n \"id\": 154,\n \"parent_id\": 91,\n \"slug\": \"db-py-refactoring-plan\",\n \"path\": \"\/prozesse\/db-py-refactoring-plan\",\n \"title\": \"db.py Refactoring Plan\",\n \"description\": \"Detaillierter Plan zur Aufspaltung der 834-Zeilen db.py nach SRP\",\n \"content\": \"<h1>db.py Refactoring Plan<\/h1>\\n\\n<h2>Status<\/h2>\\n<ul>\\n<li><strong>Task ID<\/strong>: 505<\/li>\\n<li><strong>Erstellt<\/strong>: 2025-12-28<\/li>\\n<li><strong>Status<\/strong>: Plan erstellt<\/li>\\n<\/ul>\\n\\n<h2>Ausgangslage<\/h2>\\n\\n<p><strong>Datei<\/strong>: <code>\/var\/www\/scripts\/pipeline\/db.py<\/code><br>\\n<strong>Zeilen<\/strong>: 834<br>\\n<strong>Klassen<\/strong>: 2 (Database: 707 Zeilen, PipelineProgress: 109 Zeilen)<br>\\n<strong>Problem<\/strong>: 14 verschiedene Verantwortlichkeiten in der Database-Klasse (SRP-Verletzung)<\/p>\\n\\n<h2>Ziele<\/h2>\\n\\n<ol>\\n<li>Jedes Modul unter 500 Zeilen (Code Hygiene Limit)<\/li>\\n<li>Strikte Einhaltung von SRP (Single Responsibility Principle)<\/li>\\n<li><strong>100% Rückwärtskompatibilität<\/strong> - alle bestehenden Imports funktionieren weiterhin<\/li>\\n<li>DRY, KISS, SOLID Prinzipien<\/li>\\n<\/ol>\\n\\n<h2>Architektur: Mixin-Pattern<\/h2>\\n\\n<p>Python Mixins ermöglichen das Aufteilen einer Klasse in logische Einheiten bei gleichzeitiger Beibehaltung der Abwärtskompatibilität.<\/p>\\n\\n<pre><code>db_core.py ─┐\\ndb_documents.py │\\ndb_queue.py ├── Mixins ──► db.py (Database erbt von allen)\\ndb_logging.py │\\ndb_semantic.py │\\ndb_prompts.py ─┘<\/code><\/pre>\\n\\n<h2>Modul-Aufteilung<\/h2>\\n\\n<h3>1. db_core.py (~100 Zeilen)<\/h3>\\n<p><strong>Verantwortung<\/strong>: Connection Management<\/p>\\n\\n<pre><code class=\\\"language-python\\\">class DatabaseCore:\\n def __init__(self): ...\\n def connect(self): ...\\n def disconnect(self): ...\\n def execute(self, query, params=None): ...\\n def commit(self): ...<\/code><\/pre>\\n\\n<h3>2. db_documents.py (~150 Zeilen)<\/h3>\\n<p><strong>Verantwortung<\/strong>: Document, Page, Chunk CRUD<\/p>\\n\\n<pre><code class=\\\"language-python\\\">class DocumentsMixin:\\n # Documents\\n def document_exists(self, file_path): ...\\n def document_is_done(self, file_path): ...\\n def insert_document(self, file_path, title, file_type, file_size, file_hash): ...\\n def update_document_status(self, doc_id, status, error_message=None): ...\\n \\n # Pages\\n def insert_page(self, doc_id, page_number, text_content, token_count=None): ...\\n def get_page_id(self, doc_id, page_number): ...\\n \\n # Chunks\\n def insert_chunk(self, doc_id, chunk_index, content, heading_path, ...): ...\\n def get_chunks_for_embedding(self, limit=DEFAULT_LIMIT): ...\\n def update_chunk_qdrant_id(self, chunk_id, qdrant_id): ...<\/code><\/pre>\\n\\n<h3>3. db_queue.py (~60 Zeilen)<\/h3>\\n<p><strong>Verantwortung<\/strong>: Pipeline Queue Operations<\/p>\\n\\n<pre><code class=\\\"language-python\\\">class QueueMixin:\\n def add_to_queue(self, file_path, action=\\\"process\\\"): ...\\n def get_pending_queue_items(self, limit=10): ...\\n def update_queue_status(self, queue_id, status, error_message=None): ...<\/code><\/pre>\\n\\n<h3>4. db_logging.py (~180 Zeilen)<\/h3>\\n<p><strong>Verantwortung<\/strong>: Alle Logging-Operationen<\/p>\\n\\n<pre><code class=\\\"language-python\\\">class LoggingMixin:\\n def log(self, level, message, context=None): ...\\n def log_to_protokoll(self, client_name, request, response=None, ...): ...\\n def log_provenance(self, artifact_type, artifact_id, source_type, ...): ...<\/code><\/pre>\\n\\n<h3>5. db_semantic.py (~250 Zeilen)<\/h3>\\n<p><strong>Verantwortung<\/strong>: Entity Types, Stopwords, Taxonomy, Synonyms<\/p>\\n\\n<pre><code class=\\\"language-python\\\">class SemanticMixin:\\n # Entity Types\\n def get_entity_types(self, active_only=True): ...\\n def get_entity_type_codes(self): ...\\n def build_entity_prompt_categories(self): ...\\n \\n # Stopwords\\n def get_stopwords(self, active_only=True): ...\\n def is_stopword(self, word): ...\\n def _normalize_stopword(self, word): ...\\n \\n # Synonyms (internal, nicht extern genutzt)\\n def find_entity_by_synonym(self, synonym): ...\\n def add_synonym(self, entity_id, synonym, ...): ...\\n \\n # Chunk Taxonomy\\n def add_chunk_taxonomy(self, chunk_id, term_id, ...): ...\\n def get_chunk_taxonomies(self, chunk_id): ...\\n \\n # Entity Taxonomy\\n def add_entity_taxonomy(self, entity_id, term_id, ...): ...\\n def get_entity_taxonomies(self, entity_id): ...\\n def get_taxonomy_terms(self): ...<\/code><\/pre>\\n\\n<h3>6. db_prompts.py (~70 Zeilen)<\/h3>\\n<p><strong>Verantwortung<\/strong>: Prompt-Verwaltung<\/p>\\n\\n<pre><code class=\\\"language-python\\\">class PromptsMixin:\\n def get_prompt(self, name, version=None): ...\\n def get_prompt_by_use_case(self, use_case, version=None): ...<\/code><\/pre>\\n\\n<h3>7. db.py (~100 Zeilen - Kompositions-Layer)<\/h3>\\n<p><strong>Verantwortung<\/strong>: Rückwärtskompatibilität<\/p>\\n\\n<pre><code class=\\\"language-python\\\">from db_core import DatabaseCore\\nfrom db_documents import DocumentsMixin\\nfrom db_queue import QueueMixin\\nfrom db_logging import LoggingMixin\\nfrom db_semantic import SemanticMixin\\nfrom db_prompts import PromptsMixin\\n\\nclass Database(\\n DatabaseCore,\\n DocumentsMixin,\\n QueueMixin,\\n LoggingMixin,\\n SemanticMixin,\\n PromptsMixin\\n):\\n '''Vollständige Database-Klasse mit allen Operationen.'''\\n pass\\n\\nclass PipelineProgress:\\n # ... (unverändert, eigene Klasse)\\n\\n# Globale Instanz für Rückwärtskompatibilität\\ndb = Database()<\/code><\/pre>\\n\\n<h2>Zeilen-Verteilung<\/h2>\\n\\n<table>\\n<tr><th>Modul<\/th><th>Zeilen<\/th><th>Verantwortlichkeiten<\/th><\/tr>\\n<tr><td>db_core.py<\/td><td>~100<\/td><td>Connection<\/td><\/tr>\\n<tr><td>db_documents.py<\/td><td>~150<\/td><td>Documents, Pages, Chunks<\/td><\/tr>\\n<tr><td>db_queue.py<\/td><td>~60<\/td><td>Queue<\/td><\/tr>\\n<tr><td>db_logging.py<\/td><td>~180<\/td><td>Log, Protokoll, Provenance<\/td><\/tr>\\n<tr><td>db_semantic.py<\/td><td>~250<\/td><td>Types, Stopwords, Taxonomy<\/td><\/tr>\\n<tr><td>db_prompts.py<\/td><td>~70<\/td><td>Prompts<\/td><\/tr>\\n<tr><td>db.py<\/td><td>~100<\/td><td>Komposition + PipelineProgress<\/td><\/tr>\\n<tr><td><strong>Total<\/strong><\/td><td>~910<\/td><td>(verteilt auf 7 Dateien)<\/td><\/tr>\\n<\/table>\\n\\n<h2>Rückwärtskompatibilität<\/h2>\\n\\n<p>Alle existierenden Imports funktionieren weiterhin:<\/p>\\n\\n<pre><code class=\\\"language-python\\\"># Diese Imports bleiben gültig:\\nfrom db import db\\nfrom db import Database\\nfrom db import PipelineProgress\\nfrom db import db, PipelineProgress<\/code><\/pre>\\n\\n<h2>Implementierungs-Reihenfolge<\/h2>\\n\\n<ol>\\n<li><strong>db_core.py<\/strong> erstellen (Basis für alle anderen)<\/li>\\n<li><strong>db_logging.py<\/strong> erstellen (wird von vielen benötigt)<\/li>\\n<li><strong>db_prompts.py<\/strong> erstellen (eigenständig)<\/li>\\n<li><strong>db_documents.py<\/strong> erstellen (eigenständig)<\/li>\\n<li><strong>db_queue.py<\/strong> erstellen (eigenständig)<\/li>\\n<li><strong>db_semantic.py<\/strong> erstellen (eigenständig)<\/li>\\n<li><strong>db.py<\/strong> umschreiben (Komposition)<\/li>\\n<li><strong>Tests<\/strong> durchführen (alle Pipeline-Scripts testen)<\/li>\\n<\/ol>\\n\\n<h2>Risiken & Mitigationen<\/h2>\\n\\n<table>\\n<tr><th>Risiko<\/th><th>Mitigation<\/th><\/tr>\\n<tr><td>Import-Fehler nach Refactoring<\/td><td>Kompositions-Layer in db.py exportiert alles<\/td><\/tr>\\n<tr><td>Circular Imports<\/td><td>Jedes Modul importiert nur db_core.py<\/td><\/tr>\\n<tr><td>Connection-Sharing<\/td><td>Mixins nutzen self.connection von DatabaseCore<\/td><\/tr>\\n<tr><td>Tests fehlschlagen<\/td><td>Schrittweise Tests nach jedem Modul<\/td><\/tr>\\n<\/table>\\n\\n<h2>Cleanup-Möglichkeiten<\/h2>\\n\\n<ul>\\n<li><strong>Entfernen<\/strong>: <code>find_entity_by_synonym<\/code>, <code>add_synonym<\/code> (nicht extern genutzt)<\/li>\\n<li><strong>Vereinfachen<\/strong>: <code>get_page_id<\/code> könnte in <code>insert_page<\/code> integriert werden<\/li>\\n<li><strong>Optimieren<\/strong>: Redundante Cursor-Handling kann vereinheitlicht werden<\/li>\\n<\/ul>\",\n \"status\": \"published\",\n \"sort_order\": 10,\n \"depth\": 1,\n \"created_at\": \"2025-12-28T08:53:35\",\n \"updated_at\": \"2025-12-29T14:48:47\"\n },\n \"message\": \"Document #154 updated\"\n}"
}
]
}