Make a finding report for pain in knee shown in mri report
| Sequence | Plane | Purpose |
|---|---|---|
| T2-weighted / PD Fat-Saturated | Sagittal | ACL, PCL, meniscal horns, patellar tendon |
| T2-weighted / PD Fat-Saturated | Coronal | MCL, LCL, meniscal body, cartilage |
| T2-weighted / PD Fat-Saturated | Axial | Patellofemoral joint, muscle compartments, joint fluid |
Note: The following observations are based on visual analysis of photographed MRI film sheets. Definitive diagnosis requires formal radiologist review of original DICOM files.
| Structure | Suggested Clinical Test |
|---|---|
| ACL/PCL | Lachman test, Anterior/Posterior Drawer test |
| Meniscus | McMurray test, Thessaly test |
| MCL/LCL | Valgus/Varus stress test |
| Patellofemoral | Patellar grind / apprehension test |
Generate a professional PDF report I can share with my orthopedic surgeon
ls -la /tmp/workspace/ 2>/dev/null || echo "workspace empty or missing"
mkdir -p /tmp/workspace/knee-mri-report && python3 << 'EOF' from reportlab.lib.pagesizes import A4 from reportlab.lib import colors from reportlab.lib.units import mm, cm from reportlab.platypus import (SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, HRFlowable, KeepTogether) from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHT, TA_JUSTIFY from reportlab.platypus import Image as RLImage from reportlab.pdfgen import canvas from reportlab.lib.colors import HexColor import urllib.request import os from datetime import datetime # Download both MRI images img1_url = "https://d361swluvexzqs.cloudfront.net/chats-attachments/130075-1784634767651-was358.jpg" img2_url = "https://d361swluvexzqs.cloudfront.net/chats-attachments/130074-1784634774013-67mcig.jpg" img1_path = "/tmp/workspace/knee-mri-report/mri1.jpg" img2_path = "/tmp/workspace/knee-mri-report/mri2.jpg" print("Downloading MRI images...") urllib.request.urlretrieve(img1_url, img1_path) urllib.request.urlretrieve(img2_url, img2_path) print("Images downloaded.") output_path = "/tmp/workspace/knee-mri-report/Harshit_Raj_Knee_MRI_Report.pdf" # Color palette DARK_BLUE = HexColor("#1A3A5C") MID_BLUE = HexColor("#2E6DA4") LIGHT_BLUE = HexColor("#D6E8F7") ACCENT_GOLD = HexColor("#C9A54A") LIGHT_GRAY = HexColor("#F5F5F5") MED_GRAY = HexColor("#CCCCCC") DARK_GRAY = HexColor("#444444") RED_WARN = HexColor("#C0392B") doc = SimpleDocTemplate( output_path, pagesize=A4, rightMargin=18*mm, leftMargin=18*mm, topMargin=22*mm, bottomMargin=20*mm, title="MRI Knee Finding Report - Harshit Raj", author="Orris AI Imaging Analysis", subject="MRI Knee Report", ) W, H = A4 styles = getSampleStyleSheet() # Custom styles def mk_style(name, parent='Normal', **kw): s = ParagraphStyle(name=name, parent=styles[parent], **kw) return s h1 = mk_style('H1', fontSize=20, textColor=DARK_BLUE, spaceAfter=2, spaceBefore=4, fontName='Helvetica-Bold', alignment=TA_CENTER) h2 = mk_style('H2', fontSize=13, textColor=DARK_BLUE, spaceAfter=3, spaceBefore=8, fontName='Helvetica-Bold', borderPad=2) h3 = mk_style('H3', fontSize=10.5, textColor=MID_BLUE, spaceAfter=2, spaceBefore=5, fontName='Helvetica-Bold') body = mk_style('Body', fontSize=9.5, textColor=DARK_GRAY, spaceAfter=3, spaceBefore=1, leading=14, alignment=TA_JUSTIFY) small = mk_style('Small', fontSize=8.5, textColor=colors.grey, spaceAfter=2, fontName='Helvetica', leading=11) warn = mk_style('Warn', fontSize=8.5, textColor=RED_WARN, spaceAfter=2, fontName='Helvetica-Oblique', leading=12) subtitle = mk_style('Subtitle', fontSize=11, textColor=MID_BLUE, alignment=TA_CENTER, spaceAfter=2, fontName='Helvetica') center_bold = mk_style('CenterBold', fontSize=9.5, textColor=DARK_BLUE, alignment=TA_CENTER, fontName='Helvetica-Bold') label = mk_style('Label', fontSize=9, textColor=DARK_GRAY, fontName='Helvetica-Bold') value = mk_style('Value', fontSize=9, textColor=DARK_GRAY, fontName='Helvetica') disclaimer = mk_style('Disclaimer', fontSize=8, textColor=colors.grey, leading=11, alignment=TA_JUSTIFY, fontName='Helvetica-Oblique') story = [] # ── HEADER BLOCK ───────────────────────────────────────────────────────────── header_data = [[ Paragraph("MRI KNEE JOINT", h1), Paragraph("IMAGING FINDING REPORT", subtitle), ]] header_table = Table([[ Paragraph("MRI KNEE JOINT<br/><font size=11 color='#2E6DA4'>IMAGING FINDING REPORT</font>", h1) ]], colWidths=[W - 36*mm]) header_table.setStyle(TableStyle([ ('BACKGROUND', (0,0), (-1,-1), DARK_BLUE), ('TOPPADDING', (0,0), (-1,-1), 10), ('BOTTOMPADDING', (0,0), (-1,-1), 10), ('LEFTPADDING', (0,0), (-1,-1), 14), ('RIGHTPADDING', (0,0), (-1,-1), 14), ('ALIGN', (0,0), (-1,-1), 'CENTER'), ])) # Override text colour for the header header_p = Paragraph( "<font color='white' size=20><b>MRI KNEE JOINT</b></font><br/>" "<font color='#D6E8F7' size=11>IMAGING FINDING REPORT</font>", mk_style('HeaderText', fontSize=20, alignment=TA_CENTER, leading=26) ) header_table = Table([[header_p]], colWidths=[W - 36*mm]) header_table.setStyle(TableStyle([ ('BACKGROUND', (0,0), (-1,-1), DARK_BLUE), ('TOPPADDING', (0,0), (-1,-1), 12), ('BOTTOMPADDING', (0,0), (-1,-1), 12), ('LEFTPADDING', (0,0), (-1,-1), 0), ('ALIGN', (0,0), (-1,-1), 'CENTER'), ('VALIGN', (0,0), (-1,-1), 'MIDDLE'), ])) story.append(header_table) story.append(Spacer(1, 6*mm)) # ── PATIENT INFO TABLE ──────────────────────────────────────────────────────── pi_data = [ [Paragraph("<b>Patient Name:</b>", label), Paragraph("Harshit Raj", value), Paragraph("<b>Patient ID:</b>", label), Paragraph("2607210830", value)], [Paragraph("<b>Sex:</b>", label), Paragraph("Male", value), Paragraph("<b>Date of Birth:</b>", label), Paragraph("Not Provided", value)], [Paragraph("<b>Date of Scan:</b>", label), Paragraph("July 21, 2020", value), Paragraph("<b>Report Date:</b>", label), Paragraph(datetime.now().strftime("%B %d, %Y"), value)], [Paragraph("<b>Facility:</b>", label), Paragraph("DHH Baripada / Spandan Diagnostics", value), Paragraph("<b>Accession No.:</b>", label), Paragraph("IPD", value)], [Paragraph("<b>Referring Dept.:</b>", label),Paragraph("Orthopaedics", value), Paragraph("<b>Modality:</b>", label), Paragraph("MRI (1.5T / 3T)", value)], ] col_w = [(W - 36*mm) * f for f in [0.19, 0.31, 0.19, 0.31]] pi_table = Table(pi_data, colWidths=col_w) pi_table.setStyle(TableStyle([ ('BACKGROUND', (0,0), (-1,-1), LIGHT_GRAY), ('BACKGROUND', (0,0), (-1,0), LIGHT_BLUE), ('ROWBACKGROUNDS', (0,0), (-1,-1), [LIGHT_GRAY, colors.white]), ('GRID', (0,0), (-1,-1), 0.4, MED_GRAY), ('LEFTPADDING', (0,0), (-1,-1), 7), ('RIGHTPADDING', (0,0), (-1,-1), 7), ('TOPPADDING', (0,0), (-1,-1), 5), ('BOTTOMPADDING',(0,0), (-1,-1), 5), ('VALIGN', (0,0), (-1,-1), 'MIDDLE'), ])) story.append(pi_table) story.append(Spacer(1, 5*mm)) # ── SEQUENCES TABLE ─────────────────────────────────────────────────────────── story.append(Paragraph("SEQUENCES & VIEWS OBTAINED", h2)) story.append(HRFlowable(width="100%", thickness=1.5, color=MID_BLUE, spaceAfter=4)) seq_data = [ [Paragraph("<b>Sequence</b>", center_bold), Paragraph("<b>Plane</b>", center_bold), Paragraph("<b>Primary Diagnostic Purpose</b>", center_bold)], ["T2-W / PD Fat-Saturated", "Sagittal", "ACL, PCL, patellar tendon, anterior & posterior meniscal horns"], ["T2-W / PD Fat-Saturated", "Coronal", "MCL, LCL, meniscal body, articular cartilage, joint space"], ["T2-W / PD Fat-Saturated", "Axial", "Patellofemoral joint, muscle compartments, joint effusion"], ] seq_col_w = [(W - 36*mm) * f for f in [0.28, 0.18, 0.54]] seq_table = Table(seq_data, colWidths=seq_col_w) seq_table.setStyle(TableStyle([ ('BACKGROUND', (0,0), (-1,0), DARK_BLUE), ('TEXTCOLOR', (0,0), (-1,0), colors.white), ('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.white, LIGHT_GRAY]), ('GRID', (0,0), (-1,-1), 0.4, MED_GRAY), ('FONTSIZE', (0,1), (-1,-1), 9), ('LEFTPADDING', (0,0), (-1,-1), 8), ('RIGHTPADDING', (0,0), (-1,-1), 8), ('TOPPADDING', (0,0), (-1,-1), 5), ('BOTTOMPADDING',(0,0), (-1,-1), 5), ('VALIGN', (0,0), (-1,-1), 'MIDDLE'), ('ALIGN', (0,0), (-1,0), 'CENTER'), ])) story.append(seq_table) story.append(Spacer(1, 5*mm)) # ── IMAGING OBSERVATIONS ───────────────────────────────────────────────────── story.append(Paragraph("IMAGING OBSERVATIONS", h2)) story.append(HRFlowable(width="100%", thickness=1.5, color=MID_BLUE, spaceAfter=4)) obs_sections = [ ("1. Bony Structures", "The femoral condyles, tibial plateau, and patella appear grossly intact across the visible sagittal and axial " "sequences. No acute fracture lines, bony avulsion fragments, or frank cortical erosions are identifiable at the " "resolution of the available study. Femoral articular margins are visible in the sagittal series without obvious " "subchondral cystic change."), ("2. Ligaments", "Sagittal views allow evaluation of the ACL and PCL pathways. Coronal views demonstrate the medial and lateral " "joint compartments. Definitive continuity or discontinuity of individual ligament fiber bundles cannot be " "confirmed from photographic film resolution; however, no gross avulsion bony fragment or large signal void " "indicating complete rupture was immediately apparent. Formal DICOM review is required for grading."), ("3. Menisci", "The medial and lateral menisci are partially visualized on sagittal and coronal sequences. Subtle intra-substance " "signal changes indicative of tear or degeneration cannot be reliably excluded at this resolution. No gross " "displaced meniscal fragment is overtly visible. A formal radiologist review of DICOM data is mandatory to " "classify any meniscal pathology (Grade I-III)."), ("4. Joint Effusion", "The axial sequence demonstrates the patellofemoral joint and tibiofemoral joint space from the distal thigh through " "the proximal calf. T2-hyperintense (bright white) signal is noted in the suprapatellar and/or popliteal regions, " "consistent with MILD TO MODERATE JOINT EFFUSION. This is a common finding in inflammatory synovitis, post-traumatic " "conditions, ligament strain, or early degenerative joint disease - and directly corresponds to the patient's " "presenting complaint of knee pain."), ("5. Articular Cartilage", "Articular cartilage thickness and focal defects cannot be reliably graded from the photographed film. No grossly " "visible full-thickness cartilage loss was apparent on available images. ICRS cartilage grading should be performed " "by a radiologist on high-resolution DICOM sequences."), ("6. Surrounding Soft Tissues", "No large soft-tissue mass, focal abscess, or Baker's cyst is overtly visible. The musculature of the quadriceps, " "hamstrings, and calf compartments appears grossly preserved in volume and signal intensity. The iliotibial band " "region and popliteal fossa are not obviously abnormal at this resolution."), ] for title_text, body_text in obs_sections: story.append(KeepTogether([ Paragraph(title_text, h3), Paragraph(body_text, body), Spacer(1, 2*mm), ])) story.append(Spacer(1, 3*mm)) # ── IMPRESSION ──────────────────────────────────────────────────────────────── story.append(Paragraph("IMPRESSION (PRELIMINARY)", h2)) story.append(HRFlowable(width="100%", thickness=1.5, color=ACCENT_GOLD, spaceAfter=4)) imp_data = [ [Paragraph("<b>#</b>", center_bold), Paragraph("<b>Finding</b>", center_bold), Paragraph("<b>Clinical Correlation</b>", center_bold)], ["1", "Mild to moderate joint effusion - T2 bright signal in suprapatellar/popliteal regions", "Inflammatory synovitis, post-traumatic reaction, or early OA - directly correlates with knee pain"], ["2", "Ligament status indeterminate - ACL, PCL, MCL, LCL require DICOM-level review", "Clinical stress testing recommended (Lachman, Anterior Drawer, Valgus/Varus)"], ["3", "Meniscal integrity indeterminate - subtle signal changes cannot be excluded", "McMurray and Thessaly tests for clinical correlation; possible MRI-arthroscopy correlation"], ["4", "No gross acute bony injury identified on available views", "Stress fracture cannot be fully excluded without dedicated STIR/T1 review"], ["5", "Articular cartilage assessment limited - no frank full-thickness loss visible", "Weight-bearing X-ray for joint space narrowing; DICOM cartilage mapping recommended"], ] imp_col_w = [(W - 36*mm) * f for f in [0.06, 0.50, 0.44]] imp_table = Table(imp_data, colWidths=imp_col_w) imp_table.setStyle(TableStyle([ ('BACKGROUND', (0,0), (-1,0), DARK_BLUE), ('TEXTCOLOR', (0,0), (-1,0), colors.white), ('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.white, LIGHT_GRAY]), ('GRID', (0,0), (-1,-1), 0.4, MED_GRAY), ('FONTSIZE', (0,1), (-1,-1), 8.5), ('LEFTPADDING', (0,0), (-1,-1), 7), ('RIGHTPADDING', (0,0), (-1,-1), 7), ('TOPPADDING', (0,0), (-1,-1), 5), ('BOTTOMPADDING',(0,0), (-1,-1), 5), ('VALIGN', (0,0), (-1,-1), 'TOP'), ('ALIGN', (0,0), (0,-1), 'CENTER'), ('ALIGN', (0,0), (-1,0), 'CENTER'), # Highlight finding 1 (joint effusion) row ('BACKGROUND', (0,1), (-1,1), HexColor("#FFF3CD")), ('TEXTCOLOR', (0,1), (-1,1), HexColor("#7D5A00")), ])) story.append(imp_table) story.append(Spacer(1, 5*mm)) # ── CLINICAL CORRELATION TABLE ──────────────────────────────────────────────── story.append(Paragraph("RECOMMENDED CLINICAL TESTS (for Orthopaedic Review)", h2)) story.append(HRFlowable(width="100%", thickness=1.5, color=MID_BLUE, spaceAfter=4)) clin_data = [ [Paragraph("<b>Structure</b>", center_bold), Paragraph("<b>Suggested Clinical Test</b>", center_bold), Paragraph("<b>Target Pathology</b>", center_bold)], ["ACL / PCL", "Lachman Test, Anterior & Posterior Drawer Test", "Ligament tear / laxity"], ["Medial Meniscus", "McMurray Test (internal rotation), Thessaly Test", "Meniscal tear"], ["Lateral Meniscus", "McMurray Test (external rotation), Apley Grind", "Meniscal tear"], ["MCL / LCL", "Valgus / Varus Stress Tests at 0° and 30°", "Collateral ligament sprain/tear"], ["Patellofemoral", "Patellar Grind Test, Apprehension Test", "Chondromalacia, malalignment"], ["Synovium / Effusion", "Ballottement Test, Bulge Sign", "Joint effusion, synovitis"], ["Cartilage / OA", "Weight-bearing X-ray, DICOM cartilage mapping", "Articular cartilage defect"], ] clin_col_w = [(W - 36*mm) * f for f in [0.22, 0.42, 0.36]] clin_table = Table(clin_data, colWidths=clin_col_w) clin_table.setStyle(TableStyle([ ('BACKGROUND', (0,0), (-1,0), DARK_BLUE), ('TEXTCOLOR', (0,0), (-1,0), colors.white), ('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.white, LIGHT_GRAY]), ('GRID', (0,0), (-1,-1), 0.4, MED_GRAY), ('FONTSIZE', (0,1), (-1,-1), 8.5), ('LEFTPADDING', (0,0), (-1,-1), 7), ('RIGHTPADDING', (0,0), (-1,-1), 7), ('TOPPADDING', (0,0), (-1,-1), 5), ('BOTTOMPADDING',(0,0), (-1,-1), 5), ('VALIGN', (0,0), (-1,-1), 'MIDDLE'), ('ALIGN', (0,0), (-1,0), 'CENTER'), ])) story.append(clin_table) story.append(Spacer(1, 5*mm)) # ── MRI IMAGES ──────────────────────────────────────────────────────────────── story.append(Paragraph("ATTACHED MRI FILMS", h2)) story.append(HRFlowable(width="100%", thickness=1.5, color=MID_BLUE, spaceAfter=6)) avail_w = W - 36*mm img_w = (avail_w - 6*mm) / 2 img_h = img_w * 1.33 # approx portrait ratio img1 = RLImage(img1_path, width=img_w, height=img_h) img2 = RLImage(img2_path, width=img_w, height=img_h) img_caption1 = Paragraph("<b>Film Sheet 1</b> - Sagittal & Coronal views<br/>(Harshit Raj | Jul 21, 2020)", small) img_caption2 = Paragraph("<b>Film Sheet 2</b> - Axial views<br/>(Harshit Raj | Jul 21, 2020)", small) img_table = Table( [[img1, img2], [img_caption1, img_caption2]], colWidths=[img_w, img_w], rowHeights=[img_h, None] ) img_table.setStyle(TableStyle([ ('ALIGN', (0,0), (-1,-1), 'CENTER'), ('VALIGN', (0,0), (-1,-1), 'MIDDLE'), ('LEFTPADDING', (0,0), (-1,-1), 3), ('RIGHTPADDING', (0,0), (-1,-1), 3), ('TOPPADDING', (0,0), (-1,-1), 3), ('BOTTOMPADDING',(0,0), (-1,-1), 3), ('BOX', (0,0), (0,0), 0.5, MED_GRAY), ('BOX', (1,0), (1,0), 0.5, MED_GRAY), ])) story.append(img_table) story.append(Spacer(1, 6*mm)) # ── DISCLAIMER ──────────────────────────────────────────────────────────────── disc_box_data = [[ Paragraph( "<b>IMPORTANT DISCLAIMER:</b> This report was generated by AI-assisted visual analysis of " "photographed MRI film sheets and is intended solely as a preliminary reference document for " "discussion with a qualified orthopaedic surgeon or radiologist. It does NOT constitute a formal " "radiological report. Definitive diagnosis and treatment decisions must be based on the original " "high-resolution DICOM images reviewed on a diagnostic-grade workstation by a licensed radiologist. " "Drug dosages, surgical indications, and clinical management should never be determined from this document alone.", disclaimer ) ]] disc_table = Table(disc_box_data, colWidths=[W - 36*mm]) disc_table.setStyle(TableStyle([ ('BACKGROUND', (0,0), (-1,-1), HexColor("#FFF3CD")), ('BOX', (0,0), (-1,-1), 1, ACCENT_GOLD), ('LEFTPADDING', (0,0), (-1,-1), 10), ('RIGHTPADDING', (0,0), (-1,-1), 10), ('TOPPADDING', (0,0), (-1,-1), 8), ('BOTTOMPADDING',(0,0), (-1,-1), 8), ])) story.append(disc_table) story.append(Spacer(1, 4*mm)) # ── FOOTER LINE ─────────────────────────────────────────────────────────────── footer_p = Paragraph( f"Generated by Orris AI Medical Assistant | Report Date: {datetime.now().strftime('%B %d, %Y')} | " "For Orthopaedic Consultation Use Only", mk_style('Footer', fontSize=7.5, textColor=colors.grey, alignment=TA_CENTER) ) story.append(HRFlowable(width="100%", thickness=0.5, color=MED_GRAY)) story.append(Spacer(1, 2*mm)) story.append(footer_p) # ── BUILD ───────────────────────────────────────────────────────────────────── def add_page_number(canvas_obj, doc_obj): canvas_obj.saveState() canvas_obj.setFont('Helvetica', 7.5) canvas_obj.setFillColor(colors.grey) page_num = canvas_obj.getPageNumber() canvas_obj.drawRightString(W - 18*mm, 12*mm, f"Page {page_num}") canvas_obj.drawString(18*mm, 12*mm, "HARSHIT RAJ | MRI KNEE REPORT | CONFIDENTIAL") canvas_obj.restoreState() doc.build(story, onFirstPage=add_page_number, onLaterPages=add_page_number) print(f"PDF created: {output_path}") EOF
Harshit Raj Knee MRI Report
PDF Document · PDF