[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>
This commit is contained in:
185
backend-lehrer/alerts_agent/db/topic_repository.py
Normal file
185
backend-lehrer/alerts_agent/db/topic_repository.py
Normal file
@@ -0,0 +1,185 @@
|
||||
"""
|
||||
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,
|
||||
}
|
||||
Reference in New Issue
Block a user