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>
188 lines
5.5 KiB
Python
188 lines
5.5 KiB
Python
"""
|
|
Repository für Alert Rules (Filterregeln).
|
|
"""
|
|
import uuid
|
|
from datetime import datetime
|
|
from typing import Optional, List, Dict, Any
|
|
from sqlalchemy.orm import Session as DBSession
|
|
from sqlalchemy import or_
|
|
|
|
from .models import AlertRuleDB, RuleActionEnum
|
|
|
|
|
|
class RuleRepository:
|
|
"""Repository für Alert Rules (Filterregeln)."""
|
|
|
|
def __init__(self, db: DBSession):
|
|
self.db = db
|
|
|
|
# ==================== CREATE ====================
|
|
|
|
def create(
|
|
self,
|
|
name: str,
|
|
conditions: List[Dict],
|
|
action_type: str = "keep",
|
|
action_config: Dict = None,
|
|
topic_id: str = None,
|
|
user_id: str = None,
|
|
description: str = "",
|
|
priority: int = 0,
|
|
) -> AlertRuleDB:
|
|
"""Erstellt eine neue Regel."""
|
|
rule = AlertRuleDB(
|
|
id=str(uuid.uuid4()),
|
|
topic_id=topic_id,
|
|
user_id=user_id,
|
|
name=name,
|
|
description=description,
|
|
conditions=conditions,
|
|
action_type=RuleActionEnum(action_type),
|
|
action_config=action_config or {},
|
|
priority=priority,
|
|
)
|
|
self.db.add(rule)
|
|
self.db.commit()
|
|
self.db.refresh(rule)
|
|
return rule
|
|
|
|
# ==================== READ ====================
|
|
|
|
def get_by_id(self, rule_id: str) -> Optional[AlertRuleDB]:
|
|
"""Holt eine Regel nach ID."""
|
|
return self.db.query(AlertRuleDB).filter(
|
|
AlertRuleDB.id == rule_id
|
|
).first()
|
|
|
|
def get_active(
|
|
self,
|
|
topic_id: str = None,
|
|
user_id: str = None,
|
|
) -> List[AlertRuleDB]:
|
|
"""Holt alle aktiven Regeln, sortiert nach Priorität."""
|
|
query = self.db.query(AlertRuleDB).filter(
|
|
AlertRuleDB.is_active == True
|
|
)
|
|
|
|
if topic_id:
|
|
# Topic-spezifische und globale Regeln
|
|
query = query.filter(
|
|
or_(
|
|
AlertRuleDB.topic_id == topic_id,
|
|
AlertRuleDB.topic_id.is_(None)
|
|
)
|
|
)
|
|
|
|
if user_id:
|
|
query = query.filter(
|
|
or_(
|
|
AlertRuleDB.user_id == user_id,
|
|
AlertRuleDB.user_id.is_(None)
|
|
)
|
|
)
|
|
|
|
return query.order_by(AlertRuleDB.priority.desc()).all()
|
|
|
|
def get_all(
|
|
self,
|
|
user_id: str = None,
|
|
topic_id: str = None,
|
|
is_active: bool = None,
|
|
) -> List[AlertRuleDB]:
|
|
"""Holt alle Regeln mit optionalen Filtern."""
|
|
query = self.db.query(AlertRuleDB)
|
|
|
|
if user_id:
|
|
query = query.filter(AlertRuleDB.user_id == user_id)
|
|
if topic_id:
|
|
query = query.filter(AlertRuleDB.topic_id == topic_id)
|
|
if is_active is not None:
|
|
query = query.filter(AlertRuleDB.is_active == is_active)
|
|
|
|
return query.order_by(AlertRuleDB.priority.desc()).all()
|
|
|
|
# ==================== UPDATE ====================
|
|
|
|
def update(
|
|
self,
|
|
rule_id: str,
|
|
name: str = None,
|
|
description: str = None,
|
|
conditions: List[Dict] = None,
|
|
action_type: str = None,
|
|
action_config: Dict = None,
|
|
priority: int = None,
|
|
is_active: bool = None,
|
|
) -> Optional[AlertRuleDB]:
|
|
"""Aktualisiert eine Regel."""
|
|
rule = self.get_by_id(rule_id)
|
|
if not rule:
|
|
return None
|
|
|
|
if name is not None:
|
|
rule.name = name
|
|
if description is not None:
|
|
rule.description = description
|
|
if conditions is not None:
|
|
rule.conditions = conditions
|
|
if action_type is not None:
|
|
rule.action_type = RuleActionEnum(action_type)
|
|
if action_config is not None:
|
|
rule.action_config = action_config
|
|
if priority is not None:
|
|
rule.priority = priority
|
|
if is_active is not None:
|
|
rule.is_active = is_active
|
|
|
|
self.db.commit()
|
|
self.db.refresh(rule)
|
|
return rule
|
|
|
|
def increment_match_count(self, rule_id: str) -> Optional[AlertRuleDB]:
|
|
"""Erhöht den Match-Counter einer Regel."""
|
|
rule = self.get_by_id(rule_id)
|
|
if not rule:
|
|
return None
|
|
|
|
rule.match_count += 1
|
|
rule.last_matched_at = datetime.utcnow()
|
|
|
|
self.db.commit()
|
|
self.db.refresh(rule)
|
|
return rule
|
|
|
|
# ==================== DELETE ====================
|
|
|
|
def delete(self, rule_id: str) -> bool:
|
|
"""Löscht eine Regel."""
|
|
rule = self.get_by_id(rule_id)
|
|
if not rule:
|
|
return False
|
|
|
|
self.db.delete(rule)
|
|
self.db.commit()
|
|
return True
|
|
|
|
# ==================== CONVERSION ====================
|
|
|
|
def to_dict(self, rule: AlertRuleDB) -> Dict[str, Any]:
|
|
"""Konvertiert DB-Model zu Dictionary."""
|
|
return {
|
|
"id": rule.id,
|
|
"topic_id": rule.topic_id,
|
|
"user_id": rule.user_id,
|
|
"name": rule.name,
|
|
"description": rule.description,
|
|
"conditions": rule.conditions,
|
|
"action_type": rule.action_type.value,
|
|
"action_config": rule.action_config,
|
|
"priority": rule.priority,
|
|
"is_active": rule.is_active,
|
|
"stats": {
|
|
"match_count": rule.match_count,
|
|
"last_matched_at": rule.last_matched_at.isoformat() if rule.last_matched_at else None,
|
|
},
|
|
"created_at": rule.created_at.isoformat() if rule.created_at else None,
|
|
"updated_at": rule.updated_at.isoformat() if rule.updated_at else None,
|
|
}
|