From 279df0f56bfdb63582a7c8e6afa97a3271990068 Mon Sep 17 00:00:00 2001 From: "Claude (claude-dev)" Date: Fri, 22 May 2026 18:39:21 +0000 Subject: [PATCH] feat(export): neutrale Export-Variante ohne Firmenbranding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Beim Bericht-Export lässt sich im Modal nun zwischen "Mit AegisSight-Branding" und "Ohne Firmen-Branding" wählen. Im neutralen Modus entfallen Logo, AegisSight-Zeile auf dem Deckblatt und Branding-Footer; die Datei-Metadaten werden neutralisiert. Das Deckblatt mit Titel, Stand und Ersteller bleibt erhalten. Betrifft PDF und DOCX. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/report_generator.py | 63 +++++++++++++++++++++++--------- src/report_templates/report.html | 6 +-- src/routers/incidents.py | 3 ++ src/static/dashboard.html | 5 +++ src/static/i18n/de.json | 3 ++ src/static/i18n/en.json | 3 ++ src/static/js/api.js | 5 ++- src/static/js/app.js | 4 +- 8 files changed, 70 insertions(+), 22 deletions(-) diff --git a/src/report_generator.py b/src/report_generator.py index 8ba42f0..d85ec47 100644 --- a/src/report_generator.py +++ b/src/report_generator.py @@ -462,8 +462,12 @@ def _build_export_metadata( organization_name: str | None, top_locations: list[str] | None, snapshot_count: int = 0, + include_branding: bool = True, ) -> dict: - """Einheitlicher Metadaten-Dict fuer PDF (HTML-Meta-Tags) und DOCX (core_properties).""" + """Einheitlicher Metadaten-Dict fuer PDF (HTML-Meta-Tags) und DOCX (core_properties). + + include_branding=False neutralisiert alle AegisSight-Firmenbezeichnungen (White-Label-Export). + """ is_research = incident.get("type") == "research" type_label = "Hintergrundrecherche" if is_research else "Live-Monitoring" category = "OSINT-Hintergrundrecherche" if is_research else "OSINT-Lagebericht" @@ -546,23 +550,37 @@ def _build_export_metadata( comments_lines.append("Orte: " + ", ".join(top_locations[:5])) comments = "\n".join(comments_lines) - publisher = organization_name or "AegisSight" - identifier = f"urn:aegissight:incident:{incident.get('id', '0')}:{now.strftime('%Y%m%dT%H%M%S')}" - rights = ( - "Vertrauliche Lageanalyse — AegisSight Monitor. " - "Weitergabe nur an autorisierte Empfänger." - ) + # Branding-abhaengige Felder: bei include_branding=False neutralisiert (White-Label-Export) + if include_branding: + publisher = organization_name or "AegisSight" + author = creator or "AegisSight Monitor" + creator_app = "AegisSight Monitor" + producer = "WeasyPrint + AegisSight Monitor" + urn_ns = "aegissight" + rights = ( + "Vertrauliche Lageanalyse — AegisSight Monitor. " + "Weitergabe nur an autorisierte Empfänger." + ) + else: + publisher = organization_name or "" + author = creator or "Unbekannt" + creator_app = "" + producer = "WeasyPrint" + urn_ns = "report" + rights = "Vertrauliche Lageanalyse. Weitergabe nur an autorisierte Empfänger." + identifier = f"urn:{urn_ns}:incident:{incident.get('id', '0')}:{now.strftime('%Y%m%dT%H%M%S')}" return { "title": title, - "author": creator or "AegisSight Monitor", + "author": author, "subject": subject, "keywords": unique_keywords, "keywords_comma": ", ".join(unique_keywords), "keywords_semicolon": "; ".join(unique_keywords), "category": category, "comments": comments, - "creator_app": "AegisSight Monitor", + "creator_app": creator_app, + "producer": producer, "language": "de-DE", "created": created, "modified": modified, @@ -634,7 +652,7 @@ def _enrich_pdf_metadata(pdf_bytes: bytes, meta: dict) -> bytes: # PDF Namespace xmp["pdf:Keywords"] = meta.get("keywords_comma", "") - xmp["pdf:Producer"] = "WeasyPrint + AegisSight Monitor" + xmp["pdf:Producer"] = meta.get("producer", "WeasyPrint + AegisSight Monitor") # XMP Namespace xmp["xmp:CreatorTool"] = meta.get("creator_app", "AegisSight Monitor") @@ -681,6 +699,7 @@ async def generate_pdf( organization_name: str | None = None, top_locations: list[str] | None = None, snapshot_count: int = 0, + include_branding: bool = True, ) -> bytes: """PDF-Report via WeasyPrint generieren.""" # Sections aus scope ableiten wenn nicht explizit angegeben @@ -713,6 +732,7 @@ async def generate_pdf( meta = _build_export_metadata( incident, articles, fact_checks, all_sources, creator, scope, sections, organization_name, top_locations, snapshot_count=snapshot_count, + include_branding=include_branding, ) env = Environment(loader=FileSystemLoader(str(TEMPLATE_DIR))) @@ -741,6 +761,7 @@ async def generate_pdf( timeline=_prepare_timeline(articles) if scope == "full" else [], articles=articles if scope == "full" else [], meta=meta, + include_branding=include_branding, ) # Artikel pub_date aufbereiten @@ -764,6 +785,7 @@ async def generate_docx( organization_name: str | None = None, top_locations: list[str] | None = None, snapshot_count: int = 0, + include_branding: bool = True, ) -> bytes: """Word-Report via python-docx generieren.""" doc = Document() @@ -795,6 +817,7 @@ async def generate_docx( meta = _build_export_metadata( incident, articles, fact_checks, all_sources, creator, scope, sections, organization_name, top_locations, snapshot_count=snapshot_count, + include_branding=include_branding, ) # Dateimetadaten setzen (sichtbar in Explorer/Finder, DMS-Systemen) @@ -823,13 +846,15 @@ async def generate_docx( for _ in range(6): doc.add_paragraph() - title_para = doc.add_paragraph() - title_para.alignment = WD_ALIGN_PARAGRAPH.CENTER - run = title_para.add_run("AegisSight Monitor") - run.font.size = Pt(12) - run.font.color.rgb = RGBColor(0x0a, 0x18, 0x32) + # Firmenname-Zeile nur im gebrandeten Export + if include_branding: + title_para = doc.add_paragraph() + title_para.alignment = WD_ALIGN_PARAGRAPH.CENTER + run = title_para.add_run("AegisSight Monitor") + run.font.size = Pt(12) + run.font.color.rgb = RGBColor(0x0a, 0x18, 0x32) - doc.add_paragraph() + doc.add_paragraph() type_label = "Hintergrundrecherche" if incident.get("type") == "research" else "Live-Monitoring" type_para = doc.add_paragraph() @@ -978,7 +1003,11 @@ async def generate_docx( doc.add_paragraph() footer = doc.add_paragraph() footer.alignment = WD_ALIGN_PARAGRAPH.CENTER - run = footer.add_run(f"Erstellt mit AegisSight Monitor — aegis-sight.de — {now.strftime('%d.%m.%Y')}") + if include_branding: + footer_text = f"Erstellt mit AegisSight Monitor — aegis-sight.de — {now.strftime('%d.%m.%Y')}" + else: + footer_text = f"Stand: {now.strftime('%d.%m.%Y')}" + run = footer.add_run(footer_text) run.font.size = Pt(8) run.font.color.rgb = RGBColor(0x0a, 0x18, 0x32) diff --git a/src/report_templates/report.html b/src/report_templates/report.html index aad78ab..2896027 100644 --- a/src/report_templates/report.html +++ b/src/report_templates/report.html @@ -84,7 +84,7 @@ tr:nth-child(even) { background: #f8f9fa; }
- + {% if include_branding %}{% endif %}
{{ incident_type_label }}
{{ incident.title }}
@@ -92,7 +92,7 @@ tr:nth-child(even) { background: #f8f9fa; }
Erstellt von: {{ creator }}
{% if incident.organization_name %}
Organisation: {{ incident.organization_name }}
{% endif %}
-
AegisSight Monitor
+ {% if include_branding %}
AegisSight Monitor
{% endif %}
@@ -208,7 +208,7 @@ tr:nth-child(even) { background: #f8f9fa; } {% endif %} diff --git a/src/routers/incidents.py b/src/routers/incidents.py index 921538b..e126ffd 100644 --- a/src/routers/incidents.py +++ b/src/routers/incidents.py @@ -1152,6 +1152,7 @@ async def export_incident( format: str = Query("pdf", pattern="^(pdf|docx)$"), scope: str = Query("report", pattern="^(summary|report|full)$"), sections: str = Query(None), + branding: str = Query("on", pattern="^(on|off)$"), current_user: dict = Depends(get_current_user), db: aiosqlite.Connection = Depends(db_dependency), ): @@ -1268,6 +1269,7 @@ async def export_incident( organization_name=organization_name, top_locations=top_locations, snapshot_count=snapshot_count, + include_branding=(branding == "on"), ) filename = f"{slug}_{scope_labels_key}_{date_str}.pdf" return StreamingResponse( @@ -1282,6 +1284,7 @@ async def export_incident( organization_name=organization_name, top_locations=top_locations, snapshot_count=snapshot_count, + include_branding=(branding == "on"), ) filename = f"{slug}_{scope_labels_key}_{date_str}.docx" return StreamingResponse( diff --git a/src/static/dashboard.html b/src/static/dashboard.html index 2946008..5eb5a0f 100644 --- a/src/static/dashboard.html +++ b/src/static/dashboard.html @@ -850,6 +850,11 @@ +
+ + + +