Files
breakpilot-lehrer/backend-lehrer/alerts_agent/db/models_dual_mode.py
Benjamin Admin 451365a312 [split-required] Split remaining 500-680 LOC files (final batch)
website (17 pages + 3 components):
- multiplayer/wizard, middleware/wizard+test-wizard, communication
- builds/wizard, staff-search, voice, sbom/wizard
- foerderantrag, mail/tasks, tools/communication, sbom
- compliance/evidence, uni-crawler, brandbook (already done)
- CollectionsTab, IngestionTab, RiskHeatmap

backend-lehrer (5 files):
- letters_api (641 → 2), certificates_api (636 → 2)
- alerts_agent/db/models (636 → 3)
- llm_gateway/communication_service (614 → 2)
- game/database already done in prior batch

klausur-service (2 files):
- hybrid_vocab_extractor (664 → 2)
- klausur-service/frontend: api.ts (620 → 3), EHUploadWizard (591 → 2)

voice-service (3 files):
- bqas/rag_judge (618 → 3), runner (529 → 2)
- enhanced_task_orchestrator (519 → 2)

studio-v2 (6 files):
- korrektur/[klausurId] (578 → 4), fairness (569 → 2)
- AlertsWizard (552 → 2), OnboardingWizard (513 → 2)
- korrektur/api.ts (506 → 3), geo-lernwelt (501 → 2)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-25 08:56:45 +02:00

150 lines
6.3 KiB
Python

"""
Dual-Mode System Models: Templates, Subscriptions, Sources, Digests.
These are additional ORM models for the Guided/Expert dual-mode alert system.
"""
from datetime import datetime
from sqlalchemy import (
Column, String, Integer, DateTime, JSON,
Boolean, Text, Enum as SQLEnum, ForeignKey,
)
from sqlalchemy.orm import relationship
import uuid
from classroom_engine.database import Base
from .enums import (
FeedTypeEnum,
MigrationModeEnum,
AlertModeEnum,
UserRoleEnum,
DigestStatusEnum,
)
class AlertTemplateDB(Base):
"""
Vorkonfigurierte Alert-Templates (Playbooks).
Fuer Guided Mode: Lehrer waehlen 1-3 Templates statt RSS-Feeds zu konfigurieren.
"""
__tablename__ = 'alert_templates'
id = Column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
slug = Column(String(100), unique=True, nullable=False)
name = Column(String(255), nullable=False)
description = Column(Text, default="")
icon = Column(String(50), default="")
category = Column(String(100), default="")
target_roles = Column(JSON, default=list)
topics_config = Column(JSON, default=list)
rules_config = Column(JSON, default=list)
profile_config = Column(JSON, default=dict)
importance_config = Column(JSON, default=dict)
max_cards_per_day = Column(Integer, default=10)
digest_enabled = Column(Boolean, default=True)
digest_day = Column(String(20), default="monday")
language = Column(String(10), default="de")
is_active = Column(Boolean, default=True)
is_premium = Column(Boolean, default=False)
sort_order = Column(Integer, default=0)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
subscriptions = relationship("UserAlertSubscriptionDB", back_populates="template")
def __repr__(self):
return f"<AlertTemplate {self.slug}: {self.name}>"
class AlertSourceDB(Base):
"""
Alert-Quelle fuer Migration bestehender Alerts.
Unterstuetzt: E-Mail-Weiterleitung, RSS-Import, Rekonstruktion.
"""
__tablename__ = 'alert_sources'
id = Column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
tenant_id = Column(String(36), nullable=True, index=True)
user_id = Column(String(36), nullable=True, index=True)
source_type = Column(SQLEnum(FeedTypeEnum), default=FeedTypeEnum.RSS, nullable=False)
original_label = Column(String(255), nullable=True)
inbound_address = Column(String(255), nullable=True, unique=True)
rss_url = Column(String(2000), nullable=True)
migration_mode = Column(SQLEnum(MigrationModeEnum), default=MigrationModeEnum.IMPORT, nullable=False)
topic_id = Column(String(36), ForeignKey('alert_topics.id', ondelete='SET NULL'), nullable=True)
is_active = Column(Boolean, default=True)
items_received = Column(Integer, default=0)
last_item_at = Column(DateTime, nullable=True)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
def __repr__(self):
return f"<AlertSource {self.source_type.value}: {self.original_label}>"
class UserAlertSubscriptionDB(Base):
"""
User-Subscription fuer Alert-Templates oder Expert-Profile.
Speichert Modus-Wahl, Template-Verknuepfung und Wizard-Zustand.
"""
__tablename__ = 'user_alert_subscriptions'
id = Column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
user_id = Column(String(36), nullable=False, index=True)
school_id = Column(String(36), nullable=True, index=True)
mode = Column(SQLEnum(AlertModeEnum), default=AlertModeEnum.GUIDED, nullable=False)
user_role = Column(SQLEnum(UserRoleEnum), nullable=True)
template_id = Column(String(36), ForeignKey('alert_templates.id', ondelete='SET NULL'), nullable=True)
selected_template_ids = Column(JSON, default=list)
profile_id = Column(String(36), ForeignKey('alert_profiles.id', ondelete='SET NULL'), nullable=True)
is_active = Column(Boolean, default=True)
notification_email = Column(String(255), nullable=True)
digest_enabled = Column(Boolean, default=True)
digest_frequency = Column(String(20), default="weekly")
digest_day = Column(String(20), default="monday")
last_digest_sent_at = Column(DateTime, nullable=True)
wizard_step = Column(Integer, default=0)
wizard_completed = Column(Boolean, default=False)
wizard_state = Column(JSON, default=dict)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
template = relationship("AlertTemplateDB", back_populates="subscriptions")
profile = relationship("AlertProfileDB")
digests = relationship("AlertDigestDB", back_populates="subscription", cascade="all, delete-orphan")
def __repr__(self):
return f"<UserAlertSubscription {self.user_id} ({self.mode.value})>"
class AlertDigestDB(Base):
"""
Woechentliche Digest-Zusammenfassung.
Enthaelt gerenderte Zusammenfassung + Statistiken.
"""
__tablename__ = 'alert_digests'
id = Column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
subscription_id = Column(String(36), ForeignKey('user_alert_subscriptions.id', ondelete='CASCADE'), nullable=False, index=True)
user_id = Column(String(36), nullable=False, index=True)
period_start = Column(DateTime, nullable=False)
period_end = Column(DateTime, nullable=False)
title = Column(String(255), default="")
summary_html = Column(Text, default="")
summary_pdf_url = Column(String(500), nullable=True)
total_alerts = Column(Integer, default=0)
kritisch_count = Column(Integer, default=0)
dringend_count = Column(Integer, default=0)
wichtig_count = Column(Integer, default=0)
pruefen_count = Column(Integer, default=0)
info_count = Column(Integer, default=0)
alert_ids = Column(JSON, default=list)
status = Column(SQLEnum(DigestStatusEnum), default=DigestStatusEnum.PENDING, nullable=False)
sent_at = Column(DateTime, nullable=True)
error_message = Column(Text, nullable=True)
created_at = Column(DateTime, default=datetime.utcnow)
subscription = relationship("UserAlertSubscriptionDB", back_populates="digests")
def __repr__(self):
return f"<AlertDigest {self.title} ({self.status.value})>"