Inguinal hernia case presentation with detailed examination of direct and indirect Inguinal hernia and its management
inguinal hernia anatomy direct indirect comparison diagram

This diagnostic comparison uses laparoscopic (endoscopic) imaging to demonstrate the internal anatomical differences between an indirect and a direct inguinal hernia. Image A displays an indirect inguinal hernia, characterized by a rounded protrusion located lateral to the inferior epigastric vessels. The defect is clearly defined with a localized network of fine vasculature concentrated near the base of the herniated sac. Image B depicts a direct inguinal hernia, presenting as a more diffuse, wide-based bulge through the abdominal wall, located medial to the epigastric vessels in Hesselbach's triangle. The borders of the direct hernia are less distinct than the indirect type, and the overlying peritoneum shows a more prominent, dense, and tortuous vascular network. These images illustrate the surgical perspective during a Transabdominal Preperitoneal (TAPP) repair, emphasizing the importance of anatomical landmarks, specifically the inferior epigastric vessels, in differentiating hernia types based on their exit point relative to the deep inguinal ring.

This clinical photograph captures an intraoperative view of an inguinal hernia repair, illustrating complex regional anatomy and multiple hernia sacs. The surgical field displays a rare anatomical variation where the inferior epigastric artery (IEA) follows a superficial course along the posterior wall of the inguinal canal. Two distinct direct inguinal hernia sacs are visible: S1, located lateral to the IEA, and S2, located medial to the IEA. Sac S2 appears larger with a wider base compared to the more elongated S1. Additionally, an indirect hernia sac is identified lateral to the direct components. Surgical retractors are in place to expose the posterior wall and cord structures. This image serves as a high-level educational resource for surgical trainees, demonstrating the critical importance of identifying the inferior epigastric vessels to differentiate between direct and indirect hernia components and to recognize vascular anomalies during Lichtenstein or other inguinal hernioplasty procedures.

This composite intraoperative clinical photograph illustrates the surgical anatomy of the posterior inguinal region during a laparoscopic hernia repair. Image A displays essential anatomical landmarks and triangles: a green triangle (D) representing the direct hernia area; a yellow triangle (I) for the indirect hernia area; a blue triangle (F) for the femoral hernia area; and the red 'Triangle of Doom' containing the external iliac vessels. Two orange regions (P1 and P2) designate 'Triangles of Pain,' with P2 highlighting a revised superior boundary (2 cm above the iliopubic tract) to account for variations in nerve branches (lateral cutaneous nerve of the thigh and genitofemoral nerve). Image B divides the surgical field into three zones for procedural standardization: Zone 1 (medial/inferior) focusing on Cooper's ligament and epigastric vessels, Zone 2 (central) involving the spermatic cord and vas deferens parietalization, and Zone 3 (lateral) encompassing the pain triangles. The images demonstrate the preperitoneal view necessary for TAPP/TEP procedures, emphasizing safe zones for mesh fixation to avoid neurovascular injury.

This endoscopic clinical photograph illustrates the surgical anatomy of the posterior abdominal wall during a laparoscopic inguinal hernia repair (TAPP). The image depicts five critical anatomical zones, superimposed as color-coded triangles, essential for avoiding neurovascular complications. A blue star and line denote the inguinal ligament. The triangles are labeled as follows: Blue 'I' for the Indirect hernia space; Yellow 'D' for the Direct hernia space; Grey 'P' for the 'Triangle of Pain' (bounded by the spermatic vessels and iliopubic tract, containing the femoral branch of the genitofemoral nerve and lateral femoral cutaneous nerve); Black 'D' for the 'Triangle of Doom' (bounded by the vas deferens and spermatic vessels, containing the external iliac vessels); and Green 'F' for the Femoral hernia space. This visualization serves as a surgical guide to the 'myopectineal orifice' anatomy, emphasizing safe zones for mesh fixation and areas to avoid tacking to prevent chronic pain or hemorrhage.

This grayscale clinical photograph of a cadaveric dissection illustrates the surgical anatomy of the inguinal region, specifically highlighting the topographic relationships during an indirect inguinal hernia repair. The dissection reveals the inferior epigastric artery (IEA) running cranially and medially. Lateral and inferior to the IEA, an indirect inguinal hernia is visible, marked with a white star, having protruded through the deep inguinal ring lateral to the epigastric vessels. Key neural structures are identified: the iliohypogastric nerve (IHN) is positioned superomedially to the hernia, while the ilio-inguinal nerve (I-IN) runs inferior and lateral to the IEA, passing toward the superficial inguinal ring. Black arrowheads delineate the reflected edges of the external oblique aponeurosis. The image provides critical educational context regarding the 'triangle of pain' and the importance of identifying these neurovascular structures to avoid iatrogenic injury during herniorrhaphy. The anatomical orientation is provided by a 'cranial/lateral' directional indicator, emphasizing the spatial arrangement of the spermatic cord structures and the transversalis fascia.

This composite diagnostic image features three panels (A, B, and C) of an abdomino-pelvic computerized tomography (CT) scan. Panels A and B are axial views, and Panel C is a sagittal view. The imaging demonstrates a right direct inguinal hernia involving the urinary bladder. In axial view A, a yellow arrow points to a lateral protrusion of the contrast-filled urinary bladder wall into the inguinal canal, identified as a bladder horn or cystocele. View B, at a lower axial level, shows the continuation of the herniated tissue near the symphysis pubis. The sagittal view (C) provides a longitudinal perspective, with a yellow arrow highlighting the bladder anteriorly herniating through the abdominal wall defect. The images show key pelvic anatomy including the sacrum, iliac bones, pelvic floor musculature, and the lumbar-sacral spine. This case illustrates a rare content of a direct inguinal hernia, useful for surgical planning and differentiating between direct and indirect hernia types based on their relationship to the inferior epigastric vessels.
| Feature | Indirect (Lateral/Oblique) | Direct (Medial) |
|---|---|---|
| Exit point | Lateral to inferior epigastric vessels, through deep inguinal ring | Medial to inferior epigastric vessels, through Hesselbach's triangle |
| Aetiology | Congenital (patent processus vaginalis) or acquired | Always acquired |
| Age group | All ages; infants, young males | Older, elderly males |
| Covering | Covered by all 3 layers of spermatic cord fascia (internal spermatic fascia, cremasteric fascia, external spermatic fascia) - lies WITHIN the cord | Adjacent to spermatic cord, NOT within it; covered only by attenuated transversalis fascia and (if exits external ring) external spermatic fascia |
| Path | Oblique - traverses the entire inguinal canal | Direct - passes directly/perpendicularly through posterior wall |
| Descent to scrotum | Frequent - can reach the bottom of the scrotum | Uncommon - rarely descends far |
| Shape | Pyriform/elongated | Rounded, globular, broad-based |
| Reducibility | Requires manual reduction in many cases | Often reduces spontaneously on lying down |
| Strangulation risk | Higher - narrow deep ring acts as a tight neck | Lower - broad base, wide neck |
| Bilateral | Less common | More common (bilateral weakness of posterior wall) |
| Bladder involvement | Rare | Bladder may be pulled into sac (sliding hernia) |
| Ring occlusion test | Hernia controlled (does NOT reappear) | Hernia reappears medially (NOT controlled) |


| Type | Description |
|---|---|
| Pantaloon hernia | Both direct and indirect components present simultaneously, straddling the inferior epigastric vessels like two trouser legs |
| Sliding hernia | Retroperitoneal organ (cecum right; sigmoid left; bladder in direct) forms part of the hernia sac wall |
| Richter's hernia | Only the antimesenteric border of the bowel is trapped - intestinal obstruction may be absent while strangulation occurs |
| Littre's hernia | Meckel's diverticulum in the sac |
| Maydl's hernia | "W" hernia - two loops of bowel in sac with the connecting loop remaining intraperitoneal; high strangulation risk |
| Indirect | Direct | |
|---|---|---|
| Relation to epigastric vessels | Lateral | Medial |
| Through | Deep inguinal ring → inguinal canal | Hesselbach's triangle directly |
| Congenital origin | Yes (patent PV) | No |
| Common age | Young | Elderly |
| Scrotal descent | Common | Rare |
| Strangulation | More common | Less common |
| Ring occlusion test | Controlled | Not controlled (reappears medially) |
| Finger-in-ring tip impulse | On tip (oblique) | On pulp (direct) |
| Zieman's test | Index finger impulse | Middle finger impulse |
Create a surgical examination checklist PDF for bedside inguinal hernia assessment
ls -la /tmp/workspace/ 2>/dev/null || echo "workspace empty or missing"
mkdir -p /tmp/workspace/inguinal-hernia-checklist
~/inguinal-hernia-checklist/generate_checklist.py
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import mm, cm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, KeepTogether
)
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_RIGHT
from reportlab.pdfgen import canvas
from reportlab.platypus import BaseDocTemplate, PageTemplate, Frame
import datetime
OUTPUT = "/tmp/workspace/inguinal-hernia-checklist/Inguinal_Hernia_Surgical_Examination_Checklist.pdf"
# ─── Color Palette ────────────────────────────────────────────────────────────
DARK_BLUE = colors.HexColor("#1B3A6B")
MID_BLUE = colors.HexColor("#2A5BAD")
LIGHT_BLUE = colors.HexColor("#D6E4F7")
ACCENT = colors.HexColor("#E84545") # red for warnings/strangulation
GREEN = colors.HexColor("#2D7D46")
PALE_GREEN = colors.HexColor("#DFF2E3")
PALE_YELLOW = colors.HexColor("#FFF8DC")
PALE_RED = colors.HexColor("#FDECEA")
GREY_LIGHT = colors.HexColor("#F5F5F5")
GREY_MED = colors.HexColor("#CCCCCC")
BLACK = colors.black
WHITE = colors.white
W, H = A4 # 595.27 x 841.89 pt
# ─── Page numbering canvas ─────────────────────────────────────────────────────
class NumberedCanvas(canvas.Canvas):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._saved_page_states = []
def showPage(self):
self._saved_page_states.append(dict(self.__dict__))
self._startPage()
def save(self):
num_pages = len(self._saved_page_states)
for state in self._saved_page_states:
self.__dict__.update(state)
self.draw_page_number(num_pages)
canvas.Canvas.showPage(self)
canvas.Canvas.save(self)
def draw_page_number(self, page_count):
page_num = self._saved_page_states.index(
{k: v for k, v in self.__dict__.items() if k in self._saved_page_states[0]}
) + 1 if hasattr(self, '_page_count_hack') else None
# simple footer
self.saveState()
self.setFont("Helvetica", 7)
self.setFillColor(colors.HexColor("#888888"))
self.drawCentredString(W / 2, 18, f"Inguinal Hernia Bedside Examination Checklist | Page {page_count}")
self.restoreState()
def header_footer(canvas_obj, doc):
canvas_obj.saveState()
# Top banner
canvas_obj.setFillColor(DARK_BLUE)
canvas_obj.rect(0, H - 28*mm, W, 28*mm, fill=1, stroke=0)
canvas_obj.setFillColor(WHITE)
canvas_obj.setFont("Helvetica-Bold", 13)
canvas_obj.drawCentredString(W/2, H - 14*mm, "INGUINAL HERNIA — BEDSIDE SURGICAL EXAMINATION CHECKLIST")
canvas_obj.setFont("Helvetica", 8)
canvas_obj.drawCentredString(W/2, H - 21*mm, "For use by medical students, surgical trainees, and examiners at the bedside")
# Bottom bar
canvas_obj.setFillColor(DARK_BLUE)
canvas_obj.rect(0, 0, W, 12*mm, fill=1, stroke=0)
canvas_obj.setFillColor(WHITE)
canvas_obj.setFont("Helvetica", 7)
today = datetime.date.today().strftime("%B %Y")
canvas_obj.drawString(15*mm, 4*mm, f"Sources: Bailey & Love 28e | S. Das Manual of Clinical Surgery 13e | Fischer's Mastery of Surgery 8e")
canvas_obj.drawRightString(W - 15*mm, 4*mm, today)
canvas_obj.restoreState()
def build_pdf():
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
leftMargin=14*mm,
rightMargin=14*mm,
topMargin=34*mm,
bottomMargin=18*mm,
title="Inguinal Hernia Bedside Examination Checklist",
author="Orris Medical",
)
styles = getSampleStyleSheet()
# ── Custom Styles ────────────────────────────────────────────────────────
def S(name, **kw):
return ParagraphStyle(name, **kw)
body = S("body", fontName="Helvetica", fontSize=8, leading=11, spaceAfter=2)
bold_body = S("bold_body", fontName="Helvetica-Bold", fontSize=8, leading=11)
small = S("small", fontName="Helvetica", fontSize=7, leading=9, textColor=colors.HexColor("#444444"))
h1 = S("h1", fontName="Helvetica-Bold", fontSize=10, leading=14, textColor=WHITE)
h2 = S("h2", fontName="Helvetica-Bold", fontSize=9, leading=12, textColor=DARK_BLUE)
h3 = S("h3", fontName="Helvetica-Bold", fontSize=8, leading=11, textColor=MID_BLUE)
note_st = S("note", fontName="Helvetica-Oblique", fontSize=7, leading=9, textColor=colors.HexColor("#555555"))
warn_st = S("warn", fontName="Helvetica-Bold", fontSize=8, leading=10, textColor=ACCENT)
center_s = S("center", fontName="Helvetica", fontSize=8, leading=11, alignment=TA_CENTER)
center_b = S("centerb", fontName="Helvetica-Bold", fontSize=8, leading=11, alignment=TA_CENTER, textColor=WHITE)
cell_hdr = S("cellhdr", fontName="Helvetica-Bold", fontSize=7.5, leading=10, textColor=WHITE, alignment=TA_CENTER)
cell_body = S("cellbody", fontName="Helvetica", fontSize=7.5, leading=10)
cell_b2 = S("cellb2", fontName="Helvetica-Bold", fontSize=7.5, leading=10)
story = []
# ═══════════════════════════════════════════════════════════════════════════
# SECTION HELPER
# ═══════════════════════════════════════════════════════════════════════════
def section_header(title, color=DARK_BLUE):
data = [[Paragraph(title, h1)]]
t = Table(data, colWidths=[W - 28*mm])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), color),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 8),
]))
story.append(t)
story.append(Spacer(1, 2*mm))
def subsection(title):
story.append(Spacer(1, 1.5*mm))
story.append(Paragraph(title, h2))
story.append(HRFlowable(width="100%", thickness=0.5, color=MID_BLUE, spaceAfter=1*mm))
def checkbox_row(label, note="", indent=0):
"""Returns a list of 2-column row data: [checkbox cell, label+note cell]."""
box = "☐"
lbl = Paragraph(f"<b>{label}</b>", bold_body) if not note else \
Paragraph(f"<b>{label}</b> <font size='7' color='#666666'><i>{note}</i></font>", body)
return [Paragraph(box, center_s), lbl]
def cb_table(rows, bg=None):
"""Build a compact checkbox table from rows of [box, label]."""
ts = [
("VALIGN", (0,0), (-1,-1), "TOP"),
("LEFTPADDING", (0,0), (-1,-1), 2),
("RIGHTPADDING", (0,0), (-1,-1), 2),
("TOPPADDING", (0,0), (-1,-1), 2.5),
("BOTTOMPADDING", (0,0), (-1,-1), 2.5),
("GRID", (0,0), (-1,-1), 0.3, GREY_MED),
]
if bg:
ts.append(("BACKGROUND", (0,0), (-1,-1), bg))
t = Table(rows, colWidths=[8*mm, W - 28*mm - 8*mm])
t.setStyle(TableStyle(ts))
return t
def two_col_cb(left_rows, right_rows, left_title="", right_title=""):
"""Side-by-side two-column checkbox block."""
cw = (W - 28*mm) / 2 - 2*mm
def mini(rows, title, title_color=MID_BLUE):
inner = []
if title:
inner.append(Table([[Paragraph(title, h3)]], colWidths=[cw],
style=TableStyle([
("BACKGROUND", (0,0), (-1,-1), LIGHT_BLUE),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
("LEFTPADDING", (0,0), (-1,-1), 5),
])))
cb = Table(rows, colWidths=[7*mm, cw - 7*mm],
style=TableStyle([
("VALIGN", (0,0), (-1,-1), "TOP"),
("LEFTPADDING", (0,0), (-1,-1), 2),
("RIGHTPADDING", (0,0), (-1,-1), 2),
("TOPPADDING", (0,0), (-1,-1), 2.5),
("BOTTOMPADDING", (0,0), (-1,-1), 2.5),
("GRID", (0,0), (-1,-1), 0.3, GREY_MED),
]))
inner.append(cb)
return inner
left = mini(left_rows, left_title)
right = mini(right_rows, right_title)
outer_data = [[left, right]]
outer = Table(outer_data, colWidths=[cw + 2*mm, cw + 2*mm])
outer.setStyle(TableStyle([
("VALIGN", (0,0), (-1,-1), "TOP"),
("LEFTPADDING", (0,0), (-1,-1), 0),
("RIGHTPADDING", (0,0), (-1,-1), 0),
("TOPPADDING", (0,0), (-1,-1), 0),
("BOTTOMPADDING", (0,0), (-1,-1), 0),
]))
return outer
# ═══════════════════════════════════════════════════════════════════════════
# ── SECTION 1: PATIENT DETAILS ──────────────────────────────────────────
# ═══════════════════════════════════════════════════════════════════════════
section_header("SECTION 1 — PATIENT IDENTIFICATION & PRE-EXAMINATION SETUP")
pt_data = [
[Paragraph("<b>Patient Name:</b>", body), Paragraph("_______________________________", body),
Paragraph("<b>Age / Sex:</b>", body), Paragraph("_______ / _______", body)],
[Paragraph("<b>Date:</b>", body), Paragraph("_______________________________", body),
Paragraph("<b>Ward / Bed:</b>", body), Paragraph("_______________________________", body)],
[Paragraph("<b>Examiner:</b>", body), Paragraph("_______________________________", body),
Paragraph("<b>Side:</b>", body), Paragraph("☐ Right ☐ Left ☐ Bilateral", body)],
]
pt = Table(pt_data, colWidths=[28*mm, 57*mm, 28*mm, 57*mm])
pt.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), GREY_LIGHT),
("GRID", (0,0), (-1,-1), 0.4, GREY_MED),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 5),
]))
story.append(pt)
story.append(Spacer(1, 3*mm))
# Pre-exam setup
subsection("Pre-Examination Setup")
setup = [
checkbox_row("Explain procedure and obtain verbal consent"),
checkbox_row("Adequate privacy; patient standing (primary) and lying supine (secondary)"),
checkbox_row("Expose groin, lower abdomen and scrotum/labia fully"),
checkbox_row("Examiner seated in front of standing patient"),
checkbox_row("Ask patient to point to site of swelling/discomfort"),
]
story.append(cb_table(setup, bg=GREY_LIGHT))
story.append(Spacer(1, 4*mm))
# ═══════════════════════════════════════════════════════════════════════════
# ── SECTION 2: HISTORY CHECKLIST ────────────────────────────────────────
# ═══════════════════════════════════════════════════════════════════════════
section_header("SECTION 2 — FOCUSED HISTORY", color=MID_BLUE)
hist = [
checkbox_row("Onset: sudden (with strain/lift) or gradual?"),
checkbox_row("First site: groin → scrotum (inguinal) OR below groin crease ascending (femoral)?"),
checkbox_row("Size at onset — small and gradually enlarging (acquired) OR immediately large (congenital)?"),
checkbox_row("Reducibility: disappears on lying down automatically (direct) OR requires manual reduction (indirect)?"),
checkbox_row("Aggravating factors: coughing, straining, heavy lifting, prolonged standing"),
checkbox_row("Pain/discomfort: dragging, aching; severe pain suggests incarceration/strangulation"),
checkbox_row("Features of bowel obstruction: colicky pain, vomiting, distension, absolute constipation"),
checkbox_row("Predisposing causes: chronic cough, constipation, BPH (straining), ascites, heavy labour"),
checkbox_row("Past surgical history: appendicectomy (nerve damage → weak inguinal floor), previous hernia repair"),
checkbox_row("Truss use: duration; note pigmentation/skin damage from long-term use"),
]
story.append(cb_table(hist))
story.append(Spacer(1, 4*mm))
# ═══════════════════════════════════════════════════════════════════════════
# ── SECTION 3: INSPECTION ───────────────────────────────────────────────
# ═══════════════════════════════════════════════════════════════════════════
section_header("SECTION 3 — INSPECTION (Patient Standing)")
insp = [
checkbox_row("Swelling visible at rest in groin?", "Note: above inguinal ligament = inguinal; below = femoral"),
checkbox_row("Position relative to pubic tubercle:",
"Inguinal = above & medial to PT; Femoral = below & lateral to PT"),
checkbox_row("Shape: elongated/pyriform (indirect) vs. globular/rounded (direct)"),
checkbox_row("Extent: does swelling reach bottom of scrotum? (congenital indirect hernia)"),
checkbox_row("Visible peristalsis over swelling?", "Suggests small bowel content; never seen in femoral hernia"),
checkbox_row("Skin: normal (uncomplicated) | reddened (strangulation) | pigmented+wrinkled (truss use)"),
checkbox_row("Scars: site and quality of any previous surgical scar", "Puckered scar = previous infection → common recurrence cause"),
checkbox_row("Scrotal skin movement (dartos contraction) — distinguish from peristalsis"),
checkbox_row("Ask patient to cough — observe for expansile bulge synchronous with cough"),
]
story.append(cb_table(insp, bg=GREY_LIGHT))
story.append(Spacer(1, 4*mm))
# ═══════════════════════════════════════════════════════════════════════════
# ── SECTION 4: PALPATION ────────────────────────────────────────────────
# ═══════════════════════════════════════════════════════════════════════════
section_header("SECTION 4 — PALPATION")
subsection("4A — Basic Palpation (Both positions)")
basic = [
checkbox_row("Identify pubic tubercle — confirm swelling is above (inguinal) vs. below (femoral)"),
checkbox_row("Temperature: warm (strangulation/inflammation) vs. normal"),
checkbox_row("Tenderness: mild (uncomplicated) vs. severe (complication)"),
checkbox_row("Consistency: soft/reducible (uncomplicated) | firm/hard (omentum/incarcerated bowel)"),
checkbox_row("Spermatic cord: palpate; thicker cord on affected side suggests hernia sac within cord (indirect)"),
checkbox_row("Can you get above the swelling?", "YES = scrotal swelling | CANNOT = hernia/hydrocele communicating"),
checkbox_row("Testis: palpate separately; confirm testis present and separate from swelling"),
checkbox_row("Percussion: resonant (bowel) vs. dull (omentum/fluid)"),
checkbox_row("Auscultation: bowel sounds over swelling?"),
]
story.append(cb_table(basic))
story.append(Spacer(1, 3*mm))
subsection("4B — Impulse on Coughing (Standing)")
imp = [
checkbox_row("Place finger over superficial inguinal ring; ask patient to cough"),
checkbox_row("Expansile cough impulse PRESENT → consistent with hernia"),
checkbox_row("Impulse ABSENT → suggests strangulation, incarceration, or neck blocked by adhesions"),
checkbox_row("Note: ask patient to turn face away before coughing"),
]
story.append(cb_table(imp, bg=PALE_GREEN))
story.append(Spacer(1, 3*mm))
subsection("4C — Reducibility Test (Standing → Lying)")
red = [
checkbox_row("Attempt reduction with patient standing; note direction (upward+backward+lateral for indirect)"),
checkbox_row("Reduction spontaneous on lying down → suggests DIRECT hernia"),
checkbox_row("Requires gentle sustained manual pressure → suggests INDIRECT hernia"),
checkbox_row("Note gurgling sound on reduction (bowel) or absence of sound (omentum)"),
checkbox_row("NOT REDUCIBLE → document: likely incarcerated; do NOT force reduction"),
]
story.append(cb_table(red))
story.append(Spacer(1, 3*mm))
subsection("4D — Zieman's Three-Finger Test (after complete reduction)")
story.append(Paragraph(
"<i>Note: Only applicable when no obvious swelling OR after hernia fully reduced. "
"Patient stands. Place fingers simultaneously:</i>", note_st))
story.append(Spacer(1, 1*mm))
ziem_data = [
[Paragraph("<b>Finger</b>", cell_hdr), Paragraph("<b>Position</b>", cell_hdr),
Paragraph("<b>Hernia Detected</b>", cell_hdr), Paragraph("<b>Result</b>", cell_hdr)],
[Paragraph("Index finger", cell_body),
Paragraph("Deep inguinal ring\n(½ inch above mid-inguinal point = midpoint between ASIS and symphysis pubis)", cell_body),
Paragraph("Indirect inguinal hernia", cell_body),
Paragraph("☐ Impulse felt\n☐ No impulse", cell_body)],
[Paragraph("Middle finger", cell_body),
Paragraph("Superficial inguinal ring", cell_body),
Paragraph("Direct inguinal hernia", cell_body),
Paragraph("☐ Impulse felt\n☐ No impulse", cell_body)],
[Paragraph("Ring finger", cell_body),
Paragraph("Saphenous opening\n(4 cm below & lateral to pubic tubercle)", cell_body),
Paragraph("Femoral hernia", cell_body),
Paragraph("☐ Impulse felt\n☐ No impulse", cell_body)],
]
ziem_t = Table(ziem_data, colWidths=[22*mm, 58*mm, 50*mm, 30*mm - 2*mm])
ziem_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), DARK_BLUE),
("BACKGROUND", (0,1), (-1,1), LIGHT_BLUE),
("BACKGROUND", (0,2), (-1,2), GREY_LIGHT),
("BACKGROUND", (0,3), (-1,3), LIGHT_BLUE),
("GRID", (0,0), (-1,-1), 0.5, GREY_MED),
("VALIGN", (0,0), (-1,-1), "TOP"),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
("LEFTPADDING", (0,0), (-1,-1), 4),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
]))
story.append(ziem_t)
story.append(Spacer(1, 3*mm))
subsection("4E — Invagination Test (Finger-in-Ring Test)")
inv_note = Paragraph(
"<i>Insinuate the little finger into the scrotum upwards through the superficial inguinal ring. "
"Ask patient to cough.</i>", note_st)
story.append(inv_note)
story.append(Spacer(1, 1*mm))
inv_data = [
[Paragraph("<b>Finding</b>", cell_hdr), Paragraph("<b>Interpretation</b>", cell_hdr),
Paragraph("<b>Result</b>", cell_hdr)],
[Paragraph("Impulse felt on PULP of finger", cell_body),
Paragraph("DIRECT hernia — comes directly backwards", cell_b2),
Paragraph("☐", center_s)],
[Paragraph("Impulse felt on TIP of finger", cell_body),
Paragraph("INDIRECT hernia — comes from above, backwards and outwards (oblique)", cell_b2),
Paragraph("☐", center_s)],
[Paragraph("Finger enters ring and goes DIRECTLY BACKWARDS", cell_body),
Paragraph("DIRECT hernia", cell_b2),
Paragraph("☐", center_s)],
[Paragraph("Finger enters ring going UPWARDS, BACKWARDS and OUTWARDS", cell_body),
Paragraph("INDIRECT hernia", cell_b2),
Paragraph("☐", center_s)],
]
inv_t = Table(inv_data, colWidths=[70*mm, 72*mm, 18*mm])
inv_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), DARK_BLUE),
("BACKGROUND", (0,1), (-1,1), PALE_YELLOW),
("BACKGROUND", (0,2), (-1,2), PALE_GREEN),
("BACKGROUND", (0,3), (-1,3), PALE_YELLOW),
("BACKGROUND", (0,4), (-1,4), PALE_GREEN),
("GRID", (0,0), (-1,-1), 0.5, GREY_MED),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
("LEFTPADDING", (0,0), (-1,-1), 4),
("ALIGN", (2,0), (2,-1), "CENTER"),
]))
story.append(inv_t)
story.append(Spacer(1, 3*mm))
subsection("4F — Ring Occlusion Test (Confirmatory — after full reduction, standing)")
story.append(Paragraph(
"<i>Press thumb firmly over the deep inguinal ring (½ inch above the mid-point between ASIS and symphysis pubis). "
"Ask patient to cough or strain.</i>", note_st))
story.append(Spacer(1, 1*mm))
roc_data = [
[Paragraph("<b>Finding on Coughing</b>", cell_hdr), Paragraph("<b>Interpretation</b>", cell_hdr),
Paragraph("<b>Result</b>", cell_hdr)],
[Paragraph("Hernia DOES NOT REAPPEAR (controlled)", cell_body),
Paragraph("INDIRECT hernia — deep ring occluded, hernia cannot exit", cell_b2),
Paragraph("☐", center_s)],
[Paragraph("Hernia REAPPEARS medially to occluding thumb", cell_body),
Paragraph("DIRECT hernia — exits through Hesselbach's triangle, not deep ring", cell_b2),
Paragraph("☐", center_s)],
]
roc_t = Table(roc_data, colWidths=[70*mm, 82*mm, 8*mm])
roc_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), DARK_BLUE),
("BACKGROUND", (0,1), (-1,1), PALE_GREEN),
("BACKGROUND", (0,2), (-1,2), PALE_RED),
("GRID", (0,0), (-1,-1), 0.5, GREY_MED),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
("LEFTPADDING", (0,0), (-1,-1), 4),
("ALIGN", (2,0), (2,-1), "CENTER"),
]))
story.append(roc_t)
story.append(Spacer(1, 4*mm))
# ═══════════════════════════════════════════════════════════════════════════
# ── SECTION 5: COMPARISON TABLE ─────────────────────────────────────────
# ═══════════════════════════════════════════════════════════════════════════
section_header("SECTION 5 — DIRECT vs. INDIRECT INGUINAL HERNIA: KEY DIFFERENTIATORS", color=GREEN)
comp_data = [
[Paragraph("<b>Feature</b>", cell_hdr),
Paragraph("<b>INDIRECT (Lateral/Oblique)</b>", cell_hdr),
Paragraph("<b>DIRECT (Medial)</b>", cell_hdr)],
["Relation to inf. epigastric vessels", "LATERAL — exits through deep inguinal ring", "MEDIAL — exits through Hesselbach's triangle"],
["Origin", "Congenital (patent processus vaginalis) or acquired", "Always ACQUIRED"],
["Typical age", "All ages; infants to young adults", "Middle-aged to elderly males"],
["Hernia sac & spermatic cord", "Within cord (covered by all 3 fascial layers)", "Adjacent to cord, NOT within it"],
["Path", "Oblique — traverses full inguinal canal", "Direct — perpendicular through posterior wall"],
["Shape", "Pyriform/elongated", "Globular, rounded, broad-based"],
["Scrotal descent", "Common — may reach bottom of scrotum", "Uncommon"],
["Spontaneous reduction", "Requires manual reduction (mostly)", "Often disappears on lying down"],
["Strangulation risk", "HIGHER — narrow neck at deep ring", "LOWER — broad base, wide neck"],
["Zieman's test", "Index finger impulse", "Middle finger impulse"],
["Invagination test", "Impulse on TIP of finger", "Impulse on PULP of finger"],
["Ring occlusion test", "CONTROLLED (hernia does not reappear)", "NOT controlled (reappears medially)"],
["Bilateral occurrence", "Less common", "More common (bilateral wall weakness)"],
["Bladder involvement", "Rare", "May occur (sliding direct hernia)"],
]
comp_rows = []
for i, row in enumerate(comp_data):
if i == 0:
comp_rows.append([Paragraph(str(c), cell_hdr) for c in row])
else:
comp_rows.append([
Paragraph(str(row[0]), cell_b2 if i % 2 == 1 else cell_body),
Paragraph(str(row[1]), cell_body),
Paragraph(str(row[2]), cell_body),
])
comp_t = Table(comp_rows, colWidths=[42*mm, 70*mm, 50*mm])
ts = TableStyle([
("BACKGROUND", (0,0), (-1,0), GREEN),
("GRID", (0,0), (-1,-1), 0.4, GREY_MED),
("VALIGN", (0,0), (-1,-1), "TOP"),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
("LEFTPADDING", (0,0), (-1,-1), 4),
])
for i in range(1, len(comp_rows)):
bg = GREY_LIGHT if i % 2 == 0 else WHITE
ts.add("BACKGROUND", (0,i), (-1,i), bg)
comp_t.setStyle(ts)
story.append(comp_t)
story.append(Spacer(1, 4*mm))
# ═══════════════════════════════════════════════════════════════════════════
# ── SECTION 6: COMPLICATIONS CHECKLIST ──────────────────────────────────
# ═══════════════════════════════════════════════════════════════════════════
section_header("SECTION 6 — ASSESSMENT FOR COMPLICATIONS", color=ACCENT)
story.append(Paragraph(
"<b>⚠ RED FLAGS — Assess at every examination. Mark if present.</b>", warn_st))
story.append(Spacer(1, 1*mm))
comp_check = [
checkbox_row("Irreducibility (Incarceration)",
"Firm, non-reducible swelling; may have absent cough impulse → URGENT surgical referral"),
checkbox_row("Intestinal Obstruction",
"Colicky abdominal pain + vomiting + distension + absolute constipation"),
checkbox_row("Strangulation",
"Tender, tense, irreducible swelling; ABSENT cough impulse; skin changes → EMERGENCY surgery"),
checkbox_row("Inflammation",
"Warm, red, tender swelling without signs of strangulation"),
checkbox_row("Faecal vomiting",
"Ominous — indicates low small bowel or large bowel obstruction"),
checkbox_row("Secondary hydrocele",
"Fluid around testis due to peritoneal irritation from hernia"),
]
complication_table = cb_table(comp_check, bg=PALE_RED)
story.append(complication_table)
story.append(Spacer(1, 4*mm))
# ═══════════════════════════════════════════════════════════════════════════
# ── SECTION 7: SPECIAL TYPES ────────────────────────────────────────────
# ═══════════════════════════════════════════════════════════════════════════
section_header("SECTION 7 — SPECIAL HERNIA TYPES TO RECOGNISE", color=MID_BLUE)
sp_data = [
[Paragraph("<b>Type</b>", cell_hdr), Paragraph("<b>Key Feature</b>", cell_hdr),
Paragraph("<b>Clinical Clue</b>", cell_hdr)],
["Pantaloon (Saddle-bag)", "Both direct + indirect components straddle epigastric vessels",
"Difficult to reduce; found at operation"],
["Sliding hernia", "Retroperitoneal organ (cecum R / sigmoid L / bladder in direct) forms part of sac wall",
"Caution during sac dissection — risk of bowel/bladder injury"],
["Richter's hernia", "Only antimesenteric border of bowel trapped",
"Obstruction may be absent even though strangulation is occurring"],
["Littre's hernia", "Meckel's diverticulum in the sac",
"Presents like standard hernia; diagnosed intraoperatively"],
["Maydl's hernia ('W' hernia)", "Two bowel loops in sac; connecting loop intraperitoneal",
"High strangulation risk of intraperitoneal loop"],
["Congenital (infantile)", "Hernia reaches base of scrotum at first presentation",
"Preformed sac; may appear at any age"],
]
sp_rows = []
for i, row in enumerate(sp_data):
if i == 0:
sp_rows.append([Paragraph(c, cell_hdr) for c in row])
else:
sp_rows.append([Paragraph(c, cell_b2 if i == 0 else cell_body) for c in row])
sp_t = Table(sp_rows, colWidths=[38*mm, 67*mm, 57*mm])
sp_ts = TableStyle([
("BACKGROUND", (0,0), (-1,0), MID_BLUE),
("GRID", (0,0), (-1,-1), 0.4, GREY_MED),
("VALIGN", (0,0), (-1,-1), "TOP"),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
("LEFTPADDING", (0,0), (-1,-1), 4),
])
for i in range(1, len(sp_rows)):
sp_ts.add("BACKGROUND", (0,i), (-1,i), GREY_LIGHT if i % 2 == 0 else WHITE)
sp_t.setStyle(sp_ts)
story.append(sp_t)
story.append(Spacer(1, 4*mm))
# ═══════════════════════════════════════════════════════════════════════════
# ── SECTION 8: MANAGEMENT SUMMARY ───────────────────────────────────────
# ═══════════════════════════════════════════════════════════════════════════
section_header("SECTION 8 — MANAGEMENT PATHWAY CHECKLIST")
subsection("8A — Conservative Management (Watchful Waiting)")
cons = [
checkbox_row("Appropriate for: asymptomatic/minimally symptomatic DIRECT hernia in elderly patients who decline surgery"),
checkbox_row("Educate patient: seek urgent review if hernia enlarges, becomes symptomatic, or is irreducible"),
checkbox_row("TRUSSES ARE NOT RECOMMENDED — do not prescribe"),
checkbox_row("Treat predisposing causes: cough (COPD), constipation, BPH"),
]
story.append(cb_table(cons, bg=PALE_YELLOW))
story.append(Spacer(1, 3*mm))
subsection("8B — Surgical Indications")
surg_ind = [
checkbox_row("All symptomatic hernias"),
checkbox_row("All INDIRECT hernias (higher strangulation risk)"),
checkbox_row("Irreducible/incarcerated hernia — URGENT"),
checkbox_row("Strangulated hernia — EMERGENCY"),
]
story.append(cb_table(surg_ind))
story.append(Spacer(1, 3*mm))
subsection("8C — Surgical Options")
surg_data = [
[Paragraph("<b>Procedure</b>", cell_hdr), Paragraph("<b>Indication</b>", cell_hdr),
Paragraph("<b>Key Points</b>", cell_hdr), Paragraph("<b>Plan</b>", cell_hdr)],
["Herniotomy",
"Children with indirect hernia (patent PV)",
"Excision + ligation of sac at neck; NO floor repair needed in children",
"☐"],
["Bassini Repair",
"Adults; open suture repair",
"Conjoint tendon sutured to inguinal ligament (pubic tubercle → deep ring); floor reconstruction",
"☐"],
["Shouldice Repair",
"Adults; best open suture repair",
"Four-layer repair; double-breasting of transversalis fascia; <2% lifetime failure at expert centres",
"☐"],
["Lichtenstein Tension-free (Mesh)",
"Adults — current gold standard open repair",
"Flat polypropylene mesh; tension-free; recurrence <1-2%; can be done under LA as day-case",
"☐"],
["Desarda Repair",
"Adults; no-mesh open repair",
"Strip of external oblique aponeurosis used to reinforce posterior wall",
"☐"],
["TEP (Laparoscopic)",
"Bilateral; recurrence; active patients",
"Totally extraperitoneal; no peritoneal entry; longer learning curve",
"☐"],
["TAPP (Laparoscopic)",
"Bilateral; recurrence; diagnostic uncertainty",
"Transabdominal; peritoneum entered; allows bilateral inspection",
"☐"],
["Robot-assisted",
"Institutional/surgeon preference",
"Enhanced view; no proven additional patient benefit over standard laparoscopy; high cost",
"☐"],
]
surg_rows = []
for i, row in enumerate(surg_data):
if i == 0:
surg_rows.append([Paragraph(c, cell_hdr) for c in row])
else:
surg_rows.append([Paragraph(c, cell_body) for c in row])
surg_t = Table(surg_rows, colWidths=[32*mm, 38*mm, 80*mm, 10*mm])
surg_ts = TableStyle([
("BACKGROUND", (0,0), (-1,0), DARK_BLUE),
("GRID", (0,0), (-1,-1), 0.4, GREY_MED),
("VALIGN", (0,0), (-1,-1), "TOP"),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
("LEFTPADDING", (0,0), (-1,-1), 4),
("ALIGN", (3,0), (3,-1), "CENTER"),
])
for i in range(1, len(surg_rows)):
surg_ts.add("BACKGROUND", (0,i), (-1,i), GREY_LIGHT if i % 2 == 0 else WHITE)
surg_t.setStyle(surg_ts)
story.append(surg_t)
story.append(Spacer(1, 3*mm))
# Laparoscopic advantages
lap_box_data = [[
Paragraph(
"<b>Laparoscopic advantages over open:</b> Less immediate and long-term pain (up to 5 years) · "
"Faster return to activity · Fewer wound complications · "
"Preferred for BILATERAL hernias and RECURRENCE after previous open repair",
body)
]]
lap_box = Table(lap_box_data, colWidths=[W - 28*mm])
lap_box.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), LIGHT_BLUE),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 8),
("BOX", (0,0), (-1,-1), 0.8, MID_BLUE),
]))
story.append(lap_box)
story.append(Spacer(1, 3*mm))
subsection("8D — EHS Classification (record pre-operatively)")
ehs_data = [
[Paragraph("<b>Axis</b>", cell_hdr), Paragraph("<b>Code</b>", cell_hdr),
Paragraph("<b>Value for this patient</b>", cell_hdr)],
["Primary (P) or Recurrent (R)", "P / R", "☐ P ☐ R"],
["Lateral (L) / Medial (M) / Femoral (F)", "L / M / F", "☐ L ☐ M ☐ F"],
["Defect size (fingerbreadths; 1 fb ≈ 1.5 cm)", "1 (<1.5cm) / 2 (1.5-4.5cm) / 3 (>4.5cm)", "☐ 1 ☐ 2 ☐ 3"],
["Example: primary indirect, 3 cm defect =", "PL2", "Classification: ___________"],
]
ehs_rows = []
for i, row in enumerate(ehs_data):
if i == 0:
ehs_rows.append([Paragraph(c, cell_hdr) for c in row])
else:
ehs_rows.append([Paragraph(str(c), cell_body) for c in row])
ehs_t = Table(ehs_rows, colWidths=[55*mm, 70*mm, 37*mm])
ehs_ts = TableStyle([
("BACKGROUND", (0,0), (-1,0), GREEN),
("GRID", (0,0), (-1,-1), 0.4, GREY_MED),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
("LEFTPADDING", (0,0), (-1,-1), 4),
])
for i in range(1, len(ehs_rows)):
ehs_ts.add("BACKGROUND", (0,i), (-1,i), GREY_LIGHT if i % 2 == 0 else WHITE)
ehs_t.setStyle(ehs_ts)
story.append(ehs_t)
story.append(Spacer(1, 4*mm))
# ═══════════════════════════════════════════════════════════════════════════
# ── SECTION 9: EXAMINER'S SUMMARY ───────────────────────────────────────
# ═══════════════════════════════════════════════════════════════════════════
section_header("SECTION 9 — EXAMINER'S SUMMARY & PROVISIONAL DIAGNOSIS", color=colors.HexColor("#4A4A4A"))
summary_data = [
[Paragraph("<b>Type of hernia:</b>", body),
Paragraph("☐ Indirect (Lateral) ☐ Direct (Medial) ☐ Pantaloon ☐ Femoral ☐ Uncertain", body)],
[Paragraph("<b>Side:</b>", body),
Paragraph("☐ Right ☐ Left ☐ Bilateral", body)],
[Paragraph("<b>Status:</b>", body),
Paragraph("☐ Reducible ☐ Irreducible ☐ Incarcerated ☐ Strangulated", body)],
[Paragraph("<b>Complications:</b>", body),
Paragraph("☐ None ☐ Obstruction ☐ Strangulation ☐ Inflammation ☐ Hydrocele", body)],
[Paragraph("<b>EHS Classification:</b>", body),
Paragraph("_____________", body)],
[Paragraph("<b>Management plan:</b>", body),
Paragraph("☐ Watchful waiting ☐ Elective surgery ☐ Urgent surgery ☐ Emergency surgery", body)],
[Paragraph("<b>Proposed operation:</b>", body),
Paragraph("______________________________________________________________________________", body)],
[Paragraph("<b>Anaesthesia:</b>", body),
Paragraph("☐ Local ☐ Regional ☐ General", body)],
[Paragraph("<b>Examiner signature:</b>", body),
Paragraph("________________________________ Date: _________________", body)],
]
sum_t = Table(summary_data, colWidths=[38*mm, W - 28*mm - 38*mm])
sum_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), GREY_LIGHT),
("GRID", (0,0), (-1,-1), 0.4, GREY_MED),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
]))
story.append(sum_t)
story.append(Spacer(1, 3*mm))
# Notes box
notes_data = [[Paragraph("<b>Clinical Notes / Additional Findings:</b>", body)],
[Paragraph("\n\n\n\n\n\n", body)]]
notes_t = Table(notes_data, colWidths=[W - 28*mm])
notes_t.setStyle(TableStyle([
("BOX", (0,0), (-1,-1), 0.8, DARK_BLUE),
("BACKGROUND", (0,0), (-1,0), LIGHT_BLUE),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
("LINEBELOW", (0,0), (-1,0), 0.5, MID_BLUE),
]))
story.append(notes_t)
# ─── Build ───────────────────────────────────────────────────────────────
doc.build(story, onFirstPage=header_footer, onLaterPages=header_footer)
print(f"PDF created: {OUTPUT}")
if __name__ == "__main__":
build_pdf()
cd /tmp/workspace/inguinal-hernia-checklist && python generate_checklist.py 2>&1
~/inguinal-hernia-checklist/generate_checklist.py
Successfully replaced 1 instance(s) of the string in '/tmp/workspace/inguinal-hernia-checklist/generate_checklist.py'
cd /tmp/workspace/inguinal-hernia-checklist && python generate_checklist.py 2>&1
~/inguinal-hernia-checklist/generate_checklist.py
Successfully replaced 1 instance(s) of the string in '/tmp/workspace/inguinal-hernia-checklist/generate_checklist.py'
~/inguinal-hernia-checklist/generate_checklist.py
Successfully replaced 1 instance(s) of the string in '/tmp/workspace/inguinal-hernia-checklist/generate_checklist.py'
~/inguinal-hernia-checklist/generate_checklist.py
Successfully replaced 1 instance(s) of the string in '/tmp/workspace/inguinal-hernia-checklist/generate_checklist.py'
~/inguinal-hernia-checklist/generate_checklist.py
Successfully replaced 1 instance(s) of the string in '/tmp/workspace/inguinal-hernia-checklist/generate_checklist.py'