Export: PDF/DOCX-Dateimetadaten (Title, Author, Subject, Keywords, Category, Comments)

- Neue Helper-Funktion _build_export_metadata baut einheitliches Metadaten-Dict
- PDF via HTML-Meta-Tags (title, author, description, keywords, generator, lang)
- DOCX via doc.core_properties (title, author, subject, keywords, comments,
  category, last_modified_by, language, content_status, created, modified)
- Keywords aus OSINT + Typ + Organisation + category_labels + Top-5-Orten
- Comments-Feld mit strukturiertem Block (Incident-ID, Typ, Scope, Umfang, Orte)
- Router laedt Organisation + Top-Orte aus article_locations und reicht sie durch
Dieser Commit ist enthalten in:
claude-dev
2026-04-20 18:58:34 +00:00
Ursprung 0d6ad8ea90
Commit c0f68e40a5
3 geänderte Dateien mit 198 neuen und 3 gelöschten Zeilen

Datei anzeigen

@@ -988,6 +988,27 @@ async def export_incident(
user_row = await cursor.fetchone()
creator = user_row["email"] if user_row else "Unbekannt"
# Organisation (fuer Dateimetadaten)
organization_name = None
if incident.get("tenant_id"):
cursor = await db.execute(
"SELECT name FROM organizations WHERE id = ?", (incident["tenant_id"],)
)
org_row = await cursor.fetchone()
organization_name = org_row["name"] if org_row else None
# Top-Orte (fuer Keyword-Metadaten)
cursor = await db.execute(
"""SELECT location_name, COUNT(*) AS cnt
FROM article_locations
WHERE incident_id = ?
GROUP BY COALESCE(location_name_normalized, location_name)
ORDER BY cnt DESC
LIMIT 5""",
(incident_id,),
)
top_locations = [r["location_name"] for r in await cursor.fetchall() if r["location_name"]]
# Artikel
cursor = await db.execute(
"SELECT * FROM articles WHERE incident_id = ? ORDER BY collected_at DESC",
@@ -1037,7 +1058,12 @@ async def export_incident(
scope_labels_key = scope_labels.get(scope, "lagebericht")
if format == "pdf":
pdf_bytes = await generate_pdf(incident, articles, fact_checks, snapshots, scope, creator, exec_summary, sections=sections_set)
pdf_bytes = await generate_pdf(
incident, articles, fact_checks, snapshots, scope, creator, exec_summary,
sections=sections_set,
organization_name=organization_name,
top_locations=top_locations,
)
filename = f"{slug}_{scope_labels_key}_{date_str}.pdf"
return StreamingResponse(
io.BytesIO(pdf_bytes),
@@ -1045,7 +1071,12 @@ async def export_incident(
headers={"Content-Disposition": f'attachment; filename="{filename}"'},
)
else:
docx_bytes = await generate_docx(incident, articles, fact_checks, snapshots, scope, creator, exec_summary, sections=sections_set)
docx_bytes = await generate_docx(
incident, articles, fact_checks, snapshots, scope, creator, exec_summary,
sections=sections_set,
organization_name=organization_name,
top_locations=top_locations,
)
filename = f"{slug}_{scope_labels_key}_{date_str}.docx"
return StreamingResponse(
io.BytesIO(docx_bytes),