Update src/report_generator.py
Browse files- src/report_generator.py +117 -134
src/report_generator.py
CHANGED
|
@@ -198,163 +198,145 @@ def build_medical_report(data: dict) -> bytes:
|
|
| 198 |
logo_path = "src/assets/logo.png"
|
| 199 |
|
| 200 |
# Get logo image
|
| 201 |
-
logo_img = get_reportlab_image(logo_path, width=
|
| 202 |
-
|
| 203 |
-
# Company info
|
| 204 |
-
company_info = Paragraph(
|
| 205 |
-
"""
|
| 206 |
-
<b><font color="#E0B0FF">Daease</font></b><br/>
|
| 207 |
-
Website: <font color="#E0B0FF">http://daease.com/</font><br/>
|
| 208 |
-
Email: <font color="#E0B0FF">daease.main@gmail.com</font>
|
| 209 |
-
""",
|
| 210 |
-
styles['CompanyInfo']
|
| 211 |
-
)
|
| 212 |
|
| 213 |
-
# Add
|
| 214 |
if logo_img:
|
| 215 |
-
|
| 216 |
-
header_table.setStyle(TableStyle([
|
| 217 |
-
('VALIGN', (0, 0), (-1, -1), 'TOP'),
|
| 218 |
-
('ALIGN', (0, 0), (0, 0), 'LEFT'),
|
| 219 |
-
('ALIGN', (1, 0), (1, 0), 'RIGHT'),
|
| 220 |
-
('BOTTOMPADDING', (0, 0), (-1, -1), 10),
|
| 221 |
-
('BACKGROUND', (0, 0), (-1, -1), background_color),
|
| 222 |
-
]))
|
| 223 |
-
elems.append(header_table)
|
| 224 |
-
else:
|
| 225 |
-
company_header = Paragraph(
|
| 226 |
-
"""
|
| 227 |
-
<b><font color="#E0B0FF" size="14">Daease</font></b><br/>
|
| 228 |
-
Website: <font color="#E0B0FF">http://daease.com/</font><br/>
|
| 229 |
-
Email: <font color="#E0B0FF">daease.main@gmail.com</font>
|
| 230 |
-
""",
|
| 231 |
-
styles['CompanyInfo']
|
| 232 |
-
)
|
| 233 |
-
elems.append(company_header)
|
| 234 |
|
|
|
|
| 235 |
elems.append(Spacer(1, 0.3*inch))
|
| 236 |
-
|
| 237 |
-
# Add title with subtle gradient-like effect
|
| 238 |
-
title = Paragraph(
|
| 239 |
-
'<font color="#E0B0FF">Daease</font> '
|
| 240 |
-
'<font color="#D8BFD8">Medical</font> '
|
| 241 |
-
'<font color="#DA70D6">Consultation</font> '
|
| 242 |
-
'<font color="#BA55D3">Report</font>',
|
| 243 |
-
styles['ReportTitle']
|
| 244 |
-
)
|
| 245 |
elems.append(title)
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
# Add a horizontal separator line
|
| 249 |
-
separator = Table([['']], colWidths=[doc.width], rowHeights=[1])
|
| 250 |
-
separator.setStyle(TableStyle([
|
| 251 |
-
('LINEABOVE', (0, 0), (-1, -1), 1, accent_color),
|
| 252 |
-
('BACKGROUND', (0, 0), (-1, -1), background_color),
|
| 253 |
-
]))
|
| 254 |
-
elems.append(separator)
|
| 255 |
elems.append(Spacer(1, 0.3*inch))
|
| 256 |
-
|
| 257 |
-
# --- TABLE 1: Patient Information ---
|
| 258 |
-
patient = data.get('patient', {})
|
| 259 |
|
| 260 |
-
|
|
|
|
|
|
|
|
|
|
| 261 |
patient_data = [
|
| 262 |
-
[Paragraph(
|
| 263 |
-
[
|
| 264 |
-
[
|
| 265 |
-
[
|
| 266 |
-
[Paragraph('Visit Date:', styles['NormalText']), Paragraph(f'{data.get("visit_date", datetime.now().strftime("%Y-%m-%d"))}', styles['NormalText'])]
|
| 267 |
]
|
| 268 |
|
| 269 |
-
patient_table = Table(
|
| 270 |
-
patient_data,
|
| 271 |
-
colWidths=[doc.width * 0.3, doc.width * 0.7],
|
| 272 |
-
rowHeights=[0.4*inch, 0.25*inch, 0.25*inch, 0.25*inch, 0.25*inch]
|
| 273 |
-
)
|
| 274 |
-
|
| 275 |
patient_table.setStyle(TableStyle([
|
| 276 |
-
|
| 277 |
-
('
|
| 278 |
-
('ALIGN', (0, 0), (0,
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
('
|
| 282 |
-
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
|
| 283 |
-
('RIGHTPADDING', (0, 1), (0, -1), 12),
|
| 284 |
-
|
| 285 |
-
# Cell styling
|
| 286 |
('BACKGROUND', (0, 0), (-1, -1), background_color),
|
| 287 |
-
('TEXTCOLOR', (1, 1), (1, -1), text_color),
|
| 288 |
-
|
| 289 |
-
# Borders
|
| 290 |
-
('GRID', (0, 1), (1, -1), 0.5, secondary_color),
|
| 291 |
-
('BOX', (0, 1), (1, -1), 1, accent_color),
|
| 292 |
]))
|
| 293 |
|
| 294 |
elems.append(patient_table)
|
| 295 |
-
elems.append(Spacer(1, 0.4*inch))
|
| 296 |
|
| 297 |
-
#
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
(
|
| 301 |
-
|
| 302 |
-
(
|
| 303 |
-
("Medications", data.get('medications')),
|
| 304 |
-
("Allergies", data.get('allergies')),
|
| 305 |
-
("Examination Findings", data.get('examination')),
|
| 306 |
-
("Diagnosis", data.get('diagnosis')),
|
| 307 |
-
("Recommendations", data.get('recommendations')),
|
| 308 |
-
("Reasoning", data.get('reasoning')),
|
| 309 |
-
("Sources", data.get('sources'))
|
| 310 |
-
]
|
| 311 |
-
|
| 312 |
-
# Create table data
|
| 313 |
-
elems.append(Paragraph('<font color="#BA55D3"><b>Medical Information</b></font>', styles['SectionHeading']))
|
| 314 |
-
elems.append(Spacer(1, 0.1*inch))
|
| 315 |
-
|
| 316 |
-
# Create medical info rows
|
| 317 |
-
for title, content in medical_sections:
|
| 318 |
-
if not content or str(content).strip() == '':
|
| 319 |
-
content = '–'
|
| 320 |
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 326 |
|
| 327 |
-
|
| 328 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 329 |
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 334 |
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 340 |
|
| 341 |
-
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
|
| 346 |
-
|
| 347 |
-
|
| 348 |
-
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
|
| 352 |
-
|
| 353 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 354 |
]))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 355 |
|
| 356 |
-
|
| 357 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 358 |
# Add AI signature section
|
| 359 |
elems.append(Spacer(1, 0.4*inch))
|
| 360 |
|
|
@@ -429,6 +411,7 @@ def generate_and_download_report():
|
|
| 429 |
"""
|
| 430 |
Generate a medical report from the conversation history.
|
| 431 |
Shows a form for entering patient info and then allows downloading the report.
|
|
|
|
| 432 |
"""
|
| 433 |
# Step 1: Collect patient info
|
| 434 |
if st.session_state.report_step == 1:
|
|
|
|
| 198 |
logo_path = "src/assets/logo.png"
|
| 199 |
|
| 200 |
# Get logo image
|
| 201 |
+
logo_img = get_reportlab_image(logo_path, width=2.5*inch, height=1.0*inch)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 202 |
|
| 203 |
+
# Add the logo if available
|
| 204 |
if logo_img:
|
| 205 |
+
elems.append(logo_img)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 206 |
|
| 207 |
+
# Add report title
|
| 208 |
elems.append(Spacer(1, 0.3*inch))
|
| 209 |
+
title = Paragraph('Medical Consultation Report', styles['ReportTitle'])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 210 |
elems.append(title)
|
| 211 |
+
|
| 212 |
+
# Add patient info table
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 213 |
elems.append(Spacer(1, 0.3*inch))
|
|
|
|
|
|
|
|
|
|
| 214 |
|
| 215 |
+
patient_title = Paragraph('Patient Information', styles['SectionHeading'])
|
| 216 |
+
elems.append(patient_title)
|
| 217 |
+
elems.append(Spacer(1, 0.1*inch))
|
| 218 |
+
|
| 219 |
patient_data = [
|
| 220 |
+
['Name:', Paragraph(data["patient"]["name"] or "Not provided", styles['NormalText'])],
|
| 221 |
+
['Age:', Paragraph(data["patient"]["age"] or "Not provided", styles['NormalText'])],
|
| 222 |
+
['Gender:', Paragraph(data["patient"]["gender"] or "Not provided", styles['NormalText'])],
|
| 223 |
+
['Date:', Paragraph(data["visit_date"] or datetime.now().strftime("%Y-%m-%d"), styles['NormalText'])]
|
|
|
|
| 224 |
]
|
| 225 |
|
| 226 |
+
patient_table = Table(patient_data, colWidths=[doc.width * 0.25, doc.width * 0.65])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 227 |
patient_table.setStyle(TableStyle([
|
| 228 |
+
('BACKGROUND', (0, 0), (0, -1), colors.black),
|
| 229 |
+
('TEXTCOLOR', (0, 0), (0, -1), accent_color),
|
| 230 |
+
('ALIGN', (0, 0), (0, -1), 'RIGHT'),
|
| 231 |
+
('FONTNAME', (0, 0), (0, -1), 'Helvetica-Bold'),
|
| 232 |
+
('FONTSIZE', (0, 0), (0, -1), 10),
|
| 233 |
+
('BOTTOMPADDING', (0, 0), (-1, -1), 6),
|
|
|
|
|
|
|
|
|
|
|
|
|
| 234 |
('BACKGROUND', (0, 0), (-1, -1), background_color),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 235 |
]))
|
| 236 |
|
| 237 |
elems.append(patient_table)
|
|
|
|
| 238 |
|
| 239 |
+
# Chief complaint section
|
| 240 |
+
if data.get("chief_complaint"):
|
| 241 |
+
elems.append(Spacer(1, 0.3*inch))
|
| 242 |
+
cc_title = Paragraph('Chief Complaint', styles['SectionHeading'])
|
| 243 |
+
elems.append(cc_title)
|
| 244 |
+
elems.append(Spacer(1, 0.1*inch))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 245 |
|
| 246 |
+
cc_text = Paragraph(data["chief_complaint"], styles['NormalText'])
|
| 247 |
+
elems.append(cc_text)
|
| 248 |
+
|
| 249 |
+
# History of present illness
|
| 250 |
+
if data.get("history_of_present_illness"):
|
| 251 |
+
elems.append(Spacer(1, 0.3*inch))
|
| 252 |
+
hpi_title = Paragraph('History of Present Illness', styles['SectionHeading'])
|
| 253 |
+
elems.append(hpi_title)
|
| 254 |
+
elems.append(Spacer(1, 0.1*inch))
|
| 255 |
|
| 256 |
+
hpi_text = Paragraph(data["history_of_present_illness"], styles['NormalText'])
|
| 257 |
+
elems.append(hpi_text)
|
| 258 |
+
|
| 259 |
+
# Past medical history
|
| 260 |
+
if data.get("past_medical_history"):
|
| 261 |
+
elems.append(Spacer(1, 0.3*inch))
|
| 262 |
+
pmh_title = Paragraph('Past Medical History', styles['SectionHeading'])
|
| 263 |
+
elems.append(pmh_title)
|
| 264 |
+
elems.append(Spacer(1, 0.1*inch))
|
| 265 |
|
| 266 |
+
pmh_text = Paragraph(data["past_medical_history"], styles['NormalText'])
|
| 267 |
+
elems.append(pmh_text)
|
| 268 |
+
|
| 269 |
+
# Medications
|
| 270 |
+
if data.get("medications"):
|
| 271 |
+
elems.append(Spacer(1, 0.3*inch))
|
| 272 |
+
med_title = Paragraph('Medications', styles['SectionHeading'])
|
| 273 |
+
elems.append(med_title)
|
| 274 |
+
elems.append(Spacer(1, 0.1*inch))
|
| 275 |
|
| 276 |
+
med_text = Paragraph(data["medications"], styles['NormalText'])
|
| 277 |
+
elems.append(med_text)
|
| 278 |
+
|
| 279 |
+
# Allergies
|
| 280 |
+
if data.get("allergies"):
|
| 281 |
+
elems.append(Spacer(1, 0.3*inch))
|
| 282 |
+
allergy_title = Paragraph('Allergies', styles['SectionHeading'])
|
| 283 |
+
elems.append(allergy_title)
|
| 284 |
+
elems.append(Spacer(1, 0.1*inch))
|
| 285 |
|
| 286 |
+
allergy_text = Paragraph(data["allergies"], styles['NormalText'])
|
| 287 |
+
elems.append(allergy_text)
|
| 288 |
+
|
| 289 |
+
# Examination
|
| 290 |
+
if data.get("examination"):
|
| 291 |
+
elems.append(Spacer(1, 0.3*inch))
|
| 292 |
+
exam_title = Paragraph('Examination', styles['SectionHeading'])
|
| 293 |
+
elems.append(exam_title)
|
| 294 |
+
elems.append(Spacer(1, 0.1*inch))
|
| 295 |
+
|
| 296 |
+
exam_text = Paragraph(data["examination"], styles['NormalText'])
|
| 297 |
+
elems.append(exam_text)
|
| 298 |
+
|
| 299 |
+
# Diagnosis (highlighted)
|
| 300 |
+
if data.get("diagnosis"):
|
| 301 |
+
elems.append(Spacer(1, 0.3*inch))
|
| 302 |
+
diag_title = Paragraph('Diagnosis', styles['SectionHeading'])
|
| 303 |
+
elems.append(diag_title)
|
| 304 |
+
elems.append(Spacer(1, 0.1*inch))
|
| 305 |
+
|
| 306 |
+
# Create a table for the diagnosis with a subtle highlight
|
| 307 |
+
diag_table = Table([[Paragraph(data["diagnosis"], styles['NormalText'])]],
|
| 308 |
+
colWidths=[doc.width * 0.9])
|
| 309 |
+
diag_table.setStyle(TableStyle([
|
| 310 |
+
('BACKGROUND', (0, 0), (-1, -1), colors.darkslateblue),
|
| 311 |
+
('TEXTCOLOR', (0, 0), (-1, -1), colors.white),
|
| 312 |
+
('ALIGN', (0, 0), (-1, -1), 'LEFT'),
|
| 313 |
+
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
|
| 314 |
+
('PADDING', (0, 0), (-1, -1), 8),
|
| 315 |
+
('BOTTOMPADDING', (0, 0), (-1, -1), 12),
|
| 316 |
+
('TOPPADDING', (0, 0), (-1, -1), 12),
|
| 317 |
]))
|
| 318 |
+
elems.append(diag_table)
|
| 319 |
+
|
| 320 |
+
# Recommendations
|
| 321 |
+
if data.get("recommendations"):
|
| 322 |
+
elems.append(Spacer(1, 0.3*inch))
|
| 323 |
+
rec_title = Paragraph('Recommendations', styles['SectionHeading'])
|
| 324 |
+
elems.append(rec_title)
|
| 325 |
+
elems.append(Spacer(1, 0.1*inch))
|
| 326 |
|
| 327 |
+
rec_text = Paragraph(data["recommendations"], styles['NormalText'])
|
| 328 |
+
elems.append(rec_text)
|
| 329 |
+
|
| 330 |
+
# Sources (if provided)
|
| 331 |
+
if data.get("sources") and len(data["sources"]) > 5: # Simple check to avoid empty sources
|
| 332 |
+
elems.append(Spacer(1, 0.3*inch))
|
| 333 |
+
sources_title = Paragraph('Medical References', styles['SectionHeading'])
|
| 334 |
+
elems.append(sources_title)
|
| 335 |
+
elems.append(Spacer(1, 0.1*inch))
|
| 336 |
+
|
| 337 |
+
sources_text = Paragraph(data["sources"], styles['NormalText'])
|
| 338 |
+
elems.append(sources_text)
|
| 339 |
+
|
| 340 |
# Add AI signature section
|
| 341 |
elems.append(Spacer(1, 0.4*inch))
|
| 342 |
|
|
|
|
| 411 |
"""
|
| 412 |
Generate a medical report from the conversation history.
|
| 413 |
Shows a form for entering patient info and then allows downloading the report.
|
| 414 |
+
This function is only called when explicitly requested via the Generate Report button.
|
| 415 |
"""
|
| 416 |
# Step 1: Collect patient info
|
| 417 |
if st.session_state.report_step == 1:
|