Files
guides/backend/routes.py
2026-05-27 01:00:33 +02:00

207 lines
6.7 KiB
Python

import asyncio
import uuid
from datetime import datetime, timezone
from pathlib import Path
from fastapi import APIRouter, HTTPException
from fastapi.responses import FileResponse
from config import FORMAT_META, STORAGE_DIR
from database import (
create_guide, delete_guide, get_guide, list_guides,
create_baustein as db_create_baustein, list_bausteine, get_baustein, delete_baustein as db_delete_baustein,
list_suggestions, get_suggestion, update_suggestion, delete_suggestion,
)
from generator import generate_guide, rework_guide, cancel_guide, generate_suggestions, generate_baustein_detail, is_suggestions_generating
from models import (
GuideCreateRequest, GuideReworkRequest, GuideResponse,
BausteinCreateRequest, BausteinResponse, SuggestionResponse,
)
router = APIRouter(prefix="/api")
@router.get("/formats")
async def get_formats():
return FORMAT_META
@router.post("/guides", response_model=GuideResponse)
async def create(req: GuideCreateRequest):
now = datetime.now(timezone.utc).isoformat()
guide = {
"id": str(uuid.uuid4()),
"topic": req.topic.strip(),
"format": req.format,
"instructions": req.instructions.strip(),
"status": "queued",
"progress": None,
"html_path": None,
"pdf_path": None,
"created_at": now,
"updated_at": now,
}
await create_guide(guide)
asyncio.create_task(generate_guide(guide["id"], guide["topic"], guide["format"], guide["instructions"]))
return guide
@router.get("/guides", response_model=list[GuideResponse])
async def list_all():
return await list_guides()
@router.get("/guides/{guide_id}", response_model=GuideResponse)
async def get_one(guide_id: str):
guide = await get_guide(guide_id)
if guide is None:
raise HTTPException(404, "Guide nicht gefunden")
return guide
@router.get("/guides/{guide_id}/html")
async def download_html(guide_id: str):
guide = await get_guide(guide_id)
if guide is None:
raise HTTPException(404, "Guide nicht gefunden")
if guide["status"] != "done" or not guide["html_path"]:
raise HTTPException(404, "HTML nicht verfügbar")
path = Path(guide["html_path"])
if not path.exists():
raise HTTPException(404, "Datei nicht gefunden")
return FileResponse(path, filename=f"{guide['topic']}-{guide['format']}.html", media_type="text/html")
@router.post("/guides/{guide_id}/rework")
async def rework(guide_id: str, req: GuideReworkRequest):
guide = await get_guide(guide_id)
if guide is None:
raise HTTPException(404, "Guide nicht gefunden")
if guide["status"] != "done":
raise HTTPException(400, "Guide muss fertig sein")
asyncio.create_task(rework_guide(guide_id, guide["topic"], guide["format"], req.instructions.strip()))
return {"ok": True}
@router.post("/guides/{guide_id}/cancel")
async def cancel(guide_id: str):
cancelled = await cancel_guide(guide_id)
if not cancelled:
raise HTTPException(404, "Kein aktiver Prozess gefunden")
return {"ok": True}
@router.get("/guides/{guide_id}/pdf")
async def download_pdf(guide_id: str):
guide = await get_guide(guide_id)
if guide is None:
raise HTTPException(404, "Guide nicht gefunden")
if guide["status"] != "done" or not guide["pdf_path"]:
raise HTTPException(404, "PDF nicht verfügbar")
path = Path(guide["pdf_path"])
if not path.exists():
raise HTTPException(404, "Datei nicht gefunden")
return FileResponse(path, filename=f"{guide['topic']}-{guide['format']}.pdf", media_type="application/pdf")
@router.delete("/guides/{guide_id}")
async def remove(guide_id: str):
guide = await get_guide(guide_id)
if guide is None:
raise HTTPException(404, "Guide nicht gefunden")
if guide["html_path"]:
Path(guide["html_path"]).unlink(missing_ok=True)
if guide["pdf_path"]:
Path(guide["pdf_path"]).unlink(missing_ok=True)
await delete_guide(guide_id)
return {"ok": True}
# --- Bausteine ---
@router.get("/bausteine", response_model=list[BausteinResponse])
async def get_bausteine(topic: str):
return await list_bausteine(topic)
@router.post("/bausteine", response_model=BausteinResponse)
async def add_baustein(req: BausteinCreateRequest):
now = datetime.now(timezone.utc).isoformat()
baustein = {
"id": str(uuid.uuid4()),
"topic": req.topic.strip(),
"title": req.title.strip(),
"description": "",
"purpose": "",
"example": "",
"created_at": now,
"updated_at": now,
}
await db_create_baustein(baustein)
asyncio.create_task(generate_baustein_detail(baustein["id"], baustein["topic"], baustein["title"]))
return baustein
@router.delete("/bausteine/{baustein_id}")
async def remove_baustein(baustein_id: str):
b = await get_baustein(baustein_id)
if b is None:
raise HTTPException(404, "Baustein nicht gefunden")
await db_delete_baustein(baustein_id)
return {"ok": True}
# --- Baustein Suggestions ---
@router.get("/bausteine/suggestions", response_model=list[SuggestionResponse])
async def get_suggestions(topic: str):
return await list_suggestions(topic)
@router.post("/bausteine/suggestions/generate")
async def trigger_suggestions(topic: str):
if is_suggestions_generating(topic):
return {"ok": True, "status": "already_generating"}
guides = await list_guides()
html_paths = []
for g in guides:
if g["topic"] == topic and g["status"] == "done" and g["html_path"]:
html_paths.append(Path(g["html_path"]))
asyncio.create_task(generate_suggestions(topic, html_paths))
return {"ok": True}
@router.get("/bausteine/suggestions/status")
async def suggestions_status(topic: str):
return {"generating": is_suggestions_generating(topic)}
@router.post("/bausteine/suggestions/{suggestion_id}/add")
async def accept_suggestion(suggestion_id: str):
s = await get_suggestion(suggestion_id)
if s is None:
raise HTTPException(404, "Vorschlag nicht gefunden")
now = datetime.now(timezone.utc).isoformat()
baustein = {
"id": str(uuid.uuid4()),
"topic": s["topic"],
"title": s["title"],
"description": s["description"],
"purpose": s["purpose"],
"example": s["example"],
"created_at": now,
"updated_at": now,
}
await db_create_baustein(baustein)
await delete_suggestion(suggestion_id)
return baustein
@router.post("/bausteine/suggestions/{suggestion_id}/ignore")
async def ignore_suggestion(suggestion_id: str):
s = await get_suggestion(suggestion_id)
if s is None:
raise HTTPException(404, "Vorschlag nicht gefunden")
await update_suggestion(suggestion_id, status="ignored")
return {"ok": True}