I need a history check list for my osce exam about scrotal swelling
"scrotum"[MeSH Terms] AND "diagnosis"
| Question | Points towards |
|---|---|
| Sudden severe pain, nausea or vomiting, woke from sleep, or after exertion | Testicular torsion (emergency) |
| Previous similar episodes that resolved | Intermittent torsion |
| Recent trauma, followed by immediate swelling | Haematocele or haematoma, or torsion triggered by trauma |
| Straining, heavy lifting, or coitus as a trigger | Torsion |
| Gradual onset of pain with fever, redness, or an ache starting in the groin | Epididymo-orchitis |
| Urinary symptoms (dysuria, frequency, urgency, haematuria, urethral discharge) | Epididymo-orchitis or STI |
| Fever, rigors, systemic illness, diabetes, very painful, dusky or crepitant skin | Fournier gangrene (emergency) |
| Painless, hard lump or heaviness, noticed incidentally | Testicular tumour |
| Back pain, abdominal mass, cough, haemoptysis, or breast changes (gynaecomastia) | Metastatic testicular cancer or hormone-secreting tumour |
| Bulge in the groin, or a cough impulse | Inguinal hernia |
| Obstructive bowel symptoms (vomiting, constipation, colicky pain) | Strangulated hernia |
| Bag of worms feeling, left-sided, dragging ache | Varicocele |
| Periodic fever, scrotal and cord swelling, travel to or residence in an endemic area | Filariasis |
| Chronic swelling, cough, weight loss, night sweats, or TB contact | Tuberculous epididymitis |
Make a one-page PDF cheat sheet of this history checklist
ls -la /tmp/workspace/
mkdir -p /tmp/workspace/scrotal-cheatsheet/output && cat > /tmp/workspace/scrotal-cheatsheet/build.py << 'EOF' from reportlab.lib.pagesizes import A4 from reportlab.lib import colors from reportlab.lib.units import mm from reportlab.lib.styles import ParagraphStyle from reportlab.platypus import BaseDocTemplate, PageTemplate, Frame, Paragraph, Spacer, Table, TableStyle, FrameBreak, KeepInFrame OUT = "/tmp/workspace/scrotal-cheatsheet/output/Scrotal_Swelling_History_Cheat_Sheet.pdf" NAVY = colors.HexColor("#1F3A5F"); TEAL = colors.HexColor("#2A7F8E"); RED = colors.HexColor("#B3261E") LIGHT = colors.HexColor("#EEF4F7"); REDBG = colors.HexColor("#FDECEA") W, H = A4 M = 10*mm doc = BaseDocTemplate(OUT, pagesize=A4, leftMargin=M, rightMargin=M, topMargin=M, bottomMargin=M, title="Scrotal Swelling History Cheat Sheet", author="Orris") head_h = 17*mm gap = 4*mm colw = (W - 2*M - gap)/2 body_h = H - 2*M - head_h - 8*mm f1 = Frame(M, M+6*mm, colw, body_h, leftPadding=0, rightPadding=0, topPadding=0, bottomPadding=0, id="c1") f2 = Frame(M+colw+gap, M+6*mm, colw, body_h, leftPadding=0, rightPadding=0, topPadding=0, bottomPadding=0, id="c2") def on_page(c, d): c.setFillColor(NAVY); c.rect(M, H-M-head_h+2*mm, W-2*M, head_h-2*mm, fill=1, stroke=0) c.setFillColor(colors.white); c.setFont("Helvetica-Bold", 16) c.drawString(M+4*mm, H-M-8*mm, "SCROTAL SWELLING: HISTORY CHECKLIST") c.setFont("Helvetica", 8.5) c.drawString(M+4*mm, H-M-12.5*mm, "OSCE cheat sheet | Think: torsion first, then infection, hernia, tumour") c.setFillColor(colors.grey); c.setFont("Helvetica-Oblique", 6.5) c.drawString(M, M+1.5*mm, "Sources: S Das, A Manual on Clinical Surgery 13th ed (p. 621); Rosen's Emergency Medicine (Ch. 85, Table 85.9, Box 85.5). Revision aid only.") doc.addPageTemplates([PageTemplate(id="p", frames=[f1, f2], onPage=on_page)]) h = ParagraphStyle("h", fontName="Helvetica-Bold", fontSize=8.8, textColor=colors.white, leading=11) b = ParagraphStyle("b", fontName="Helvetica", fontSize=7.6, leading=9.4, leftIndent=8, bulletIndent=0) bs = ParagraphStyle("bs", parent=b, fontSize=7.2, leading=8.8) cell = ParagraphStyle("cell", fontName="Helvetica", fontSize=7, leading=8.4) cellb = ParagraphStyle("cellb", parent=cell, fontName="Helvetica-Bold") note = ParagraphStyle("note", fontName="Helvetica-Oblique", fontSize=7, leading=8.6) def sec(title, items, color=TEAL, style=b): t = Table([[Paragraph(title, h)]], colWidths=[colw]) t.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),color),("TOPPADDING",(0,0),(-1,-1),2),("BOTTOMPADDING",(0,0),(-1,-1),2),("LEFTPADDING",(0,0),(-1,-1),4)])) out = [t, Spacer(1,1.5)] for it in items: out.append(Paragraph(it, style, bulletText="\u2610")) out.append(Spacer(1,4)) return out S = [] S += sec("1. OPENING", [ "Wash hands, introduce, name + DOB, explain, <b>consent</b>", "Privacy offered, <b>chaperone</b> offered", "Open question: \"Tell me what brought you in today\""]) S += sec("2. PRESENTING COMPLAINT", [ "<b>Onset:</b> sudden (hours) or gradual (weeks/months)? Who noticed?", "<b>Duration / progression:</b> bigger, smaller, static?", "<b>Side</b> (L / R / both) and <b>site</b> (testis, above testis, whole scrotum, into groin)", "<b>Pain:</b> painless or painful, character, severity, radiation (groin, loin, abdomen), constant vs intermittent, relieving / worsening factors", "<b>Variability:</b> worse by evening / better lying down (hydrocele, varicocele)? Cough impulse (hernia)? Can you get above it?", "<b>Associated:</b> heaviness, dragging ache, hardness, change in testis size"]) S += sec("3. SYSTEMS SCREEN", [ "<b>Urinary:</b> dysuria, frequency, haematuria, poor stream, urethral discharge", "<b>Sexual / fertility:</b> libido, erections, fertility concerns", "<b>GI:</b> vomiting, constipation, colicky pain (strangulated hernia)", "<b>Constitutional:</b> fever, rigors, weight loss, night sweats"]) S += sec("4. PMH / DH / FH", [ "<b>Undescended testis</b> in childhood, orchidopexy, hernia repair, vasectomy", "Mumps orchitis, previous UTI / STI, catheter or urethral instrumentation", "TB, diabetes, immunosuppression, previous testicular cancer", "<b>Drugs:</b> anticoagulants, chemo, immunosuppressants. <b>Allergies</b> (antibiotics)", "<b>FH:</b> testicular cancer, hernia, torsion / bell-clapper"]) S += sec("5. SOCIAL / SEXUAL", [ "Sexual history: partners, new partner, condoms, past STIs (ask sensitively)", "Occupation: prolonged standing (varicocele); soot, tar, oil (scrotal skin cancer)", "Heavy lifting, sport / contact, cycling, travel (filariasis areas)", "Smoking, alcohol, drugs. Impact on work and relationships"]) S += sec("6. ICE + CLOSE", [ "<b>Ideas:</b> \"What do you think is causing it?\"", "<b>Concerns:</b> \"Anything you are worried about?\" (often cancer)", "<b>Expectations:</b> \"What were you hoping for today?\"", "Summarise, ask if anything missed, outline plan (exam, urinalysis, US). If torsion suspected: <b>urgent urology, do not delay for imaging</b>", "Thank patient, wash hands"]) S.append(FrameBreak()) # right column red = [ "Sudden severe pain, N/V, woke from sleep, after exertion", "Previous similar settling episodes (intermittent torsion)", "Fever + very painful, dusky or crepitant skin, diabetic", "Colicky pain, vomiting, irreducible groin lump"] t = Table([[Paragraph("RED FLAGS: THINK EMERGENCY", ParagraphStyle("hr", parent=h))]], colWidths=[colw]) t.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),RED),("TOPPADDING",(0,0),(-1,-1),2),("BOTTOMPADDING",(0,0),(-1,-1),2),("LEFTPADDING",(0,0),(-1,-1),4)])) rf = [Paragraph(f"<b>{a}</b> {c}", cell) for a,c in [ ("Torsion:","sudden severe pain, nausea / vomiting, woke from sleep or after exertion, past settling episodes"), ("Fournier gangrene:","fever, severe pain, dusky or crepitant skin, diabetes / immunosuppressed"), ("Strangulated hernia:","colicky pain, vomiting, irreducible groin lump")]] rt = Table([[x] for x in rf], colWidths=[colw]) rt.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),REDBG),("BOX",(0,0),(-1,-1),0.8,RED),("TOPPADDING",(0,0),(-1,-1),1.5),("BOTTOMPADDING",(0,0),(-1,-1),1.5),("LEFTPADDING",(0,0),(-1,-1),4)])) S += [t, rt, Spacer(1,5)] # clue table t = Table([[Paragraph("7. HISTORY CLUE TO DIAGNOSIS", h)]], colWidths=[colw]) t.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),TEAL),("TOPPADDING",(0,0),(-1,-1),2),("BOTTOMPADDING",(0,0),(-1,-1),2),("LEFTPADDING",(0,0),(-1,-1),4)])) rows = [ ("Trauma then immediate swelling","Haematocele / haematoma"), ("Ache in groin, then fever, redness, severe pain","Epididymo-orchitis"), ("Dysuria, discharge, new partner","Epididymitis / STI"), ("Painless hard lump, found incidentally","Testicular tumour"), ("Back pain, cough, gynaecomastia","Metastatic / hormonal tumour"), ("Groin bulge, cough impulse","Inguinal hernia"), ("Left-sided dragging ache, bag of worms, standing job","Varicocele"), ("Painless, fluctuant, worse by evening","Hydrocele"), ("Periodic fever, cord + scrotal swelling, endemic area","Filariasis"), ("Chronic swelling, weight loss, night sweats, TB contact","TB epididymitis"), ] data = [[Paragraph("<b>If the patient says...</b>", cell), Paragraph("<b>Think...</b>", cell)]] + [[Paragraph(a,cell),Paragraph(c,cell)] for a,c in rows] ct = Table(data, colWidths=[colw*0.62, colw*0.38]) ct.setStyle(TableStyle([("GRID",(0,0),(-1,-1),0.3,colors.HexColor("#B8C4CC")),("BACKGROUND",(0,0),(-1,0),LIGHT), ("ROWBACKGROUNDS",(0,1),(-1,-1),[colors.white, colors.HexColor("#F7FAFB")]), ("TOPPADDING",(0,0),(-1,-1),1.5),("BOTTOMPADDING",(0,0),(-1,-1),1.5),("LEFTPADDING",(0,0),(-1,-1),3),("RIGHTPADDING",(0,0),(-1,-1),3),("VALIGN",(0,0),(-1,-1),"MIDDLE")])) S += [t, ct, Spacer(1,5)] # age t = Table([[Paragraph("8. AGE PATTERNS", h)]], colWidths=[colw]) t.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),TEAL),("TOPPADDING",(0,0),(-1,-1),2),("BOTTOMPADDING",(0,0),(-1,-1),2),("LEFTPADDING",(0,0),(-1,-1),4)])) age = [["Infant","Hernia, hydrocele"],["Child","Hernia, torsion, epididymitis"],["Adolescent","Torsion, epididymitis, trauma"], ["Adult","Epididymitis, hernia, trauma, tumour, torsion, Fournier"]] at = Table([[Paragraph(f"<b>{a}</b>",cell),Paragraph(c,cell)] for a,c in age], colWidths=[colw*0.2, colw*0.8]) at.setStyle(TableStyle([("GRID",(0,0),(-1,-1),0.3,colors.HexColor("#B8C4CC")),("BACKGROUND",(0,0),(0,-1),LIGHT),("TOPPADDING",(0,0),(-1,-1),1.5),("BOTTOMPADDING",(0,0),(-1,-1),1.5),("LEFTPADDING",(0,0),(-1,-1),3)])) S += [t, at, Spacer(1,2), Paragraph("Teratoma 20-30 y, seminoma 30-40 y, primary hydrocele >40 y, epididymal cyst / spermatocele >40 y, scrotal skin cancer >50 y.", note), Spacer(1,5)] # comparison t = Table([[Paragraph("9. ACUTE SCROTUM: QUICK COMPARE", h)]], colWidths=[colw]) t.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),TEAL),("TOPPADDING",(0,0),(-1,-1),2),("BOTTOMPADDING",(0,0),(-1,-1),2),("LEFTPADDING",(0,0),(-1,-1),4)])) cmp_rows = [["","Testis torsion","Appendix torsion","Epididymitis"], ["Age","<1 y, puberty","7-14 y","Adult"], ["Onset","Hours","1-2 days","Days-weeks"], ["Pain","Whole testis","Upper pole","Epididymis"], ["Systemic","N/V","None","Fever possible"], ["Cremasteric","Absent","Intact","Intact"], ["Pyuria","Rare","No","Yes"], ["Treatment","Surgery","Supportive","Antibiotics"]] cd = [[Paragraph(("<b>%s</b>" % x) if (i==0 or j==0) else x, cell) for j,x in enumerate(r)] for i,r in enumerate(cmp_rows)] cw = [colw*0.22, colw*0.26, colw*0.26, colw*0.26] cmpt = Table(cd, colWidths=cw) cmpt.setStyle(TableStyle([("GRID",(0,0),(-1,-1),0.3,colors.HexColor("#B8C4CC")),("BACKGROUND",(0,0),(-1,0),LIGHT),("BACKGROUND",(0,1),(0,-1),LIGHT), ("BACKGROUND",(1,1),(1,-1),REDBG),("TOPPADDING",(0,0),(-1,-1),1.5),("BOTTOMPADDING",(0,0),(-1,-1),1.5),("LEFTPADDING",(0,0),(-1,-1),3)])) S += [t, cmpt, Spacer(1,2), Paragraph("No single finding reliably separates torsion from other causes. If torsion is possible: urgent urology.", note), Spacer(1,5)] # tips t = Table([[Paragraph("OSCE TIPS", h)]], colWidths=[colw]) t.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),NAVY),("TOPPADDING",(0,0),(-1,-1),2),("BOTTOMPADDING",(0,0),(-1,-1),2),("LEFTPADDING",(0,0),(-1,-1),4)])) S += [t, Spacer(1,1.5)] for x in ["Ask about <b>sudden-onset pain</b> early: torsion is the can't-miss diagnosis", "Ask sexual history without judgement; explain why you ask", "Always ask undescended testis and family history of testicular cancer", "Use signposting and brief summaries; finish with ICE"]: S.append(Paragraph(x, b, bulletText="\u2022")) doc.build(S) EOF cd /tmp/workspace/scrotal-cheatsheet && python build.py && python -c " from pypdf import PdfReader r=PdfReader('output/Scrotal_Swelling_History_Cheat_Sheet.pdf');print(len(r.pages))"
cd /tmp/workspace/scrotal-cheatsheet && python -c " import pdfplumber with pdfplumber.open('output/Scrotal_Swelling_History_Cheat_Sheet.pdf') as p: p.pages[0].to_image(resolution=110).save('output/preview.png') print('ok')"
cd /tmp/workspace/scrotal-cheatsheet && python - << 'EOF' s = open('build.py').read() s = s.replace('fontSize=7.6, leading=9.4, leftIndent=8, bulletIndent=0', 'fontSize=9, leading=11.4, leftIndent=11, bulletIndent=0, bulletFontName="ZapfDingbats", bulletFontSize=8') s = s.replace('fontSize=7.2, leading=8.8','fontSize=8.5, leading=10.5') s = s.replace('fontSize=7, leading=8.4','fontSize=8.2, leading=10') s = s.replace('fontSize=7, leading=8.6','fontSize=8, leading=10') s = s.replace('fontSize=8.8, textColor=colors.white, leading=11','fontSize=10, textColor=colors.white, leading=12.5') s = s.replace('bulletText="\\u2610"','bulletText="o"') s = s.replace('Spacer(1,4))\n return out','Spacer(1,6))\n return out') s = s.replace('Spacer(1,5)','Spacer(1,7)') s = s.replace('("TOPPADDING",(0,0),(-1,-1),1.5),("BOTTOMPADDING",(0,0),(-1,-1),1.5)','("TOPPADDING",(0,0),(-1,-1),2.2),("BOTTOMPADDING",(0,0),(-1,-1),2.2)') open('build.py','w').write(s) EOF python build.py && python -c " from pypdf import PdfReader print(len(PdfReader('output/Scrotal_Swelling_History_Cheat_Sheet.pdf').pages)) import pdfplumber with pdfplumber.open('output/Scrotal_Swelling_History_Cheat_Sheet.pdf') as p: p.pages[0].to_image(resolution=110).save('output/preview.png')"
cd /tmp/workspace/scrotal-cheatsheet && sed -i 's/colWidths=\[colw\*0.2, colw\*0.8\]/colWidths=[colw*0.26, colw*0.74]/' build.py && python build.py && python -c " from pypdf import PdfReader print(len(PdfReader('output/Scrotal_Swelling_History_Cheat_Sheet.pdf').pages)) import pdfplumber with pdfplumber.open('output/Scrotal_Swelling_History_Cheat_Sheet.pdf') as p: p.pages[0].crop((0,100,300,400)).to_image(resolution=200).save('output/zoom.png')"
cd /tmp/workspace/scrotal-cheatsheet && ls output && python -c " import pdfplumber with pdfplumber.open('output/Scrotal_Swelling_History_Cheat_Sheet.pdf') as p: pg=p.pages[0] pg.crop((0,60,300,300)).to_image(resolution=200).save('output/zoom.png') pg.to_image(resolution=110).save('output/preview.png') print('done')"
cd /tmp/workspace/scrotal-cheatsheet && python - << 'EOF' s = open('build.py').read() s = s.replace("from reportlab.platypus import", "from reportlab.graphics.shapes import Drawing, Rect\nfrom reportlab.platypus import",1) s = s.replace("def sec(title", '''bn = ParagraphStyle("bn", fontName="Helvetica", fontSize=9, leading=11.4) def box(): d = Drawing(8, 10); d.add(Rect(0.5, 2, 6.5, 6.5, strokeColor=NAVY, strokeWidth=0.8, fillColor=None)); return d def item(txt, st=None): t = Table([[box(), Paragraph(txt, st or bn)]], colWidths=[11, colw-11]) t.setStyle(TableStyle([("LEFTPADDING",(0,0),(-1,-1),0),("RIGHTPADDING",(0,0),(-1,-1),0),("TOPPADDING",(0,0),(-1,-1),0),("BOTTOMPADDING",(0,0),(-1,-1),1.2),("VALIGN",(0,0),(-1,-1),"TOP")])) return t def sec(title''',1) s = s.replace('out.append(Paragraph(it, style, bulletText="o"))','out.append(item(it))') open('build.py','w').write(s) EOF python build.py && python -c " from pypdf import PdfReader print(len(PdfReader('output/Scrotal_Swelling_History_Cheat_Sheet.pdf').pages)) import pdfplumber with pdfplumber.open('output/Scrotal_Swelling_History_Cheat_Sheet.pdf') as p: p.pages[0].to_image(resolution=110).save('output/preview2.png')"
Scrotal Swelling History Cheat Sheet
PDF Document · PDF