Files
breakpilot-lehrer/backend-lehrer/alerts_agent/db/topic_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

186 lines
5.6 KiB
Python

"""
Repository für Alert Topics (Feed-Quellen).
"""
import uuid
from datetime import datetime
from typing import Optional, List, Dict, Any
from sqlalchemy.orm import Session as DBSession
from .models import AlertTopicDB, FeedTypeEnum
class TopicRepository:
"""Repository für Alert Topics (Feed-Quellen)."""
def __init__(self, db: DBSession):
self.db = db
# ==================== CREATE ====================
def create(
self,
name: str,
feed_url: str = None,
feed_type: str = "rss",
user_id: str = None,
description: str = "",
fetch_interval_minutes: int = 60,
is_active: bool = True,
) -> AlertTopicDB:
"""Erstellt ein neues Topic."""
topic = AlertTopicDB(
id=str(uuid.uuid4()),
user_id=user_id,
name=name,
description=description,
feed_url=feed_url,
feed_type=FeedTypeEnum(feed_type),
fetch_interval_minutes=fetch_interval_minutes,
is_active=is_active,
)
self.db.add(topic)
self.db.commit()
self.db.refresh(topic)
return topic
# ==================== READ ====================
def get_by_id(self, topic_id: str) -> Optional[AlertTopicDB]:
"""Holt ein Topic nach ID."""
return self.db.query(AlertTopicDB).filter(
AlertTopicDB.id == topic_id
).first()
def get_all(
self,
user_id: str = None,
is_active: bool = None,
limit: int = 100,
offset: int = 0,
) -> List[AlertTopicDB]:
"""Holt alle Topics mit optionalen Filtern."""
query = self.db.query(AlertTopicDB)
if user_id:
query = query.filter(AlertTopicDB.user_id == user_id)
if is_active is not None:
query = query.filter(AlertTopicDB.is_active == is_active)
return query.order_by(
AlertTopicDB.created_at.desc()
).offset(offset).limit(limit).all()
def get_active_for_fetch(self) -> List[AlertTopicDB]:
"""Holt alle aktiven Topics die gefetcht werden sollten."""
return self.db.query(AlertTopicDB).filter(
AlertTopicDB.is_active == True,
AlertTopicDB.feed_url.isnot(None),
).all()
# ==================== UPDATE ====================
def update(
self,
topic_id: str,
name: str = None,
description: str = None,
feed_url: str = None,
feed_type: str = None,
is_active: bool = None,
fetch_interval_minutes: int = None,
) -> Optional[AlertTopicDB]:
"""Aktualisiert ein Topic."""
topic = self.get_by_id(topic_id)
if not topic:
return None
if name is not None:
topic.name = name
if description is not None:
topic.description = description
if feed_url is not None:
topic.feed_url = feed_url
if feed_type is not None:
topic.feed_type = FeedTypeEnum(feed_type)
if is_active is not None:
topic.is_active = is_active
if fetch_interval_minutes is not None:
topic.fetch_interval_minutes = fetch_interval_minutes
self.db.commit()
self.db.refresh(topic)
return topic
def update_fetch_status(
self,
topic_id: str,
last_fetch_error: str = None,
items_fetched: int = 0,
) -> Optional[AlertTopicDB]:
"""Aktualisiert den Fetch-Status eines Topics."""
topic = self.get_by_id(topic_id)
if not topic:
return None
topic.last_fetched_at = datetime.utcnow()
topic.last_fetch_error = last_fetch_error
topic.total_items_fetched += items_fetched
self.db.commit()
self.db.refresh(topic)
return topic
def increment_stats(
self,
topic_id: str,
kept: int = 0,
dropped: int = 0,
) -> Optional[AlertTopicDB]:
"""Erhöht die Statistiken eines Topics."""
topic = self.get_by_id(topic_id)
if not topic:
return None
topic.items_kept += kept
topic.items_dropped += dropped
self.db.commit()
self.db.refresh(topic)
return topic
# ==================== DELETE ====================
def delete(self, topic_id: str) -> bool:
"""Löscht ein Topic (und alle zugehörigen Items via CASCADE)."""
topic = self.get_by_id(topic_id)
if not topic:
return False
self.db.delete(topic)
self.db.commit()
return True
# ==================== CONVERSION ====================
def to_dict(self, topic: AlertTopicDB) -> Dict[str, Any]:
"""Konvertiert DB-Model zu Dictionary."""
return {
"id": topic.id,
"user_id": topic.user_id,
"name": topic.name,
"description": topic.description,
"feed_url": topic.feed_url,
"feed_type": topic.feed_type.value,
"is_active": topic.is_active,
"fetch_interval_minutes": topic.fetch_interval_minutes,
"last_fetched_at": topic.last_fetched_at.isoformat() if topic.last_fetched_at else None,
"last_fetch_error": topic.last_fetch_error,
"stats": {
"total_items_fetched": topic.total_items_fetched,
"items_kept": topic.items_kept,
"items_dropped": topic.items_dropped,
},
"created_at": topic.created_at.isoformat() if topic.created_at else None,
"updated_at": topic.updated_at.isoformat() if topic.updated_at else None,
}