""" 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, }