diff --git a/src/backend/site/router.py b/src/backend/site/router.py index 69c9410..7550d25 100644 --- a/src/backend/site/router.py +++ b/src/backend/site/router.py @@ -218,8 +218,35 @@ async def download_pdf(request: Request) -> StreamingResponse: # Cache the generated PDF pdf_cache.set(session_id, result, pdf_buffer) - # Prepare filename - filename = f"swot-analysis-{session_id[:8]}.pdf" + # Prepare filename with company names and date + import re + from datetime import datetime + + # Sanitize company names for filename (remove special chars, limit length) + def sanitize_filename(text: str, max_length: int = 30) -> str: + # Remove special characters, keep alphanumeric, spaces, hyphens + text = re.sub(r"[^\w\s-]", "", text) + # Replace spaces with hyphens + text = re.sub(r"\s+", "-", text.strip()) + # Limit length + return text[:max_length].rstrip("-") + + # Build entity string + primary = sanitize_filename(result.primary_entity) + if result.comparison_entities: + # Include first comparison entity + comparison = sanitize_filename(result.comparison_entities[0]) + entities_str = f"{primary}-vs-{comparison}" + if len(result.comparison_entities) > 1: + entities_str += f"-plus{len(result.comparison_entities) - 1}" + else: + entities_str = primary + + # Add date + date_str = datetime.now().strftime("%Y-%m-%d") + + # Build final filename + filename = f"swot-{entities_str}-{date_str}.pdf" # Return as streaming response (iterate over BytesIO in chunks) def iterfile(): @@ -230,7 +257,7 @@ async def download_pdf(request: Request) -> StreamingResponse: content=iterfile(), media_type="application/pdf", headers={ - "Content-Disposition": f"attachment; filename={filename}", + "Content-Disposition": f'attachment; filename="{filename}"', "Cache-Control": "no-cache", }, ) diff --git a/tests/integration/test_pdf_export.py b/tests/integration/test_pdf_export.py index 74d4773..3287c88 100644 --- a/tests/integration/test_pdf_export.py +++ b/tests/integration/test_pdf_export.py @@ -230,7 +230,9 @@ class TestPDFDownloadEndpoint: assert response.status_code == 200 assert response.headers["content-type"] == "application/pdf" assert "attachment" in response.headers["content-disposition"] - assert "swot-analysis" in response.headers["content-disposition"] + # New filename format: swot-{company}-{date}.pdf + assert "swot-" in response.headers["content-disposition"] + assert ".pdf" in response.headers["content-disposition"] # Verify it's a valid PDF by reading the stream body_content = b"" @@ -281,7 +283,9 @@ class TestPDFDownloadEndpoint: async def test_download_pdf_filename_format( self, mock_session_id: str, sample_swot_analysis ): - """Test PDF filename follows expected format""" + """Test PDF filename follows expected format: swot-{company}-{date}.pdf""" + from datetime import datetime + from backend.site.router import download_pdf # Create mock request @@ -297,5 +301,10 @@ class TestPDFDownloadEndpoint: assert response.status_code == 200 disposition = response.headers["content-disposition"] - # Format: swot-analysis-{session_id[:8]}.pdf - assert f"swot-analysis-{mock_session_id[:8]}.pdf" in disposition + + # New format: swot-{primary_entity}-vs-{comparison[0]}-{date}.pdf + # Sample has primary_entity="Google", comparison_entities=["Microsoft", "Amazon"] + assert "swot-Google-vs-Microsoft" in disposition + assert "plus1" in disposition # +1 more comparison (Amazon) + assert datetime.now().strftime("%Y-%m-%d") in disposition + assert ".pdf" in disposition