Files
breakpilot-lehrer/backend-lehrer/alerts_agent/db/profile_repository.py
Benjamin Admin b6983ab1dc [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>
2026-04-24 23:35:37 +02:00

227 lines
7.4 KiB
Python

"""
Repository für Alert Profiles (Nutzer-Profile für Relevanz-Scoring).
"""
import uuid
from datetime import datetime
from typing import Optional, List, Dict, Any
from sqlalchemy.orm import Session as DBSession
from sqlalchemy.orm.attributes import flag_modified
from .models import AlertProfileDB
class ProfileRepository:
"""Repository für Alert Profiles (Nutzer-Profile für Relevanz-Scoring)."""
def __init__(self, db: DBSession):
self.db = db
# ==================== CREATE / GET-OR-CREATE ====================
def get_or_create(self, user_id: str = None) -> AlertProfileDB:
"""Holt oder erstellt ein Profil."""
profile = self.get_by_user_id(user_id)
if profile:
return profile
# Neues Profil erstellen
profile = AlertProfileDB(
id=str(uuid.uuid4()),
user_id=user_id,
name="Default" if not user_id else f"Profile {user_id[:8]}",
)
self.db.add(profile)
self.db.commit()
self.db.refresh(profile)
return profile
def create_default_education_profile(self, user_id: str = None) -> AlertProfileDB:
"""Erstellt ein Standard-Profil für Bildungsthemen."""
profile = AlertProfileDB(
id=str(uuid.uuid4()),
user_id=user_id,
name="Bildung Default",
priorities=[
{
"label": "Inklusion",
"weight": 0.9,
"keywords": ["inklusiv", "Förderbedarf", "Behinderung", "Barrierefreiheit"],
"description": "Inklusive Bildung, Förderschulen, Nachteilsausgleich"
},
{
"label": "Datenschutz Schule",
"weight": 0.85,
"keywords": ["DSGVO", "Schülerfotos", "Einwilligung", "personenbezogene Daten"],
"description": "DSGVO in Schulen, Datenschutz bei Klassenfotos"
},
{
"label": "Schulrecht Bayern",
"weight": 0.8,
"keywords": ["BayEUG", "Schulordnung", "Kultusministerium", "Bayern"],
"description": "Bayerisches Schulrecht, Verordnungen"
},
{
"label": "Digitalisierung Schule",
"weight": 0.7,
"keywords": ["DigitalPakt", "Tablet-Klasse", "Lernplattform"],
"description": "Digitale Medien im Unterricht"
},
],
exclusions=["Stellenanzeige", "Praktikum gesucht", "Werbung", "Pressemitteilung"],
policies={
"prefer_german_sources": True,
"max_age_days": 30,
"min_content_length": 100,
}
)
self.db.add(profile)
self.db.commit()
self.db.refresh(profile)
return profile
# ==================== READ ====================
def get_by_id(self, profile_id: str) -> Optional[AlertProfileDB]:
"""Holt ein Profil nach ID."""
return self.db.query(AlertProfileDB).filter(
AlertProfileDB.id == profile_id
).first()
def get_by_user_id(self, user_id: str) -> Optional[AlertProfileDB]:
"""Holt ein Profil nach User-ID."""
if not user_id:
# Default-Profil ohne User
return self.db.query(AlertProfileDB).filter(
AlertProfileDB.user_id.is_(None)
).first()
return self.db.query(AlertProfileDB).filter(
AlertProfileDB.user_id == user_id
).first()
# ==================== UPDATE ====================
def update_priorities(
self,
profile_id: str,
priorities: List[Dict],
) -> Optional[AlertProfileDB]:
"""Aktualisiert die Prioritäten eines Profils."""
profile = self.get_by_id(profile_id)
if not profile:
return None
profile.priorities = priorities
self.db.commit()
self.db.refresh(profile)
return profile
def update_exclusions(
self,
profile_id: str,
exclusions: List[str],
) -> Optional[AlertProfileDB]:
"""Aktualisiert die Ausschlüsse eines Profils."""
profile = self.get_by_id(profile_id)
if not profile:
return None
profile.exclusions = exclusions
self.db.commit()
self.db.refresh(profile)
return profile
def add_feedback(
self,
profile_id: str,
title: str,
url: str,
is_relevant: bool,
reason: str = "",
) -> Optional[AlertProfileDB]:
"""Fügt Feedback als Beispiel hinzu."""
profile = self.get_by_id(profile_id)
if not profile:
return None
example = {
"title": title,
"url": url,
"reason": reason,
"added_at": datetime.utcnow().isoformat(),
}
if is_relevant:
examples = list(profile.positive_examples or [])
examples.append(example)
profile.positive_examples = examples[-20:] # Max 20
profile.total_kept += 1
flag_modified(profile, "positive_examples")
else:
examples = list(profile.negative_examples or [])
examples.append(example)
profile.negative_examples = examples[-20:] # Max 20
profile.total_dropped += 1
flag_modified(profile, "negative_examples")
profile.total_scored += 1
self.db.commit()
self.db.refresh(profile)
return profile
def update_stats(
self,
profile_id: str,
kept: int = 0,
dropped: int = 0,
) -> Optional[AlertProfileDB]:
"""Aktualisiert die Statistiken eines Profils."""
profile = self.get_by_id(profile_id)
if not profile:
return None
profile.total_scored += kept + dropped
profile.total_kept += kept
profile.total_dropped += dropped
self.db.commit()
self.db.refresh(profile)
return profile
# ==================== DELETE ====================
def delete(self, profile_id: str) -> bool:
"""Löscht ein Profil."""
profile = self.get_by_id(profile_id)
if not profile:
return False
self.db.delete(profile)
self.db.commit()
return True
# ==================== CONVERSION ====================
def to_dict(self, profile: AlertProfileDB) -> Dict[str, Any]:
"""Konvertiert DB-Model zu Dictionary."""
return {
"id": profile.id,
"user_id": profile.user_id,
"name": profile.name,
"priorities": profile.priorities,
"exclusions": profile.exclusions,
"policies": profile.policies,
"examples": {
"positive": len(profile.positive_examples or []),
"negative": len(profile.negative_examples or []),
},
"stats": {
"total_scored": profile.total_scored,
"total_kept": profile.total_kept,
"total_dropped": profile.total_dropped,
"accuracy_estimate": profile.accuracy_estimate,
},
"created_at": profile.created_at.isoformat() if profile.created_at else None,
"updated_at": profile.updated_at.isoformat() if profile.updated_at else None,
}