Files
Benjamin Boenisch 5a31f52310 Initial commit: breakpilot-lehrer - Lehrer KI Platform
Services: Admin-Lehrer, Backend-Lehrer, Studio v2, Website,
Klausur-Service, School-Service, Voice-Service, Geo-Service,
BreakPilot Drive, Agent-Core

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 23:47:26 +01:00

174 lines
4.4 KiB
Python

"""
Legal Crawler API Routes.
Endpoints für das Crawlen und Abrufen von rechtlichen Bildungsinhalten.
"""
import logging
import asyncio
from typing import List, Optional
from fastapi import APIRouter, HTTPException, BackgroundTasks
from pydantic import BaseModel
from ..services.legal_crawler import get_legal_crawler, LegalCrawler
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/legal-crawler", tags=["legal-crawler"])
class CrawlStatusResponse(BaseModel):
"""Response für Crawl-Status."""
status: str
message: str
stats: Optional[dict] = None
class LegalDocumentResponse(BaseModel):
"""Response für ein rechtliches Dokument."""
id: str
url: str
title: str
law_name: Optional[str]
state: Optional[str]
paragraphs: Optional[list]
last_crawled_at: Optional[str]
class LegalReferenceFromDB(BaseModel):
"""Rechtliche Referenz aus der DB."""
law: str
url: str
state: Optional[str]
title: str
paragraphs: list
# Globaler Status für laufenden Crawl
_crawl_status = {
"running": False,
"last_run": None,
"last_stats": None,
}
async def _run_crawl(db_pool):
"""Führt den Crawl asynchron durch."""
global _crawl_status
_crawl_status["running"] = True
try:
crawler = get_legal_crawler()
stats = await crawler.crawl_legal_seeds(db_pool)
_crawl_status["last_stats"] = stats
_crawl_status["last_run"] = "completed"
except Exception as e:
logger.error(f"Crawl-Fehler: {e}")
_crawl_status["last_run"] = f"error: {str(e)}"
finally:
_crawl_status["running"] = False
@router.post("/start", response_model=CrawlStatusResponse)
async def start_crawl(background_tasks: BackgroundTasks):
"""
Startet einen neuen Crawl für alle Legal-Seeds.
Der Crawl läuft im Hintergrund und kann über /status abgefragt werden.
"""
global _crawl_status
if _crawl_status["running"]:
return CrawlStatusResponse(
status="already_running",
message="Ein Crawl läuft bereits. Bitte warten Sie, bis er abgeschlossen ist."
)
# Hinweis: In Produktion würde hier der DB-Pool übergeben werden
# Für jetzt nur Status setzen
_crawl_status["running"] = True
_crawl_status["last_run"] = "started"
return CrawlStatusResponse(
status="started",
message="Crawl wurde gestartet. Nutzen Sie /status um den Fortschritt zu prüfen."
)
@router.get("/status", response_model=CrawlStatusResponse)
async def get_crawl_status():
"""Gibt den aktuellen Crawl-Status zurück."""
return CrawlStatusResponse(
status="running" if _crawl_status["running"] else "idle",
message=_crawl_status.get("last_run") or "Noch nie gecrawlt",
stats=_crawl_status.get("last_stats")
)
@router.get("/documents", response_model=List[LegalDocumentResponse])
async def get_legal_documents(
state: Optional[str] = None,
doc_type: Optional[str] = None,
limit: int = 50
):
"""
Gibt gecrawlte rechtliche Dokumente zurück.
Args:
state: Filter nach Bundesland (z.B. "NW", "BY")
doc_type: Filter nach Dokumenttyp (z.B. "schulgesetz")
limit: Max. Anzahl Dokumente
Returns:
Liste von LegalDocumentResponse
"""
# TODO: DB-Query implementieren wenn DB-Pool verfügbar
# Für jetzt leere Liste zurückgeben
return []
@router.get("/references/{state}")
async def get_legal_references_for_state(state: str):
"""
Gibt rechtliche Referenzen für ein Bundesland zurück.
Dies ist der Endpoint, den der Communication-Service nutzt.
Args:
state: Bundesland-Kürzel (z.B. "NW", "BY", "BE")
Returns:
Dict mit Schulgesetz-Informationen und Paragraphen
"""
# TODO: Aus DB laden
# Mapping von state-Kürzeln zu DB-Werten
state_mapping = {
"NRW": "NW",
"NW": "NW",
"BY": "BY",
"BW": "BW",
"BE": "BE",
"BB": "BB",
"HB": "HB",
"HH": "HH",
"HE": "HE",
"MV": "MV",
"NI": "NI",
"RP": "RP",
"SL": "SL",
"SN": "SN",
"ST": "ST",
"SH": "SH",
"TH": "TH",
}
db_state = state_mapping.get(state.upper(), state.upper())
# Placeholder - später aus DB
return {
"state": state,
"documents": [],
"message": "Dokumente werden nach dem ersten Crawl verfügbar sein"
}