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