[split-required] Split 500-1000 LOC files across all services

backend-lehrer (5 files):
- alerts_agent/db/repository.py (992 → 5), abitur_docs_api.py (956 → 3)
- teacher_dashboard_api.py (951 → 3), services/pdf_service.py (916 → 3)
- mail/mail_db.py (987 → 6)

klausur-service (5 files):
- legal_templates_ingestion.py (942 → 3), ocr_pipeline_postprocess.py (929 → 4)
- ocr_pipeline_words.py (876 → 3), ocr_pipeline_ocr_merge.py (616 → 2)
- KorrekturPage.tsx (956 → 6)

website (5 pages):
- mail (985 → 9), edu-search (958 → 8), mac-mini (950 → 7)
- ocr-labeling (946 → 7), audit-workspace (871 → 4)

studio-v2 (5 files + 1 deleted):
- page.tsx (946 → 5), MessagesContext.tsx (925 → 4)
- korrektur (914 → 6), worksheet-cleanup (899 → 6)
- useVocabWorksheet.ts (888 → 3)
- Deleted dead page-original.tsx (934 LOC)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-04-24 23:35:37 +02:00
parent 6811264756
commit b6983ab1dc
99 changed files with 13484 additions and 16106 deletions

View File

@@ -0,0 +1,253 @@
"""
Mail Database - Connection Pool and Schema Initialization.
"""
import os
# Database Configuration - from Vault or environment (test default for CI)
DATABASE_URL = os.getenv("DATABASE_URL", "postgresql://test:test@localhost:5432/test")
# Flag to check if using test defaults
_DB_CONFIGURED = DATABASE_URL != "postgresql://test:test@localhost:5432/test"
# Connection pool (shared with metrics_db)
_pool = None
async def get_pool():
"""Get or create database connection pool."""
global _pool
if _pool is None:
try:
import asyncpg
_pool = await asyncpg.create_pool(DATABASE_URL, min_size=2, max_size=10)
except ImportError:
print("Warning: asyncpg not installed. Mail database disabled.")
return None
except Exception as e:
print(f"Warning: Failed to connect to PostgreSQL: {e}")
return None
return _pool
async def init_mail_tables() -> bool:
"""Initialize mail tables in PostgreSQL."""
pool = await get_pool()
if pool is None:
return False
create_tables_sql = """
-- =============================================================================
-- External Email Accounts
-- =============================================================================
CREATE TABLE IF NOT EXISTS external_email_accounts (
id VARCHAR(36) PRIMARY KEY,
user_id VARCHAR(36) NOT NULL,
tenant_id VARCHAR(36) NOT NULL,
email VARCHAR(255) NOT NULL,
display_name VARCHAR(255),
account_type VARCHAR(50) DEFAULT 'personal',
-- IMAP Settings (password stored in Vault)
imap_host VARCHAR(255) NOT NULL,
imap_port INTEGER DEFAULT 993,
imap_ssl BOOLEAN DEFAULT TRUE,
-- SMTP Settings
smtp_host VARCHAR(255) NOT NULL,
smtp_port INTEGER DEFAULT 465,
smtp_ssl BOOLEAN DEFAULT TRUE,
-- Vault path for credentials
vault_path VARCHAR(500),
-- Status tracking
status VARCHAR(20) DEFAULT 'pending',
last_sync TIMESTAMP,
sync_error TEXT,
email_count INTEGER DEFAULT 0,
unread_count INTEGER DEFAULT 0,
-- Timestamps
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
-- Constraints
UNIQUE(user_id, email)
);
CREATE INDEX IF NOT EXISTS idx_mail_accounts_user ON external_email_accounts(user_id);
CREATE INDEX IF NOT EXISTS idx_mail_accounts_tenant ON external_email_accounts(tenant_id);
CREATE INDEX IF NOT EXISTS idx_mail_accounts_status ON external_email_accounts(status);
-- =============================================================================
-- Aggregated Emails
-- =============================================================================
CREATE TABLE IF NOT EXISTS aggregated_emails (
id VARCHAR(36) PRIMARY KEY,
account_id VARCHAR(36) REFERENCES external_email_accounts(id) ON DELETE CASCADE,
user_id VARCHAR(36) NOT NULL,
tenant_id VARCHAR(36) NOT NULL,
-- Email identification
message_id VARCHAR(500) NOT NULL,
folder VARCHAR(100) DEFAULT 'INBOX',
-- Email content
subject TEXT,
sender_email VARCHAR(255),
sender_name VARCHAR(255),
recipients JSONB DEFAULT '[]',
cc JSONB DEFAULT '[]',
body_preview TEXT,
body_text TEXT,
body_html TEXT,
has_attachments BOOLEAN DEFAULT FALSE,
attachments JSONB DEFAULT '[]',
headers JSONB DEFAULT '{}',
-- Status flags
is_read BOOLEAN DEFAULT FALSE,
is_starred BOOLEAN DEFAULT FALSE,
is_deleted BOOLEAN DEFAULT FALSE,
-- Dates
date_sent TIMESTAMP,
date_received TIMESTAMP,
-- AI enrichment
category VARCHAR(50),
sender_type VARCHAR(50),
sender_authority_name VARCHAR(255),
detected_deadlines JSONB DEFAULT '[]',
suggested_priority VARCHAR(20),
ai_summary TEXT,
ai_analyzed_at TIMESTAMP,
created_at TIMESTAMP DEFAULT NOW(),
-- Prevent duplicate imports
UNIQUE(account_id, message_id)
);
CREATE INDEX IF NOT EXISTS idx_emails_account ON aggregated_emails(account_id);
CREATE INDEX IF NOT EXISTS idx_emails_user ON aggregated_emails(user_id);
CREATE INDEX IF NOT EXISTS idx_emails_tenant ON aggregated_emails(tenant_id);
CREATE INDEX IF NOT EXISTS idx_emails_date ON aggregated_emails(date_received DESC);
CREATE INDEX IF NOT EXISTS idx_emails_category ON aggregated_emails(category);
CREATE INDEX IF NOT EXISTS idx_emails_unread ON aggregated_emails(is_read) WHERE is_read = FALSE;
CREATE INDEX IF NOT EXISTS idx_emails_starred ON aggregated_emails(is_starred) WHERE is_starred = TRUE;
CREATE INDEX IF NOT EXISTS idx_emails_sender ON aggregated_emails(sender_email);
-- =============================================================================
-- Inbox Tasks (Arbeitsvorrat)
-- =============================================================================
CREATE TABLE IF NOT EXISTS inbox_tasks (
id VARCHAR(36) PRIMARY KEY,
user_id VARCHAR(36) NOT NULL,
tenant_id VARCHAR(36) NOT NULL,
email_id VARCHAR(36) REFERENCES aggregated_emails(id) ON DELETE SET NULL,
account_id VARCHAR(36) REFERENCES external_email_accounts(id) ON DELETE SET NULL,
-- Task content
title VARCHAR(500) NOT NULL,
description TEXT,
priority VARCHAR(20) DEFAULT 'medium',
status VARCHAR(20) DEFAULT 'pending',
deadline TIMESTAMP,
-- Source information
source_email_subject TEXT,
source_sender VARCHAR(255),
source_sender_type VARCHAR(50),
-- AI extraction info
ai_extracted BOOLEAN DEFAULT FALSE,
confidence_score FLOAT,
-- Completion tracking
completed_at TIMESTAMP,
reminder_at TIMESTAMP,
-- Timestamps
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_tasks_user ON inbox_tasks(user_id);
CREATE INDEX IF NOT EXISTS idx_tasks_tenant ON inbox_tasks(tenant_id);
CREATE INDEX IF NOT EXISTS idx_tasks_status ON inbox_tasks(status);
CREATE INDEX IF NOT EXISTS idx_tasks_deadline ON inbox_tasks(deadline) WHERE deadline IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_tasks_priority ON inbox_tasks(priority);
CREATE INDEX IF NOT EXISTS idx_tasks_email ON inbox_tasks(email_id) WHERE email_id IS NOT NULL;
-- =============================================================================
-- Email Templates
-- =============================================================================
CREATE TABLE IF NOT EXISTS email_templates (
id VARCHAR(36) PRIMARY KEY,
user_id VARCHAR(36), -- NULL for system templates
tenant_id VARCHAR(36),
name VARCHAR(255) NOT NULL,
category VARCHAR(100),
subject_template TEXT,
body_template TEXT,
variables JSONB DEFAULT '[]',
is_system BOOLEAN DEFAULT FALSE,
usage_count INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_templates_user ON email_templates(user_id);
CREATE INDEX IF NOT EXISTS idx_templates_tenant ON email_templates(tenant_id);
CREATE INDEX IF NOT EXISTS idx_templates_system ON email_templates(is_system);
-- =============================================================================
-- Mail Audit Log
-- =============================================================================
CREATE TABLE IF NOT EXISTS mail_audit_log (
id VARCHAR(36) PRIMARY KEY,
user_id VARCHAR(36) NOT NULL,
tenant_id VARCHAR(36),
action VARCHAR(100) NOT NULL,
entity_type VARCHAR(50), -- account, email, task
entity_id VARCHAR(36),
details JSONB,
ip_address VARCHAR(45),
user_agent TEXT,
created_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_mail_audit_user ON mail_audit_log(user_id);
CREATE INDEX IF NOT EXISTS idx_mail_audit_created ON mail_audit_log(created_at DESC);
CREATE INDEX IF NOT EXISTS idx_mail_audit_action ON mail_audit_log(action);
-- =============================================================================
-- Sync Status Tracking
-- =============================================================================
CREATE TABLE IF NOT EXISTS mail_sync_status (
id VARCHAR(36) PRIMARY KEY,
account_id VARCHAR(36) REFERENCES external_email_accounts(id) ON DELETE CASCADE,
folder VARCHAR(100),
last_uid INTEGER DEFAULT 0,
last_sync TIMESTAMP,
sync_errors INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
UNIQUE(account_id, folder)
);
"""
try:
async with pool.acquire() as conn:
await conn.execute(create_tables_sql)
print("Mail tables initialized successfully")
return True
except Exception as e:
print(f"Failed to initialize mail tables: {e}")
return False