Spaces:
Running
Running
| import pandas as pd | |
| from reportlab.lib.pagesizes import letter | |
| from reportlab.lib import colors | |
| from reportlab.lib.units import inch | |
| from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer, Image, PageBreak | |
| from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle | |
| from reportlab.lib.enums import TA_CENTER, TA_LEFT | |
| from datetime import datetime | |
| import random | |
| import os | |
| reports_df = pd.read_csv('indiana_reports.csv') | |
| projections_df = pd.read_csv('indiana_projections.csv') | |
| def header_footer(canvas, doc): | |
| canvas.saveState() | |
| canvas.setFillColor(colors.HexColor('#1a365d')) | |
| canvas.setFont('Helvetica-Bold', 14) | |
| canvas.drawString(0.75*inch, letter[1] - 0.5*inch, "ST. LUKE'S MEDICAL CENTER") | |
| canvas.setFont('Helvetica', 9) | |
| canvas.setFillColor(colors.HexColor('#4a5568')) | |
| canvas.drawString(0.75*inch, letter[1] - 0.65*inch, "Global City • We love life") | |
| canvas.setFont('Helvetica-Bold', 11) | |
| canvas.setFillColor(colors.HexColor('#2b6cb0')) | |
| canvas.drawRightString(letter[0] - 0.75*inch, letter[1] - 0.5*inch, "DIAGNOSTIC X-RAY") | |
| canvas.setStrokeColor(colors.HexColor('#e2e8f0')) | |
| canvas.setLineWidth(0.5) | |
| canvas.line(0.75*inch, letter[1] - 0.75*inch, letter[0] - 0.75*inch, letter[1] - 0.75*inch) | |
| canvas.setFont('Helvetica', 8) | |
| canvas.setFillColor(colors.HexColor('#718096')) | |
| canvas.drawCentredString(letter[0]/2, 0.5*inch, | |
| f"Page {doc.page} of 2 | Validated: {datetime.now().strftime('%d-%b-%Y %I:%M %p')}") | |
| canvas.drawCentredString(letter[0]/2, 0.35*inch, | |
| "This report has been electronically signed and validated. No signature is required.") | |
| canvas.restoreState() | |
| def create_radiology_report(uid, report_row, output_filename, report_num): | |
| doc = SimpleDocTemplate(output_filename, pagesize=letter, rightMargin=0.75*inch, leftMargin=0.75*inch, | |
| topMargin=1*inch, bottomMargin=0.8*inch) | |
| story = [] | |
| styles = getSampleStyleSheet() | |
| section_style = ParagraphStyle( | |
| 'Section', | |
| parent=styles['Heading2'], | |
| fontSize=11, | |
| textColor=colors.HexColor('#1a365d'), | |
| spaceAfter=6, | |
| spaceBefore=10, | |
| fontName='Helvetica-Bold' | |
| ) | |
| body_style = ParagraphStyle( | |
| 'Body', | |
| parent=styles['Normal'], | |
| fontSize=10, | |
| leading=14, | |
| textColor=colors.HexColor('#2d3748'), | |
| alignment=TA_LEFT | |
| ) | |
| patient_id = f"{random.randint(1000000000, 9999999999)}" | |
| exam_date = datetime.now().strftime("%d-%b-%Y") | |
| exam_time = datetime.now().strftime("%I:%M %p") | |
| dob = f"{random.randint(1,28)}-{random.choice(['JAN','FEB','MAR','APR','MAY','JUN','JUL','AUG','SEP'])}-{random.randint(1950,2000)}" | |
| age = random.randint(25,85) | |
| gender = random.choice(['M', 'F']) | |
| print(f"\n{'='*70}") | |
| print(f" REPORT #{report_num} - UID: {uid}") | |
| print(f"{'='*70}") | |
| print(f" Patient ID: {patient_id}") | |
| print(f" Exam Date: {exam_date} {exam_time}") | |
| print(f" Age/Gender: {age}Y / {gender}") | |
| projection_rows_list = projections_df[projections_df['uid'] == uid] | |
| print(f" Images Expected: {len(projection_rows_list)}") | |
| images_found_count = 0 | |
| for idx, proj_row in projection_rows_list.iterrows(): | |
| filename = proj_row['filename'] | |
| image_path = f"images/{filename}" | |
| if os.path.exists(image_path): | |
| exists = "✓" | |
| images_found_count += 1 | |
| else: | |
| exists = "✗" | |
| print(f" {exists} {filename}") | |
| indication = report_row.get('indication', '') | |
| findings = report_row.get('findings', '') | |
| impression = report_row.get('impression', '') | |
| print(f"\n History: {str(indication)[:60]}..." if len(str(indication)) > 60 else f"\n History: {indication}") | |
| print(f" Findings: {str(findings)[:60]}..." if len(str(findings)) > 60 else f" Findings: {findings}") | |
| print(f" Impression: {str(impression)[:60]}..." if len(str(impression)) > 60 else f" Impression: {impression}") | |
| patient_name = random.choice(['DE LEON, Debbie Dichoco', 'SANTOS, Maria Clara', 'CRUZ, Juan Pedro', 'SOLOMON, Jannica Mica Estolano']) | |
| referring_physician = random.choice(['ANG, SAMUEL DEE M.D.', 'REYES, MARIA SANTOS M.D.', 'CRUZ, JOSE ANTONIO M.D.']) | |
| patient_info = [ | |
| ['', '', Paragraph(f"<b>DATE OF BIRTH:</b>", body_style), Paragraph(dob, body_style), Paragraph(f"<b>AGE:</b>", body_style), Paragraph(f"{age} year/s", body_style)], | |
| [Paragraph(f"<b>{patient_id}</b>", body_style), '', '', '', Paragraph(f"<b>GENDER:</b>", body_style), Paragraph(gender, body_style)], | |
| [Paragraph(f"<b>{patient_name}</b>", body_style), '', '', '', '', ''], | |
| [Paragraph(f"<b>ROOM/BED:</b>", body_style), Paragraph("OutPatient", body_style), | |
| Paragraph(f"<b>REFERRING<br/>PHYSICIAN:</b>", body_style), Paragraph(referring_physician, body_style), | |
| Paragraph(f"<b>DATE/TIME OF<br/>EXAM:</b>", body_style), Paragraph(f"{exam_date}<br/>{exam_time}", body_style)], | |
| ['', '', '', '', Paragraph(f"<b>DATE/TIME OF<br/>REQUEST:</b>", body_style), Paragraph(f"{exam_date}<br/>{exam_time}", body_style)] | |
| ] | |
| patient_table = Table(patient_info, colWidths=[1.3*inch, 1.2*inch, 1.2*inch, 1.4*inch, 1.2*inch, 1.2*inch]) | |
| patient_table.setStyle(TableStyle([ | |
| ('VALIGN', (0, 0), (-1, -1), 'TOP'), | |
| ('ALIGN', (0, 0), (-1, -1), 'LEFT'), | |
| ('BOTTOMPADDING', (0, 0), (-1, -1), 3), | |
| ('TOPPADDING', (0, 0), (-1, -1), 3), | |
| ('LEFTPADDING', (0, 0), (-1, -1), 0), | |
| ('RIGHTPADDING', (0, 0), (-1, -1), 5), | |
| ])) | |
| story.append(patient_table) | |
| story.append(Spacer(1, 0.25*inch)) | |
| story.append(Paragraph("EXAMINATION:", section_style)) | |
| story.append(Paragraph("CHEST", body_style)) | |
| story.append(Spacer(1, 0.1*inch)) | |
| if pd.notna(indication) and str(indication).strip() and str(indication).lower() not in ['none', 'nan', '']: | |
| story.append(Paragraph("HISTORY:", section_style)) | |
| clean_indication = str(indication).replace('XXXX', '[redacted]') | |
| story.append(Paragraph(clean_indication, body_style)) | |
| story.append(Spacer(1, 0.1*inch)) | |
| comparison = report_row.get('comparison', '') | |
| if pd.notna(comparison) and str(comparison).strip() and str(comparison).lower() not in ['none', 'none.', 'nan', '']: | |
| story.append(Paragraph("COMPARISON:", section_style)) | |
| clean_comparison = str(comparison).replace('XXXX', '[redacted]') | |
| story.append(Paragraph(clean_comparison, body_style)) | |
| story.append(Spacer(1, 0.1*inch)) | |
| if len(projection_rows_list) > 0: | |
| techniques = ", ".join([row['projection'] for _, row in projection_rows_list.iterrows()]) | |
| story.append(Paragraph("TECHNIQUE:", section_style)) | |
| story.append(Paragraph(f"{techniques} view", body_style)) | |
| story.append(Spacer(1, 0.1*inch)) | |
| story.append(Paragraph("FINDINGS:", section_style)) | |
| if pd.notna(findings) and str(findings).strip() and str(findings).lower() != 'nan': | |
| clean_findings = str(findings).replace('XXXX', '[redacted]') | |
| story.append(Paragraph(clean_findings, body_style)) | |
| else: | |
| story.append(Paragraph("No significant findings.", body_style)) | |
| story.append(Spacer(1, 0.1*inch)) | |
| story.append(Paragraph("IMPRESSION:", section_style)) | |
| if pd.notna(impression) and str(impression).strip() and str(impression).lower() != 'nan': | |
| clean_impression = str(impression).replace('XXXX', '[redacted]') | |
| story.append(Paragraph(clean_impression, body_style)) | |
| else: | |
| story.append(Paragraph("Normal chest radiograph.", body_style)) | |
| story.append(Spacer(1, 0.2*inch)) | |
| approval_data = [ | |
| [Paragraph("<b>Approved By:</b>", body_style), ''] | |
| ] | |
| signature_path = "images/signature.jpg" | |
| radiologist_name = random.choice(['JOSE ANTONIO CRUZ', 'NERELLA KRISHNA TEJA', 'MARIA ELENA SANTOS', 'MICHAEL VINCENTO CORSINO']) | |
| if os.path.exists(signature_path): | |
| sig_img = Image(signature_path, width=1.2*inch, height=0.4*inch) | |
| approval_data.append([sig_img, '']) | |
| approval_data.append([Paragraph(f"<b>{radiologist_name}, M.D. (Radiologist)</b>", body_style), '']) | |
| approval_data.append([Paragraph(f"MBBS, MD RADIO DIAGNOSIS | REG NO. APMC/FMR/{random.randint(90000, 99999)}", | |
| ParagraphStyle('Small', parent=body_style, fontSize=8)), '']) | |
| approval_table = Table(approval_data, colWidths=[3*inch, 4*inch]) | |
| approval_table.setStyle(TableStyle([ | |
| ('VALIGN', (0, 0), (-1, -1), 'TOP'), | |
| ('ALIGN', (0, 0), (0, -1), 'LEFT'), | |
| ('BOTTOMPADDING', (0, 0), (-1, -1), 2), | |
| ('TOPPADDING', (0, 0), (-1, -1), 2), | |
| ])) | |
| story.append(approval_table) | |
| story.append(PageBreak()) | |
| if len(projection_rows_list) > 0: | |
| images_found = [] | |
| for _, proj_row in projection_rows_list.iterrows(): | |
| filename = proj_row['filename'] | |
| image_path = f"images/{filename}" | |
| if os.path.exists(image_path): | |
| try: | |
| projection_label = proj_row.get('projection', 'View') | |
| images_found.append((image_path, projection_label)) | |
| except Exception as e: | |
| print(f" ✗ Error checking image {image_path}: {e}") | |
| print(f"\n Images Embedded: {len(images_found)}") | |
| if images_found: | |
| image_table_data = [] | |
| for i in range(0, len(images_found), 2): | |
| row_images = [] | |
| for j in range(2): | |
| if i + j < len(images_found): | |
| img_path, label = images_found[i + j] | |
| try: | |
| img = Image(img_path, width=3*inch, height=3*inch) | |
| caption = Paragraph(f"<b>{label} View</b>", | |
| ParagraphStyle('Caption', parent=body_style, alignment=TA_CENTER, fontSize=10)) | |
| cell = Table([[img], [caption]], colWidths=[3*inch]) | |
| cell.setStyle(TableStyle([ | |
| ('ALIGN', (0, 0), (-1, -1), 'CENTER'), | |
| ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), | |
| ])) | |
| row_images.append(cell) | |
| except Exception as e: | |
| print(f" ✗ Could not load image {img_path}: {e}") | |
| row_images.append('') | |
| else: | |
| row_images.append('') | |
| if row_images: | |
| image_table_data.append(row_images) | |
| if image_table_data: | |
| image_table = Table(image_table_data, colWidths=[3.3*inch, 3.3*inch]) | |
| image_table.setStyle(TableStyle([ | |
| ('ALIGN', (0, 0), (-1, -1), 'CENTER'), | |
| ('VALIGN', (0, 0), (-1, -1), 'TOP'), | |
| ('LEFTPADDING', (0, 0), (-1, -1), 10), | |
| ('RIGHTPADDING', (0, 0), (-1, -1), 10), | |
| ('TOPPADDING', (0, 0), (-1, -1), 15), | |
| ('BOTTOMPADDING', (0, 0), (-1, -1), 15), | |
| ])) | |
| story.append(image_table) | |
| doc.build(story, onFirstPage=header_footer, onLaterPages=header_footer) | |
| print(f"\n ✓ PDF Created: {output_filename}") | |
| print(f"{'='*70}\n") | |
| output_dir = "generated_reports" | |
| os.makedirs(output_dir, exist_ok=True) | |
| sample_reports = reports_df.head(5) | |
| print("\n" + "="*70) | |
| print(" RADIOLOGY REPORT GENERATOR") | |
| print("="*70) | |
| print(f" Total Reports: {len(sample_reports)}") | |
| print(f" Output Directory: {output_dir}/") | |
| print("="*70) | |
| for idx, (_, report_row) in enumerate(sample_reports.iterrows(), 1): | |
| uid = report_row['uid'] | |
| output_filename = f"{output_dir}/radiology_report_{idx}.pdf" | |
| create_radiology_report(uid, report_row, output_filename, idx) | |
| print("\n" + "="*70) | |
| print(f" ✓ ALL REPORTS GENERATED SUCCESSFULLY!") | |
| print(f" ✓ Location: {output_dir}/") | |
| print("="*70 + "\n") | |