Mac-Worker:
- pdf_export.py mit reportlab (pure Python, kein externes Tool)
- Nutzt dieselbe profile.docx-Konfig wie der DOCX-Renderer →
strukturell identisch, nur andere Optik
- POST /api/export/pdf
- requirements: reportlab==4.2.5
LXC:
- Job.pdf_path neu (Migration: ALTER TABLE job ADD COLUMN)
- Pipeline-Step nach DOCX: PDF wird ebenfalls erzeugt
(Progress: 90 DOCX → 95 PDF → 100 done)
- /api/jobs/{id}/download/pdf
- /api/jobs/{id}/retry löscht PDF mit, damit es neu erzeugt wird
- Frontend: zusätzlicher Download-Button "PDF" (vorne im Array)
- Tabelle: Download-Spalte 320px, 3-spaltiges Grid (5 Buttons → 3+2)
- Cards (Mobile) bleiben 2-spaltig (5 Buttons → 3 Zeilen)
67 lines
2.7 KiB
Python
67 lines
2.7 KiB
Python
from pathlib import Path
|
|
import httpx
|
|
from .config import settings
|
|
|
|
|
|
class MacClient:
|
|
def __init__(self, base_url: str | None = None, timeout: int | None = None):
|
|
self.base_url = (base_url or settings.mac_api_url).rstrip("/")
|
|
self.timeout = timeout or settings.mac_api_timeout
|
|
|
|
async def health(self) -> dict:
|
|
async with httpx.AsyncClient(timeout=10) as c:
|
|
r = await c.get(f"{self.base_url}/health")
|
|
r.raise_for_status()
|
|
return r.json()
|
|
|
|
async def list_profiles(self) -> list[dict]:
|
|
async with httpx.AsyncClient(timeout=10) as c:
|
|
r = await c.get(f"{self.base_url}/api/profiles")
|
|
r.raise_for_status()
|
|
return r.json().get("profiles", [])
|
|
|
|
async def transcribe(self, audio_path: Path, language: str = "de") -> dict:
|
|
async with httpx.AsyncClient(timeout=self.timeout) as c:
|
|
with audio_path.open("rb") as f:
|
|
files = {"audio": (audio_path.name, f, "application/octet-stream")}
|
|
data = {"language": language}
|
|
r = await c.post(f"{self.base_url}/api/transcribe", files=files, data=data)
|
|
r.raise_for_status()
|
|
return r.json()
|
|
|
|
async def summarize(self, transcript: str, title: str = "", profile: str | None = None) -> dict:
|
|
async with httpx.AsyncClient(timeout=self.timeout) as c:
|
|
r = await c.post(
|
|
f"{self.base_url}/api/summarize",
|
|
json={"transcript": transcript, "title": title, "profile": profile},
|
|
)
|
|
r.raise_for_status()
|
|
return r.json()
|
|
|
|
async def protocol(self, transcript: str, summary: dict, title: str = "", profile: str | None = None) -> dict:
|
|
async with httpx.AsyncClient(timeout=self.timeout) as c:
|
|
r = await c.post(
|
|
f"{self.base_url}/api/protocol",
|
|
json={"transcript": transcript, "summary": summary, "title": title, "profile": profile},
|
|
)
|
|
r.raise_for_status()
|
|
return r.json()
|
|
|
|
async def export_docx(self, data: dict, profile: str | None = None) -> bytes:
|
|
async with httpx.AsyncClient(timeout=self.timeout) as c:
|
|
r = await c.post(
|
|
f"{self.base_url}/api/export/docx",
|
|
json={"data": data, "profile": profile},
|
|
)
|
|
r.raise_for_status()
|
|
return r.content
|
|
|
|
async def export_pdf(self, data: dict, profile: str | None = None) -> bytes:
|
|
async with httpx.AsyncClient(timeout=self.timeout) as c:
|
|
r = await c.post(
|
|
f"{self.base_url}/api/export/pdf",
|
|
json={"data": data, "profile": profile},
|
|
)
|
|
r.raise_for_status()
|
|
return r.content
|