db.py Refactoring Plan

Status

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

  1. Jedes Modul unter 500 Zeilen (Code Hygiene Limit)
  2. Strikte Einhaltung von SRP (Single Responsibility Principle)
  3. 100% Rückwärtskompatibilität - alle bestehenden Imports funktionieren weiterhin
  4. 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

ModulZeilenVerantwortlichkeiten
db_core.py~100Connection
db_documents.py~150Documents, Pages, Chunks
db_queue.py~60Queue
db_logging.py~180Log, Protokoll, Provenance
db_semantic.py~250Types, Stopwords, Taxonomy
db_prompts.py~70Prompts
db.py~100Komposition + 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

  1. db_core.py erstellen (Basis für alle anderen)
  2. db_logging.py erstellen (wird von vielen benötigt)
  3. db_prompts.py erstellen (eigenständig)
  4. db_documents.py erstellen (eigenständig)
  5. db_queue.py erstellen (eigenständig)
  6. db_semantic.py erstellen (eigenständig)
  7. db.py umschreiben (Komposition)
  8. Tests durchführen (alle Pipeline-Scripts testen)

Risiken & Mitigationen

RisikoMitigation
Import-Fehler nach RefactoringKompositions-Layer in db.py exportiert alles
Circular ImportsJedes Modul importiert nur db_core.py
Connection-SharingMixins nutzen self.connection von DatabaseCore
Tests fehlschlagenSchrittweise Tests nach jedem Modul

Cleanup-Möglichkeiten