Ali2206 commited on
Commit
f23bd60
·
verified ·
1 Parent(s): 0ccca39

Update endpoints.py

Browse files
Files changed (1) hide show
  1. endpoints.py +277 -164
endpoints.py CHANGED
@@ -75,9 +75,22 @@ def create_router(agent, logger, patients_collection, analysis_collection, users
75
  patient = await patients_collection.find_one({"fhir_id": analysis.get("patient_id")})
76
  if not patient:
77
  continue # Skip if patient no longer exists
78
- analysis["full_name"] = patient.get("full_name", "Unknown")
79
- analysis["_id"] = str(analysis["_id"])
80
- enriched_results.append(analysis)
 
 
 
 
 
 
 
 
 
 
 
 
 
81
 
82
  return enriched_results
83
 
@@ -85,6 +98,68 @@ def create_router(agent, logger, patients_collection, analysis_collection, users
85
  logger.error(f"Error fetching analysis results: {e}")
86
  raise HTTPException(status_code=500, detail="Failed to retrieve analysis results")
87
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  @router.get("/patients/{patient_id}/analysis-reports/pdf")
89
  async def get_patient_analysis_reports_pdf(
90
  patient_id: str = Path(..., description="The ID of the patient"),
@@ -102,97 +177,109 @@ def create_router(agent, logger, patients_collection, analysis_collection, users
102
  if not analyses:
103
  raise HTTPException(status_code=404, detail="No analysis reports found for this patient")
104
 
105
- # Creating LaTeX document
106
- latex_content = r"""
107
- \documentclass[a4paper,12pt]{article}
108
- \usepackage[utf8]{inputenc}
109
- \usepackage[T1]{fontenc}
110
- \usepackage{lmodern}
111
- \usepackage{geometry}
112
- \geometry{margin=1in}
113
- \usepackage{enumitem}
114
- \usepackage{fancyhdr}
115
- \usepackage{lastpage}
116
- \usepackage{datetime}
117
- \pagestyle{fancy}
118
- \fancyhf{}
119
- \rhead{Patient Analysis Report}
120
- \lhead{\today}
121
- \cfoot{Page \thepage\ of \pageref{LastPage}}
122
- \begin{document}
123
- """
124
-
125
- # Adding patient information
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  patient_name = patient.get("full_name", "Unknown")
127
- latex_content += f"""
128
- \\section*{{Analysis Reports for {patient_name} (ID: {patient_id})}}
129
- \\textbf{{Patient Name:}} {patient_name}\\\\
130
- \\textbf{{Patient ID:}} {patient_id}\\\\
131
- \\textbf{{Generated on:}} \\today\\\\
132
- """
133
-
134
- # Adding analysis reports
135
  for idx, analysis in enumerate(analyses, 1):
136
  timestamp = analysis.get("timestamp", datetime.utcnow()).strftime("%Y-%m-%d %H:%M:%S")
137
  suicide_risk = analysis.get("suicide_risk", {})
138
  risk_level = suicide_risk.get("level", "none").capitalize()
139
  risk_score = suicide_risk.get("score", 0.0)
140
- risk_factors = ", ".join(suicide_risk.get("factors", [])) or "None"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141
 
142
- latex_content += f"""
143
- \\subsection*{{Report {idx} - {timestamp}}}
144
- \\begin{{description}}
145
- \\item[Risk Level:] {risk_level}
146
- \\item[Risk Score:] {risk_score:.2f}
147
- \\item[Risk Factors:] {risk_factors}
148
- """
149
-
150
- # Adding additional analysis details if available
151
  if analysis.get("summary"):
152
- latex_content += f" \\item[Summary:] {analysis['summary']}\n"
 
 
 
153
  if analysis.get("recommendations"):
154
- recommendations = ", ".join(analysis["recommendations"]) if isinstance(analysis["recommendations"], list) else analysis["recommendations"]
155
- latex_content += f" \\item[Recommendations:] {recommendations}\n"
156
-
157
- latex_content += r"\end{description}\vspace{0.5cm}"
158
-
159
- latex_content += r"\end{document}"
160
-
161
- # Creating temporary directory for LaTeX compilation
162
- with tempfile.TemporaryDirectory() as tmpdirname:
163
- latex_file = PathLib(tmpdirname) / "report.tex"
164
- pdf_file = PathLib(tmpdirname) / "report.pdf"
165
-
166
- # Writing LaTeX content to file
167
- with open(latex_file, "w", encoding="utf-8") as f:
168
- f.write(latex_content)
169
-
170
- # Compiling LaTeX to PDF using pdflatex
171
- try:
172
- subprocess.run(
173
- ["pdflatex", "-output-directory", tmpdirname, str(latex_file)],
174
- check=True,
175
- stdout=subprocess.PIPE,
176
- stderr=subprocess.PIPE,
177
- text=True
178
- )
179
- except subprocess.CalledProcessError as e:
180
- logger.error(f"LaTeX compilation failed: {e.stderr}")
181
- raise HTTPException(status_code=500, detail="Failed to generate PDF report")
182
-
183
- if not pdf_file.exists():
184
- raise HTTPException(status_code=500, detail="PDF generation failed")
185
-
186
- # Reading the generated PDF
187
- with open(pdf_file, "rb") as f:
188
- pdf_content = f.read()
189
-
190
- # Returning the PDF as a response
191
- return FileResponse(
192
- pdf_file,
193
- media_type="application/pdf",
194
- headers={"Content-Disposition": f"attachment; filename={patient_name.replace(' ', '_')}_{patient_id}_analysis_reports.pdf"}
195
- )
196
 
197
  except HTTPException:
198
  raise
@@ -211,27 +298,58 @@ def create_router(agent, logger, patients_collection, analysis_collection, users
211
  if not patients:
212
  raise HTTPException(status_code=404, detail="No patients found")
213
 
214
- # Creating LaTeX document
215
- latex_content = r"""
216
- \documentclass[a4paper,12pt]{article}
217
- \usepackage[utf8]{inputenc}
218
- \usepackage[T1]{fontenc}
219
- \usepackage{lmodern}
220
- \usepackage{geometry}
221
- \geometry{margin=1in}
222
- \usepackage{enumitem}
223
- \usepackage{fancyhdr}
224
- \usepackage{lastpage}
225
- \usepackage{datetime}
226
- \pagestyle{fancy}
227
- \fancyhf{}
228
- \rhead{All Patients Analysis Reports}
229
- \lhead{\today}
230
- \cfoot{Page \thepage\ of \pageref{LastPage}}
231
- \begin{document}
232
- \section*{Analysis Reports for All Patients}
233
- \textbf{Generated on:} \today\\\\
234
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
235
 
236
  # Flag to track if any analyses exist
237
  has_analyses = False
@@ -248,78 +366,73 @@ def create_router(agent, logger, patients_collection, analysis_collection, users
248
 
249
  has_analyses = True
250
 
251
- # Adding patient section
252
- latex_content += f"""
253
- \\section*{{Patient: {patient_name} (ID: {patient_id})}}
254
- \\textbf{{Patient Name:}} {patient_name}\\\\
255
- \\textbf{{Patient ID:}} {patient_id}\\\\
256
- """
257
 
258
- # Adding analysis reports for the patient
259
  for idx, analysis in enumerate(analyses, 1):
260
  timestamp = analysis.get("timestamp", datetime.utcnow()).strftime("%Y-%m-%d %H:%M:%S")
261
  suicide_risk = analysis.get("suicide_risk", {})
262
  risk_level = suicide_risk.get("level", "none").capitalize()
263
  risk_score = suicide_risk.get("score", 0.0)
264
- risk_factors = ", ".join(suicide_risk.get("factors", [])) or "None"
265
-
266
- latex_content += f"""
267
- \\subsection*{{Report {idx} - {timestamp}}}
268
- \\begin{{description}}
269
- \\item[Risk Level:] {risk_level}
270
- \\item[Risk Score:] {risk_score:.2f}
271
- \\item[Risk Factors:] {risk_factors}
272
- """
273
-
274
- # Adding additional analysis details if available
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
275
  if analysis.get("summary"):
276
- latex_content += f" \\item[Summary:] {analysis['summary']}\n"
 
 
 
277
  if analysis.get("recommendations"):
278
- recommendations = ", ".join(analysis["recommendations"]) if isinstance(analysis["recommendations"], list) else analysis["recommendations"]
279
- latex_content += f" \\item[Recommendations:] {recommendations}\n"
280
-
281
- latex_content += r"\end{description}\vspace{0.5cm}"
 
 
282
 
283
- latex_content += r"\end{document}"
 
284
 
285
  if not has_analyses:
286
  raise HTTPException(status_code=404, detail="No analysis reports found for any patients")
287
 
288
- # Creating temporary directory for LaTeX compilation
289
- with tempfile.TemporaryDirectory() as tmpdirname:
290
- latex_file = PathLib(tmpdirname) / "all_reports.tex"
291
- pdf_file = PathLib(tmpdirname) / "all_reports.pdf"
292
 
293
- # Writing LaTeX content to file
294
- with open(latex_file, "w", encoding="utf-8") as f:
295
- f.write(latex_content)
296
-
297
- # Compiling LaTeX to PDF using pdflatex
298
- try:
299
- subprocess.run(
300
- ["pdflatex", "-output-directory", tmpdirname, str(latex_file)],
301
- check=True,
302
- stdout=subprocess.PIPE,
303
- stderr=subprocess.PIPE,
304
- text=True
305
- )
306
- except subprocess.CalledProcessError as e:
307
- logger.error(f"LaTeX compilation failed: {e.stderr}")
308
- raise HTTPException(status_code=500, detail="Failed to generate PDF report")
309
-
310
- if not pdf_file.exists():
311
- raise HTTPException(status_code=500, detail="PDF generation failed")
312
-
313
- # Reading the generated PDF
314
- with open(pdf_file, "rb") as f:
315
- pdf_content = f.read()
316
-
317
- # Returning the PDF as a response
318
- return FileResponse(
319
- pdf_file,
320
- media_type="application/pdf",
321
- headers={"Content-Disposition": f"attachment; filename=all_patients_analysis_reports_{datetime.utcnow().strftime('%Y%m%d')}.pdf"}
322
- )
323
 
324
  except HTTPException:
325
  raise
 
75
  patient = await patients_collection.find_one({"fhir_id": analysis.get("patient_id")})
76
  if not patient:
77
  continue # Skip if patient no longer exists
78
+
79
+ # Format the response to match the expected structure
80
+ formatted_analysis = {
81
+ "_id": str(analysis["_id"]),
82
+ "patient_id": analysis.get("patient_id"),
83
+ "full_name": patient.get("full_name", "Unknown"),
84
+ "timestamp": analysis.get("timestamp"),
85
+ "suicide_risk": {
86
+ "level": analysis.get("suicide_risk", {}).get("level", "none"),
87
+ "score": analysis.get("suicide_risk", {}).get("score", 0.0),
88
+ "factors": analysis.get("suicide_risk", {}).get("factors", [])
89
+ },
90
+ "summary": analysis.get("summary", ""),
91
+ "recommendations": analysis.get("recommendations", [])
92
+ }
93
+ enriched_results.append(formatted_analysis)
94
 
95
  return enriched_results
96
 
 
98
  logger.error(f"Error fetching analysis results: {e}")
99
  raise HTTPException(status_code=500, detail="Failed to retrieve analysis results")
100
 
101
+ @router.post("/patients/analyze")
102
+ async def analyze_patients(
103
+ current_user: dict = Depends(get_current_user)
104
+ ):
105
+ """Trigger analysis for all patients"""
106
+ logger.info(f"Triggering analysis for all patients by {current_user['email']}")
107
+ try:
108
+ # Get all patients
109
+ patients = await patients_collection.find({}).to_list(length=None)
110
+
111
+ if not patients:
112
+ return {"message": "No patients found to analyze", "analyzed_count": 0}
113
+
114
+ analyzed_count = 0
115
+ for patient in patients:
116
+ try:
117
+ from analysis import analyze_patient
118
+ await analyze_patient(patient)
119
+ analyzed_count += 1
120
+ logger.info(f"✅ Analyzed patient: {patient.get('full_name', 'Unknown')}")
121
+ except Exception as e:
122
+ logger.error(f"❌ Failed to analyze patient {patient.get('full_name', 'Unknown')}: {e}")
123
+ continue
124
+
125
+ return {
126
+ "message": f"Analysis completed for {analyzed_count} patients",
127
+ "analyzed_count": analyzed_count,
128
+ "total_patients": len(patients)
129
+ }
130
+
131
+ except Exception as e:
132
+ logger.error(f"Error triggering analysis: {e}")
133
+ raise HTTPException(status_code=500, detail="Failed to trigger analysis")
134
+
135
+ @router.post("/patients/{patient_id}/analyze")
136
+ async def analyze_specific_patient(
137
+ patient_id: str = Path(..., description="The ID of the patient to analyze"),
138
+ current_user: dict = Depends(get_current_user)
139
+ ):
140
+ """Trigger analysis for a specific patient"""
141
+ logger.info(f"Triggering analysis for patient {patient_id} by {current_user['email']}")
142
+ try:
143
+ # Get the patient
144
+ patient = await patients_collection.find_one({"fhir_id": patient_id})
145
+
146
+ if not patient:
147
+ raise HTTPException(status_code=404, detail="Patient not found")
148
+
149
+ # Analyze the patient
150
+ from analysis import analyze_patient
151
+ await analyze_patient(patient)
152
+
153
+ return {
154
+ "message": f"Analysis completed for patient {patient.get('full_name', 'Unknown')}",
155
+ "patient_id": patient_id,
156
+ "patient_name": patient.get("full_name", "Unknown")
157
+ }
158
+
159
+ except Exception as e:
160
+ logger.error(f"Error analyzing patient {patient_id}: {e}")
161
+ raise HTTPException(status_code=500, detail="Failed to analyze patient")
162
+
163
  @router.get("/patients/{patient_id}/analysis-reports/pdf")
164
  async def get_patient_analysis_reports_pdf(
165
  patient_id: str = Path(..., description="The ID of the patient"),
 
177
  if not analyses:
178
  raise HTTPException(status_code=404, detail="No analysis reports found for this patient")
179
 
180
+ # Generate PDF using ReportLab
181
+ try:
182
+ from reportlab.lib.pagesizes import A4
183
+ from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle
184
+ from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
185
+ from reportlab.lib.units import inch
186
+ from reportlab.lib import colors
187
+ from io import BytesIO
188
+ except ImportError:
189
+ logger.error("ReportLab not available, falling back to text report")
190
+ raise HTTPException(status_code=500, detail="PDF generation not available")
191
+
192
+ logger.info("📄 Generating PDF report using ReportLab")
193
+ buffer = BytesIO()
194
+ doc = SimpleDocTemplate(buffer, pagesize=A4, rightMargin=72, leftMargin=72, topMargin=72, bottomMargin=18)
195
+ story = []
196
+ styles = getSampleStyleSheet()
197
+
198
+ # Custom styles
199
+ title_style = ParagraphStyle(
200
+ 'CustomTitle',
201
+ parent=styles['Heading1'],
202
+ fontSize=18,
203
+ spaceAfter=30,
204
+ alignment=1,
205
+ textColor=colors.darkblue
206
+ )
207
+
208
+ heading_style = ParagraphStyle(
209
+ 'CustomHeading',
210
+ parent=styles['Heading2'],
211
+ fontSize=14,
212
+ spaceAfter=12,
213
+ spaceBefore=20,
214
+ textColor=colors.darkblue
215
+ )
216
+
217
+ normal_style = styles['Normal']
218
+
219
+ # Title
220
  patient_name = patient.get("full_name", "Unknown")
221
+ story.append(Paragraph(f"PATIENT ANALYSIS REPORT", title_style))
222
+ story.append(Paragraph(f"Patient: {patient_name}", normal_style))
223
+ story.append(Paragraph(f"Generated on {datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')}", normal_style))
224
+ story.append(Spacer(1, 20))
225
+
226
+ # Analysis Reports
 
 
227
  for idx, analysis in enumerate(analyses, 1):
228
  timestamp = analysis.get("timestamp", datetime.utcnow()).strftime("%Y-%m-%d %H:%M:%S")
229
  suicide_risk = analysis.get("suicide_risk", {})
230
  risk_level = suicide_risk.get("level", "none").capitalize()
231
  risk_score = suicide_risk.get("score", 0.0)
232
+ risk_factors = suicide_risk.get("factors", [])
233
+
234
+ story.append(Paragraph(f"Report {idx} - {timestamp}", heading_style))
235
+
236
+ # Risk Assessment Table
237
+ risk_data = [
238
+ ["Risk Level", risk_level],
239
+ ["Risk Score", f"{risk_score:.2f}"],
240
+ ["Risk Factors", ", ".join(risk_factors) if risk_factors else "None"]
241
+ ]
242
+
243
+ risk_table = Table(risk_data, colWidths=[2*inch, 4*inch])
244
+ risk_table.setStyle(TableStyle([
245
+ ('BACKGROUND', (0, 0), (0, -1), colors.lightblue),
246
+ ('TEXTCOLOR', (0, 0), (-1, -1), colors.black),
247
+ ('ALIGN', (0, 0), (-1, -1), 'LEFT'),
248
+ ('FONTNAME', (0, 0), (-1, -1), 'Helvetica'),
249
+ ('FONTSIZE', (0, 0), (-1, -1), 10),
250
+ ('BOTTOMPADDING', (0, 0), (-1, -1), 12),
251
+ ('BACKGROUND', (1, 0), (1, -1), colors.beige),
252
+ ('GRID', (0, 0), (-1, -1), 1, colors.black)
253
+ ]))
254
 
255
+ story.append(risk_table)
256
+ story.append(Spacer(1, 12))
257
+
258
+ # Summary and Recommendations
 
 
 
 
 
259
  if analysis.get("summary"):
260
+ story.append(Paragraph("Summary:", heading_style))
261
+ story.append(Paragraph(str(analysis["summary"]), normal_style))
262
+ story.append(Spacer(1, 12))
263
+
264
  if analysis.get("recommendations"):
265
+ recommendations = analysis["recommendations"]
266
+ if isinstance(recommendations, list):
267
+ recommendations = ", ".join(recommendations)
268
+ story.append(Paragraph("Recommendations:", heading_style))
269
+ story.append(Paragraph(str(recommendations), normal_style))
270
+ story.append(Spacer(1, 20))
271
+
272
+ # Build PDF
273
+ doc.build(story)
274
+ pdf_content = buffer.getvalue()
275
+ buffer.close()
276
+
277
+ # Return PDF as response
278
+ return StreamingResponse(
279
+ io.BytesIO(pdf_content),
280
+ media_type="application/pdf",
281
+ headers={"Content-Disposition": f"attachment; filename={patient_name.replace(' ', '_')}_{patient_id}_analysis_reports.pdf"}
282
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
283
 
284
  except HTTPException:
285
  raise
 
298
  if not patients:
299
  raise HTTPException(status_code=404, detail="No patients found")
300
 
301
+ # Generate PDF using ReportLab
302
+ try:
303
+ from reportlab.lib.pagesizes import A4
304
+ from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, PageBreak
305
+ from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
306
+ from reportlab.lib.units import inch
307
+ from reportlab.lib import colors
308
+ from io import BytesIO
309
+ except ImportError:
310
+ logger.error("ReportLab not available, falling back to text report")
311
+ raise HTTPException(status_code=500, detail="PDF generation not available")
312
+
313
+ logger.info("📄 Generating PDF report for all patients using ReportLab")
314
+ buffer = BytesIO()
315
+ doc = SimpleDocTemplate(buffer, pagesize=A4, rightMargin=72, leftMargin=72, topMargin=72, bottomMargin=18)
316
+ story = []
317
+ styles = getSampleStyleSheet()
318
+
319
+ # Custom styles
320
+ title_style = ParagraphStyle(
321
+ 'CustomTitle',
322
+ parent=styles['Heading1'],
323
+ fontSize=20,
324
+ spaceAfter=30,
325
+ alignment=1,
326
+ textColor=colors.darkblue
327
+ )
328
+
329
+ patient_heading_style = ParagraphStyle(
330
+ 'PatientHeading',
331
+ parent=styles['Heading2'],
332
+ fontSize=16,
333
+ spaceAfter=15,
334
+ spaceBefore=25,
335
+ textColor=colors.darkgreen
336
+ )
337
+
338
+ report_heading_style = ParagraphStyle(
339
+ 'ReportHeading',
340
+ parent=styles['Heading3'],
341
+ fontSize=12,
342
+ spaceAfter=10,
343
+ spaceBefore=15,
344
+ textColor=colors.darkblue
345
+ )
346
+
347
+ normal_style = styles['Normal']
348
+
349
+ # Title
350
+ story.append(Paragraph("ALL PATIENTS ANALYSIS REPORTS", title_style))
351
+ story.append(Paragraph(f"Generated on {datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')}", normal_style))
352
+ story.append(Spacer(1, 30))
353
 
354
  # Flag to track if any analyses exist
355
  has_analyses = False
 
366
 
367
  has_analyses = True
368
 
369
+ # Patient section
370
+ story.append(Paragraph(f"Patient: {patient_name}", patient_heading_style))
371
+ story.append(Spacer(1, 10))
 
 
 
372
 
373
+ # Analysis Reports for this patient
374
  for idx, analysis in enumerate(analyses, 1):
375
  timestamp = analysis.get("timestamp", datetime.utcnow()).strftime("%Y-%m-%d %H:%M:%S")
376
  suicide_risk = analysis.get("suicide_risk", {})
377
  risk_level = suicide_risk.get("level", "none").capitalize()
378
  risk_score = suicide_risk.get("score", 0.0)
379
+ risk_factors = suicide_risk.get("factors", [])
380
+
381
+ story.append(Paragraph(f"Report {idx} - {timestamp}", report_heading_style))
382
+
383
+ # Risk Assessment Table
384
+ risk_data = [
385
+ ["Risk Level", risk_level],
386
+ ["Risk Score", f"{risk_score:.2f}"],
387
+ ["Risk Factors", ", ".join(risk_factors) if risk_factors else "None"]
388
+ ]
389
+
390
+ risk_table = Table(risk_data, colWidths=[2*inch, 4*inch])
391
+ risk_table.setStyle(TableStyle([
392
+ ('BACKGROUND', (0, 0), (0, -1), colors.lightblue),
393
+ ('TEXTCOLOR', (0, 0), (-1, -1), colors.black),
394
+ ('ALIGN', (0, 0), (-1, -1), 'LEFT'),
395
+ ('FONTNAME', (0, 0), (-1, -1), 'Helvetica'),
396
+ ('FONTSIZE', (0, 0), (-1, -1), 9),
397
+ ('BOTTOMPADDING', (0, 0), (-1, -1), 8),
398
+ ('BACKGROUND', (1, 0), (1, -1), colors.beige),
399
+ ('GRID', (0, 0), (-1, -1), 1, colors.black)
400
+ ]))
401
+
402
+ story.append(risk_table)
403
+ story.append(Spacer(1, 10))
404
+
405
+ # Summary and Recommendations
406
  if analysis.get("summary"):
407
+ story.append(Paragraph("Summary:", report_heading_style))
408
+ story.append(Paragraph(str(analysis["summary"]), normal_style))
409
+ story.append(Spacer(1, 10))
410
+
411
  if analysis.get("recommendations"):
412
+ recommendations = analysis["recommendations"]
413
+ if isinstance(recommendations, list):
414
+ recommendations = ", ".join(recommendations)
415
+ story.append(Paragraph("Recommendations:", report_heading_style))
416
+ story.append(Paragraph(str(recommendations), normal_style))
417
+ story.append(Spacer(1, 15))
418
 
419
+ # Add page break between patients
420
+ story.append(PageBreak())
421
 
422
  if not has_analyses:
423
  raise HTTPException(status_code=404, detail="No analysis reports found for any patients")
424
 
425
+ # Build PDF
426
+ doc.build(story)
427
+ pdf_content = buffer.getvalue()
428
+ buffer.close()
429
 
430
+ # Return PDF as response
431
+ return StreamingResponse(
432
+ io.BytesIO(pdf_content),
433
+ media_type="application/pdf",
434
+ headers={"Content-Disposition": f"attachment; filename=all_patients_analysis_reports_{datetime.utcnow().strftime('%Y%m%d')}.pdf"}
435
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
436
 
437
  except HTTPException:
438
  raise