Make notes on Hydatid Cyst for Surgical residency final exam
Sources: Sabiston Textbook of Surgery (21st ed.), Bailey & Love's Short Practice of Surgery (28th ed.), Maingot's Abdominal Operations (12th ed.)
| Layer | Name | Characteristics |
|---|---|---|
| Outermost (host-derived) | Pericyst (Exocyst/Adventitia) | Fibrous capsule from host reaction; blood vessels and bile ducts become incorporated as it grows; calcifies over time |
| Middle | Ectocyst (Laminated membrane) | Bluish-white, gelatinous, ~0.5 cm thick; chitinous structure without nuclei; acts as bacterial barrier and protein ultrafilter |
| Inner (parasite-derived) | Endocyst (Germinal/Germinal layer) | 10-25 μm thick; produces hydatid fluid, ectocyst, brood capsules, scoleces, and daughter cysts |
| Site | Frequency |
|---|---|
| Liver | 50-75% (most common) |
| Lungs | 20-25% |
| Kidney, spleen, brain, bone | 5-10% |

| Stage | Description |
|---|---|
| CL | Unilocular anechoic cyst, no internal echoes or septations |
| CE1 | Anechoic cyst with fine internal echoes (hydatid sand) |
| CE2 | Internal septations; multivesicular/honeycomb/cartwheel/rosette formation |
| CE3a | Daughter cysts with detached laminated membrane |
| CE3b | Daughter cysts inside solid matrix (transitional) |
| CE4 | No visible daughter cysts; hypo/hyperechoic mixture ("bag of wool") - inactive |
| CE5 | Partial or complete calcification of wall - inactive/degenerative |
Important: FNAC/percutaneous aspiration was historically contraindicated (risk of anaphylaxis + spillage), but PAIR technique has superseded this dogma.
| Feature | Pyogenic | Amebic | Hydatid | Congenital | Cystadenoma |
|---|---|---|---|---|---|
| Number | Single/multiple | One or few | Usually single | Single/multiple | Single with loculations |
| Wall | Thick | Thick | Thick | Thin | Variable |
| Contents | Pus with blood | Red-brown "anchovy paste" | Clear/bilious, gelatinous | Clear, water density | Green-brown mucinous |
| Wall character | Uniform/multiloculated | Usually uniform | Uniform, daughter cysts; 50% calcified | Uniform | Septations common |

| Agent | Concentration | Notes |
|---|---|---|
| Hypertonic saline | 15-20% | Most commonly used |
| Ethanol | 75-95% | Effective |
| Povidone-iodine | 5% (some use 10%) | Risk of sclerosing cholangitis if biliary communication |
| Saline + Ethanol combination | 30% + 95% | Used for PAIR |
| Point | Key Fact |
|---|---|
| Most common cause of liver cysts worldwide | Echinococcal infection |
| Definitive host | Dog |
| Accidental intermediate host | Humans |
| Most common organ affected | Liver (50-75%), right lobe (75%) |
| Second most common organ | Lungs (20-25%) |
| Most common presenting symptom | Abdominal pain |
| Most frequent clinical sign | Hepatomegaly |
| Emergency presentation | Anaphylactic shock from rupture |
| Best imaging modality | CT scan (for planning); USG (worldwide, first choice) |
| Pathognomonic USG sign | Daughter cysts / "rosette" / floating membrane ("water lily sign") |
| Calcification | Does NOT always mean dead cyst |
| Drug of choice | Albendazole |
| Scolicidal agent of choice | Hypertonic saline 15-20% |
| PAIR contraindication | Biliary communication |
| Risk of scolicidal agent in biliary communication | Sclerosing cholangitis |
| Surgery preparation | Albendazole pre-op + pack off abdomen |
| Recurrence rate | 1-20% (<5% in expert centers) |
| E. multilocularis vs. granulosus | Alveolar, invasive, no pericyst, often lethal; ectogenic vesiculation |
| Hydatid sand | 400,000 scoleces/mL of fluid |
| "Bag of wool" appearance on USG | CE4 - inactive/degenerative stage |
Create a printable PDF quick-reference card for exam day
ls -la /home/daytona/workspace/ 2>/dev/null || echo "workspace empty or not found"
mkdir -p /home/daytona/workspace/hydatid-cyst && python3 -c "from reportlab.lib import colors; print('reportlab ok')" && python3 -c "from reportlab.platypus import SimpleDocTemplate; print('platypus ok')"
~/hydatid-cyst/make_card.py
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import mm, cm
from reportlab.lib.styles import ParagraphStyle, getSampleStyleSheet
from reportlab.lib.enums import TA_LEFT, TA_CENTER
from reportlab.platypus import (
BaseDocTemplate, Frame, PageTemplate, Paragraph,
Table, TableStyle, Spacer, HRFlowable, KeepTogether
)
from reportlab.platypus.flowables import BalancedColumns
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
import os
OUTPUT = "/home/daytona/workspace/hydatid-cyst/Hydatid_Cyst_Quick_Reference.pdf"
# ─── Colour palette ───────────────────────────────────────────────────────────
NAVY = colors.HexColor("#1a3a5c")
TEAL = colors.HexColor("#0d7377")
AMBER = colors.HexColor("#e8a020")
RED = colors.HexColor("#c0392b")
LGRAY = colors.HexColor("#f0f4f8")
MGRAY = colors.HexColor("#d0d8e4")
WHITE = colors.white
BLACK = colors.HexColor("#1a1a1a")
GREEN = colors.HexColor("#1a7a4a")
# ─── Page setup ───────────────────────────────────────────────────────────────
PAGE_W, PAGE_H = A4 # 210 × 297 mm
MARGIN_T = 22*mm
MARGIN_B = 16*mm
MARGIN_L = 12*mm
MARGIN_R = 12*mm
COL_GAP = 5*mm
COL_W = (PAGE_W - MARGIN_L - MARGIN_R - COL_GAP) / 2
# ─── Styles ───────────────────────────────────────────────────────────────────
def make_styles():
S = {}
base = dict(fontName="Helvetica", fontSize=7, leading=9.5, textColor=BLACK)
S["h_section"] = ParagraphStyle("h_section",
fontName="Helvetica-Bold", fontSize=8, leading=10,
textColor=WHITE, spaceAfter=2)
S["h_sub"] = ParagraphStyle("h_sub",
fontName="Helvetica-Bold", fontSize=7, leading=9,
textColor=NAVY, spaceBefore=3, spaceAfter=1)
S["body"] = ParagraphStyle("body", **base)
S["bullet"] = ParagraphStyle("bullet",
fontName="Helvetica", fontSize=7, leading=9.5,
textColor=BLACK, leftIndent=8, firstLineIndent=-6)
S["key"] = ParagraphStyle("key",
fontName="Helvetica-Bold", fontSize=7, leading=9.5, textColor=NAVY)
S["warn"] = ParagraphStyle("warn",
fontName="Helvetica-Bold", fontSize=6.5, leading=8.5,
textColor=RED, spaceBefore=2)
S["small"] = ParagraphStyle("small",
fontName="Helvetica", fontSize=6, leading=8, textColor=colors.HexColor("#555555"))
S["title"] = ParagraphStyle("title",
fontName="Helvetica-Bold", fontSize=14, leading=17,
textColor=WHITE, alignment=TA_CENTER)
S["subtitle"] = ParagraphStyle("subtitle",
fontName="Helvetica", fontSize=8, leading=10,
textColor=colors.HexColor("#c8d8e8"), alignment=TA_CENTER)
S["tbl_hdr"] = ParagraphStyle("tbl_hdr",
fontName="Helvetica-Bold", fontSize=6.5, leading=8, textColor=WHITE)
S["tbl_body"] = ParagraphStyle("tbl_body",
fontName="Helvetica", fontSize=6.5, leading=8, textColor=BLACK)
S["tbl_key"] = ParagraphStyle("tbl_key",
fontName="Helvetica-Bold", fontSize=6.5, leading=8, textColor=NAVY)
return S
S = make_styles()
# ─── Helper builders ──────────────────────────────────────────────────────────
def section_header(title, color=NAVY):
"""Coloured banner for a section."""
tbl = Table([[Paragraph(f" {title}", S["h_section"])]],
colWidths=[COL_W])
tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), color),
("LEFTPADDING", (0,0), (-1,-1), 4),
("RIGHTPADDING", (0,0), (-1,-1), 4),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING",(0,0), (-1,-1), 3),
("ROUNDEDCORNERS", [3]),
]))
return tbl
def bullet(text, bold_part=None):
if bold_part:
text = text.replace(bold_part, f"<b>{bold_part}</b>", 1)
return Paragraph(f"<bullet>•</bullet> {text}", S["bullet"])
def kv(key, value):
return Paragraph(f"<b>{key}:</b> {value}", S["body"])
def sp(h=2):
return Spacer(1, h*mm)
def mini_table(headers, rows, col_widths, hdr_color=NAVY):
data = [[Paragraph(h, S["tbl_hdr"]) for h in headers]]
for row in rows:
data.append([Paragraph(str(c), S["tbl_body"]) for c in row])
t = Table(data, colWidths=col_widths)
style = [
("BACKGROUND", (0,0), (-1,0), hdr_color),
("BACKGROUND", (0,1), (-1,-1), LGRAY),
("ROWBACKGROUNDS",(0,1),(-1,-1), [WHITE, LGRAY]),
("GRID", (0,0), (-1,-1), 0.3, MGRAY),
("LEFTPADDING", (0,0), (-1,-1), 3),
("RIGHTPADDING", (0,0), (-1,-1), 3),
("TOPPADDING", (0,0), (-1,-1), 2),
("BOTTOMPADDING",(0,0), (-1,-1), 2),
("VALIGN", (0,0), (-1,-1), "TOP"),
]
t.setStyle(TableStyle(style))
return t
def highlight_box(text, bg=colors.HexColor("#fff3cd"), border=AMBER):
inner = Paragraph(text, ParagraphStyle("hi", fontName="Helvetica-Bold",
fontSize=6.5, leading=8.5, textColor=BLACK))
t = Table([[inner]], colWidths=[COL_W - 4])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), bg),
("BOX", (0,0), (-1,-1), 1, border),
("LEFTPADDING", (0,0), (-1,-1), 5),
("RIGHTPADDING", (0,0), (-1,-1), 5),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING",(0,0), (-1,-1), 3),
]))
return t
def red_box(text):
return highlight_box(f"⚠ {text}", bg=colors.HexColor("#fdecea"), border=RED)
def green_box(text):
return highlight_box(f"✓ {text}", bg=colors.HexColor("#e8f5e9"), border=GREEN)
# ─── Content builders ──────────────────────────────────────────────────────────
def left_column():
items = []
# ── OVERVIEW ──────────────────────────────────────────────────────────────
items += [section_header("OVERVIEW | AETIOLOGY", NAVY), sp(1)]
items += [
kv("Disease", "Echinococcosis / Hydatid disease (Zoonosis)"),
kv("Organism", "<i>E. granulosus</i> (most common) | <i>E. multilocularis</i> (alveolar/lethal) | <i>E. ligartus</i>"),
kv("Definitive host", "Dog (tapeworm in proximal small bowel)"),
kv("Intermediate host", "Sheep (natural) | Humans (accidental, end-stage)"),
kv("Transmission", "Ingestion of ova from dog feces; contaminated water/vegetables; NO human-to-human"),
kv("Endemic", "Mediterranean, Middle East, Far East, S. America, Australia, NZ, E. Africa"),
sp(1),
]
# ── LIFE CYCLE ────────────────────────────────────────────────────────────
items += [section_header("LIFE CYCLE", TEAL), sp(1)]
lc = [
"Dog excretes ova in feces (thousands/day)",
"Human ingests ova → duodenum → oncosphere released",
"Oncosphere penetrates mucosa → portal blood",
"Lodges in liver (1st filter) or lungs → larval stage = Hydatid cyst",
"Cyst grows 1 cm/first 6 months, then 2–3 cm/year",
]
for i, step in enumerate(lc, 1):
items.append(Paragraph(f"<b>{i}.</b> {step}", S["bullet"]))
items.append(sp(1))
# ── CYST STRUCTURE ────────────────────────────────────────────────────────
items += [section_header("CYST WALL STRUCTURE (3 Layers)", colors.HexColor("#5b2d8e")), sp(1)]
rows = [
["<b>Pericyst</b> (outermost)", "Host-derived fibrous capsule; bile ducts + vessels incorporated; calcifies over time"],
["<b>Ectocyst</b> (middle)", "Bluish-white chitinous laminated membrane, ~0.5 cm; bacterial barrier; no nuclei"],
["<b>Endocyst</b> (inner)", "Germinal layer (10–25 µm); produces fluid, brood capsules, scoleces, daughter cysts"],
]
items += [mini_table(["Layer", "Description"], rows, [COL_W*0.32, COL_W*0.68], colors.HexColor("#5b2d8e")), sp(1)]
items += [
Paragraph("<b>Hydatid sand</b> = brood capsules + protoscoleces + calcareous bodies (~400,000 scoleces/mL)", S["body"]),
Paragraph("<b>Daughter cysts</b> = replicas of mother cyst (endogenic vesiculation)", S["body"]),
Paragraph("<b>E. multilocularis</b>: ectogenic vesiculation → invasive, multilocular, necrotic centre, often lethal", S["warn"]),
sp(1),
]
# ── DISTRIBUTION ──────────────────────────────────────────────────────────
items += [section_header("DISTRIBUTION", colors.HexColor("#2e7d32")), sp(1)]
rows2 = [
["Liver", "50–75%", "Right lobe 75% | Solitary 75%"],
["Lungs", "20–25%", "Second most common"],
["Other (kidney, spleen, brain, bone)", "5–10%", "—"],
]
items += [mini_table(["Site", "Frequency", "Notes"], rows2,
[COL_W*0.45, COL_W*0.2, COL_W*0.35], colors.HexColor("#2e7d32")), sp(1)]
items += [green_box("Most common cause of liver cysts worldwide"), sp(1)]
# ── CLINICAL FEATURES ─────────────────────────────────────────────────────
items += [section_header("CLINICAL FEATURES", colors.HexColor("#b71c1c")), sp(1)]
items += [
Paragraph("<b>Usually asymptomatic until complications occur</b> | Age ~45 yrs | Equal sex distribution", S["body"]),
sp(1),
Paragraph("<b>Elective presentation:</b>", S["h_sub"]),
bullet("Abdominal pain (most common symptom)"),
bullet("Dyspepsia, vomiting"),
bullet("Hepatomegaly (most frequent sign)"),
bullet("Jaundice ~8% | Fever ~8%"),
sp(1),
Paragraph("<b>Emergency presentations:</b>", S["h_sub"]),
bullet("Anaphylactic shock from cyst rupture (potentially fatal)"),
bullet("Coughing white material (scoleces) from hepato-bronchial rupture"),
bullet("Severe abdominal pain after minor trauma"),
sp(1),
Paragraph("<b>Complications:</b>", S["h_sub"]),
bullet("Biliary rupture → biliary colic, jaundice, cholangitis"),
bullet("Bronchial rupture → haemoptysis, cough"),
bullet("Peritoneal/pleural/pericardial rupture → disseminated echinococcosis"),
bullet("Bacterial superinfection → mimics pyogenic abscess"),
bullet("Calcification (does NOT always = dead cyst)"),
sp(1),
]
return items
def right_column():
items = []
# ── INVESTIGATIONS ────────────────────────────────────────────────────────
items += [section_header("INVESTIGATIONS", TEAL), sp(1)]
items += [
Paragraph("<b>Blood:</b> Eosinophilia | ELISA | Immunoelectrophoresis (low sensitivity/specificity)", S["body"]),
sp(1),
Paragraph("<b>USG</b> (1st choice worldwide — ~90% specificity):", S["h_sub"]),
bullet("Hydatid sand (free-floating hyperechogenic foci)"),
bullet('"Water lily sign" = floating detached laminated membrane'),
bullet("Rosette / honeycomb / cartwheel = daughter cysts"),
bullet("Eggshell calcification"),
sp(1),
]
# ── USG STAGING TABLE ─────────────────────────────────────────────────────
items += [Paragraph("<b>WHO/Gharbi USG Staging (CE Classification):</b>", S["h_sub"])]
stg_rows = [
["CL", "Unilocular anechoic, no septations"],
["CE1", "Fine internal echoes — hydatid sand"],
["CE2", "Septations — honeycomb/rosette (daughter cysts)"],
["CE3a", "Detached laminated membrane (water lily)"],
["CE3b", "Daughter cysts in solid matrix"],
["CE4", '"Bag of wool" — inactive, no daughter cysts'],
["CE5", "Partial/complete calcification — inactive"],
]
items += [mini_table(["Stage", "USG Description"], stg_rows,
[COL_W*0.18, COL_W*0.82], TEAL), sp(1)]
items += [
Paragraph("<b>CT</b> (best for planning): smooth SOL + septa; daughter cysts; depth/location; calcification", S["body"]),
Paragraph("<b>MRI/MRCP</b>: biliary communication; daughter cysts communicating with biliary tree", S["body"]),
Paragraph("<b>ERCP</b>: delineate biliary anatomy; drain biliary tree pre-op", S["body"]),
sp(1),
]
# ── DIFF DIAGNOSIS TABLE ──────────────────────────────────────────────────
items += [section_header("DIFF Dx — LIVER CYSTS", colors.HexColor("#4a148c")), sp(1)]
dd_rows = [
["<b>Hydatid</b>", "Thick", "Clear/bilious; daughter cysts; 50% calcified"],
["Pyogenic", "Thick", "Pus with blood; single/multiple"],
["Amebic", "Thick", "Anchovy paste (red-brown)"],
["Congenital", "Thin", "Clear, water density"],
["Cystadenoma", "Variable", "Green-brown mucinous; septations"],
]
items += [mini_table(["Type", "Wall", "Contents/Features"], dd_rows,
[COL_W*0.28, COL_W*0.15, COL_W*0.57], colors.HexColor("#4a148c")), sp(1)]
# ── TREATMENT ─────────────────────────────────────────────────────────────
items += [section_header("TREATMENT ALGORITHM", RED), sp(1)]
items += [
Paragraph("<b>Inactive cysts (CE4/CE5) or small <4 cm, deep, asymptomatic</b> → Monitor with USG", S["body"]),
Paragraph("<b>Active cysts</b> → Albendazole (≥3 months pre-op) → PAIR or Surgery", S["body"]),
Paragraph("<b>Elderly + densely calcified + asymptomatic</b> → Conservative management", S["body"]),
sp(1),
]
# PAIR
items += [Paragraph("<b>PAIR</b> (Puncture, Aspiration, Injection, Re-aspiration):", S["h_sub"])]
items += [
bullet("USG/CT-guided puncture → aspirate contents → inject contrast → infuse scolicidal agent → re-aspirate"),
bullet("Scolicidal agents: 15–20% hypertonic saline (most common) | 95% ethanol | 5% povidone-iodine"),
red_box("CONTRAINDICATED if biliary communication present → risk of sclerosing cholangitis"),
sp(1),
]
# Surgery
items += [Paragraph("<b>Surgical Treatment:</b>", S["h_sub"])]
surg_rows = [
["Conservative (drainage)", "Marsupialization/deroofing + omentoplasty; evacuation + drainage; laparoscopic deroofing"],
["Radical (resection)", "Total/partial pericystectomy; hepatic segmentectomy; partial hepatectomy"],
]
items += [mini_table(["Approach", "Procedures"], surg_rows,
[COL_W*0.3, COL_W*0.7], RED), sp(1)]
items += [
Paragraph("<b>Operative principles:</b>", S["h_sub"]),
bullet("Pack off abdomen before opening cyst (anaphylaxis + seeding risk)"),
bullet("Aspiration via closed suction → scolicidal flush (hypertonic saline 15–20%)"),
bullet("Epinephrine + steroids on standby for anaphylaxis"),
bullet("Biliary communication: suture repair ± ERCP post-op ± common bile duct approach"),
bullet("Laparoscopic approach increasingly used — CT mandatory for planning"),
Paragraph("<b>Recurrence:</b> 1–20%; <5% in expert centres", S["body"]),
sp(1),
]
# ── MEDICAL THERAPY ───────────────────────────────────────────────────────
items += [section_header("MEDICAL THERAPY & SCOLICIDAL AGENTS", colors.HexColor("#01579b")), sp(1)]
med_rows = [
["<b>Albendazole</b>", "Drug of choice", "Well absorbed; liver-activated; ≥3 mo pre-op; ≥1 mo post-op if spillage"],
["Mebendazole", "2nd choice", "Poorly absorbed; liver-inactivated"],
["Praziquantel", "Adjunct", "Named-patient basis"],
]
items += [mini_table(["Drug", "Role", "Notes"], med_rows,
[COL_W*0.28, COL_W*0.2, COL_W*0.52], colors.HexColor("#01579b")), sp(1)]
items += [Paragraph("<b>Alone: only ~30% successful</b> — always combine with definitive treatment", S["warn"]), sp(1)]
# ── HIGH YIELD ────────────────────────────────────────────────────────────
items += [section_header("HIGH-YIELD EXAM FACTS 🎯", AMBER), sp(1)]
hy = [
("<b>Definitive host</b>", "Dog"),
("<b>Accidental intermediate host</b>", "Humans"),
("<b>#1 organ affected</b>", "Liver (right lobe 75%)"),
("<b>#2 organ</b>", "Lungs"),
("<b>Most common cause of liver cysts worldwide</b>", "Echinococcal infection"),
("<b>Pathognomonic USG sign</b>", "Daughter cysts / rosette / water lily sign"),
("<b>Calcification</b>", "Does NOT always = dead cyst"),
("<b>Drug of choice</b>", "Albendazole"),
("<b>Best scolicidal agent</b>", "Hypertonic saline 15–20%"),
("<b>PAIR contraindication</b>", "Biliary communication"),
("<b>Emergency presentation</b>", "Anaphylactic shock from rupture"),
("<b>Hydatid sand</b>", "~400,000 scoleces/mL"),
("<b>CE4 USG</b>", '"Bag of wool" = inactive'),
("<b>E. multilocularis</b>", "Ectogenic vesiculation; invasive; often lethal"),
]
hy_data = [[Paragraph(k, S["tbl_key"]), Paragraph(v, S["tbl_body"])] for k, v in hy]
hy_tbl = Table(hy_data, colWidths=[COL_W*0.45, COL_W*0.55])
hy_tbl.setStyle(TableStyle([
("ROWBACKGROUNDS", (0,0), (-1,-1), [WHITE, LGRAY]),
("GRID", (0,0), (-1,-1), 0.3, MGRAY),
("LEFTPADDING", (0,0), (-1,-1), 3),
("RIGHTPADDING", (0,0), (-1,-1), 3),
("TOPPADDING", (0,0), (-1,-1), 2),
("BOTTOMPADDING", (0,0), (-1,-1), 2),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
items.append(hy_tbl)
items.append(sp(1))
# ── PREVENTION ────────────────────────────────────────────────────────────
items += [section_header("PREVENTION", colors.HexColor("#2e7d32")), sp(1)]
items += [
bullet("Wash hands after contact with dogs"),
bullet("Avoid vegetables grown at ground level in endemic areas"),
bullet("Do NOT feed animal entrails to dogs (breaks life cycle)"),
]
return items
# ─── Document assembly ────────────────────────────────────────────────────────
def build_header(canvas, doc):
canvas.saveState()
# Header banner
canvas.setFillColor(NAVY)
canvas.rect(0, PAGE_H - 18*mm, PAGE_W, 18*mm, fill=1, stroke=0)
canvas.setFillColor(AMBER)
canvas.rect(0, PAGE_H - 20*mm, PAGE_W, 2*mm, fill=1, stroke=0)
canvas.setFont("Helvetica-Bold", 14)
canvas.setFillColor(WHITE)
canvas.drawCentredString(PAGE_W/2, PAGE_H - 10*mm, "HYDATID CYST | Quick Reference Card")
canvas.setFont("Helvetica", 7.5)
canvas.setFillColor(colors.HexColor("#a0c4e0"))
canvas.drawCentredString(PAGE_W/2, PAGE_H - 15*mm,
"Surgical Residency Final Exam • Echinococcosis / Hydatid Disease • Sources: Sabiston | Bailey & Love | Maingot's")
# Footer
canvas.setFillColor(NAVY)
canvas.rect(0, 0, PAGE_W, 10*mm, fill=1, stroke=0)
canvas.setFont("Helvetica", 6)
canvas.setFillColor(colors.HexColor("#a0c4e0"))
canvas.drawString(MARGIN_L, 3.5*mm, "Orris Medical • For educational use only • Verify all doses/guidelines with current references")
canvas.drawRightString(PAGE_W - MARGIN_R, 3.5*mm, "July 2026")
# Vertical divider
canvas.setStrokeColor(MGRAY)
canvas.setLineWidth(0.5)
mid_x = MARGIN_L + COL_W + COL_GAP/2
canvas.line(mid_x, MARGIN_B + 2*mm, mid_x, PAGE_H - MARGIN_T - 2*mm)
canvas.restoreState()
def main():
doc = BaseDocTemplate(OUTPUT,
pagesize=A4,
leftMargin=MARGIN_L,
rightMargin=MARGIN_R,
topMargin=MARGIN_T,
bottomMargin=MARGIN_B)
left_frame = Frame(MARGIN_L, MARGIN_B,
COL_W, PAGE_H - MARGIN_T - MARGIN_B,
leftPadding=0, rightPadding=0,
topPadding=0, bottomPadding=0,
id="left")
right_frame = Frame(MARGIN_L + COL_W + COL_GAP, MARGIN_B,
COL_W, PAGE_H - MARGIN_T - MARGIN_B,
leftPadding=0, rightPadding=0,
topPadding=0, bottomPadding=0,
id="right")
doc.addPageTemplates([PageTemplate(id="two_col",
frames=[left_frame, right_frame],
onPage=build_header)])
story = left_column()
# Signal column break (FrameBreak equivalent via empty frame usage)
from reportlab.platypus import FrameBreak
story.append(FrameBreak())
story += right_column()
doc.build(story)
print(f"PDF created: {OUTPUT}")
if __name__ == "__main__":
main()
cd /home/daytona/workspace/hydatid-cyst && python3 make_card.py 2>&1
ls -lh /home/daytona/workspace/hydatid-cyst/Hydatid_Cyst_Quick_Reference.pdf
cd /home/daytona/workspace/hydatid-cyst && python3 -c " from pypdf import PdfReader r = PdfReader('Hydatid_Cyst_Quick_Reference.pdf') print(f'Pages: {len(r.pages)}') for i, p in enumerate(r.pages): t = p.extract_text() print(f'Page {i+1}: {len(t)} chars') print(t[:200]) print('---') "
~/hydatid-cyst/make_card.py
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.lib.enums import TA_CENTER
from reportlab.platypus import (
BaseDocTemplate, Frame, PageTemplate, Paragraph,
Table, TableStyle, Spacer, FrameBreak
)
OUTPUT = "/home/daytona/workspace/hydatid-cyst/Hydatid_Cyst_Quick_Reference.pdf"
# ── Colour palette ─────────────────────────────────────────────────────────────
NAVY = colors.HexColor("#1a3a5c")
TEAL = colors.HexColor("#0d7377")
AMBER = colors.HexColor("#c8860e")
RED = colors.HexColor("#b71c1c")
PURPLE = colors.HexColor("#4a148c")
DGREEN = colors.HexColor("#1b5e20")
BLUE = colors.HexColor("#01579b")
LGRAY = colors.HexColor("#f2f5f9")
MGRAY = colors.HexColor("#ccd5e0")
WHITE = colors.white
BLACK = colors.HexColor("#111111")
WARN = colors.HexColor("#fdecea")
WARNB = colors.HexColor("#b71c1c")
HILY = colors.HexColor("#fff8e1")
# ── Page geometry ──────────────────────────────────────────────────────────────
PAGE_W, PAGE_H = A4
MT = 18*mm # top margin (under header)
MB = 9*mm # bottom margin (above footer)
ML = 10*mm
MR = 10*mm
GAP = 4*mm
COL_W = (PAGE_W - ML - MR - GAP) / 2
# ── Styles ─────────────────────────────────────────────────────────────────────
def S(name, **kw):
defaults = dict(fontName="Helvetica", fontSize=6.4, leading=8.2, textColor=BLACK)
defaults.update(kw)
return ParagraphStyle(name, **defaults)
s_body = S("body")
s_bullet = S("bullet", leftIndent=7, firstLineIndent=-5)
s_bold = S("bold", fontName="Helvetica-Bold")
s_hdr = S("hdr", fontName="Helvetica-Bold", fontSize=7.2, leading=9, textColor=WHITE)
s_sub = S("sub", fontName="Helvetica-Bold", fontSize=6.5, leading=8, textColor=NAVY, spaceBefore=2, spaceAfter=1)
s_warn = S("warn", fontName="Helvetica-Bold", fontSize=6.2, leading=7.8, textColor=RED)
s_th = S("th", fontName="Helvetica-Bold", fontSize=6.2, leading=7.5, textColor=WHITE)
s_td = S("td", fontName="Helvetica", fontSize=6.2, leading=7.5, textColor=BLACK)
s_tdkey = S("tdkey", fontName="Helvetica-Bold", fontSize=6.2, leading=7.5, textColor=NAVY)
def sp(h=1.5): return Spacer(1, h*mm)
def sec(title, color=NAVY):
t = Table([[Paragraph(f" {title}", s_hdr)]], colWidths=[COL_W])
t.setStyle(TableStyle([
("BACKGROUND", (0,0),(-1,-1), color),
("LEFTPADDING", (0,0),(-1,-1), 3),
("RIGHTPADDING", (0,0),(-1,-1), 3),
("TOPPADDING", (0,0),(-1,-1), 2),
("BOTTOMPADDING", (0,0),(-1,-1), 2),
]))
return t
def tbl(headers, rows, widths, hcolor=NAVY):
data = [[Paragraph(h, s_th) for h in headers]]
for r in rows:
data.append([Paragraph(str(c), s_td) for c in r])
t = Table(data, colWidths=widths)
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1, 0), hcolor),
("ROWBACKGROUNDS",(0,1), (-1,-1), [WHITE, LGRAY]),
("GRID", (0,0), (-1,-1), 0.25, MGRAY),
("LEFTPADDING", (0,0), (-1,-1), 2),
("RIGHTPADDING", (0,0), (-1,-1), 2),
("TOPPADDING", (0,0), (-1,-1), 1.5),
("BOTTOMPADDING", (0,0), (-1,-1), 1.5),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
return t
def kv_tbl(rows, c1w=0.42):
data = [[Paragraph(k, s_tdkey), Paragraph(v, s_td)] for k,v in rows]
t = Table(data, colWidths=[COL_W*c1w, COL_W*(1-c1w)])
t.setStyle(TableStyle([
("ROWBACKGROUNDS",(0,0),(-1,-1),[WHITE,LGRAY]),
("GRID", (0,0),(-1,-1), 0.25, MGRAY),
("LEFTPADDING", (0,0),(-1,-1), 2),
("RIGHTPADDING", (0,0),(-1,-1), 2),
("TOPPADDING", (0,0),(-1,-1), 1.5),
("BOTTOMPADDING",(0,0),(-1,-1), 1.5),
("VALIGN", (0,0),(-1,-1), "TOP"),
]))
return t
def box(text, bg=HILY, bc=AMBER, style=None):
st = style or S("bx", fontName="Helvetica-Bold", fontSize=6.2, leading=7.8, textColor=BLACK)
inner = Table([[Paragraph(text, st)]], colWidths=[COL_W - 6])
inner.setStyle(TableStyle([
("BACKGROUND", (0,0),(-1,-1), bg),
("BOX", (0,0),(-1,-1), 0.8, bc),
("LEFTPADDING", (0,0),(-1,-1), 4),
("RIGHTPADDING", (0,0),(-1,-1), 4),
("TOPPADDING", (0,0),(-1,-1), 2),
("BOTTOMPADDING",(0,0),(-1,-1), 2),
]))
return inner
def rbox(text): return box(f"⚠ {text}", bg=WARN, bc=WARNB,
style=S("rb", fontName="Helvetica-Bold", fontSize=6.2, leading=7.8, textColor=RED))
def b(text): return Paragraph(f"• {text}", s_bullet)
def p(text): return Paragraph(text, s_body)
# ── Left column ────────────────────────────────────────────────────────────────
def left_col():
items = []
# OVERVIEW
items += [sec("OVERVIEW | AETIOLOGY", NAVY), sp(1)]
items += [kv_tbl([
("<b>Disease</b>", "Echinococcosis / Hydatid disease (Zoonosis)"),
("<b>Organism</b>", "<i>E. granulosus</i> (most common) | <i>E. multilocularis</i> (alveolar, lethal) | <i>E. ligartus</i>"),
("<b>Definitive host</b>", "Dog — adult tapeworm in proximal small bowel; thousands of ova/day"),
("<b>Intermediate host</b>","Sheep (natural) | Humans (accidental, end-stage — no human-to-human transmission)"),
("<b>Transmission</b>", "Ingestion of ova from dog feces; contaminated water/vegetables"),
("<b>Endemic regions</b>", "Mediterranean, Middle East, Far East, S. America, Australia, NZ, E. Africa"),
]), sp(1)]
# LIFE CYCLE
items += [sec("LIFE CYCLE", TEAL), sp(1)]
for i, step in enumerate([
"Dog excretes ova in feces",
"Human ingests ova → duodenum → oncosphere released",
"Oncosphere penetrates mucosa → portal blood",
"Lodges in liver (1st filter) or lungs → larval stage = Hydatid cyst",
"Grows 1 cm/first 6 months, then 2–3 cm/year thereafter",
], 1):
items.append(Paragraph(f"<b>{i}.</b> {step}", s_bullet))
items.append(sp(1))
# CYST STRUCTURE
items += [sec("CYST WALL — 3 LAYERS", PURPLE), sp(1)]
items += [tbl(["Layer", "Description"],
[["<b>Pericyst</b> (outermost — host)", "Fibrous adventitial reaction; bile ducts + vessels incorporated; calcifies over time"],
["<b>Ectocyst</b> (middle)", "Chitinous laminated membrane ~0.5 cm; bluish-white; bacterial barrier; no nuclei"],
["<b>Endocyst</b> (inner — parasite)", "Germinal layer 10–25 µm; produces fluid, brood capsules, scoleces, daughter cysts"]],
[COL_W*0.32, COL_W*0.68], PURPLE), sp(0.8)]
items += [
p("• <b>Hydatid sand</b> = brood capsules + protoscoleces + calcareous bodies (~400,000 scoleces/mL)"),
p("• <b>Daughter cysts</b> = replicas of mother cyst (endogenic vesiculation from germinal layer)"),
p("• <b>E. multilocularis</b>: ectogenic vesiculation → invasive, multilocular, necrotic centre, often lethal"),
sp(1),
]
# DISTRIBUTION
items += [sec("DISTRIBUTION", DGREEN), sp(1)]
items += [tbl(["Site", "Freq.", "Notes"],
[["Liver", "50–75%", "Right lobe 75% | Solitary 75% | 50% of solitary have daughter cysts"],
["Lungs", "20–25%", "2nd most common"],
["Other (kidney, spleen, brain, bone)", "5–10%", "—"]],
[COL_W*0.42, COL_W*0.14, COL_W*0.44], DGREEN), sp(0.8)]
items += [box("<b>Most common cause of liver cysts worldwide</b> • Echinococcal infection"), sp(1)]
# CLINICAL FEATURES
items += [sec("CLINICAL FEATURES", RED), sp(1)]
items += [p("<b>Usually asymptomatic until complications</b> | Age ~45 yrs | Equal M:F"), sp(0.5)]
items += [p("<b>Elective:</b> Abdominal pain (most common) • Dyspepsia/vomiting • Hepatomegaly (most frequent sign) • Jaundice ~8% • Fever ~8%")]
items += [sp(0.5), p("<b>Emergency:</b> Anaphylactic shock from rupture (potentially fatal) • Coughing scoleces from hepato-bronchial rupture • Severe pain after minor trauma")]
items += [sp(0.5), p("<b>Complications:</b> Biliary rupture → jaundice/cholangitis • Bronchial rupture → haemoptysis • Peritoneal rupture → disseminated disease • Bacterial superinfection (mimics pyogenic abscess) • Calcification (does NOT always = dead cyst)")]
items.append(sp(1))
return items
# ── Right column ───────────────────────────────────────────────────────────────
def right_col():
items = []
# INVESTIGATIONS
items += [sec("INVESTIGATIONS", TEAL), sp(1)]
items += [p("<b>Blood:</b> Eosinophilia | ELISA | Immunoelectrophoresis (limited sensitivity/specificity)"), sp(0.5)]
items += [p("<b>USG</b> (1st choice worldwide, ~90% specificity): hydatid sand (hyperechoic foci) • 'Water lily sign' (floating membrane) • Rosette/honeycomb/cartwheel (daughter cysts) • Eggshell calcification"), sp(0.8)]
# USG STAGING
items += [p("<b>WHO/Gharbi CE Staging:</b>")]
items += [tbl(["Stage", "USG Description", "Status"],
[["CL", "Unilocular anechoic, no septations", "Active"],
["CE1", "Fine echoes — hydatid sand", "Active"],
["CE2", "Honeycomb/rosette — daughter cysts", "Active"],
["CE3a", "Detached laminated membrane (water lily)", "Transitional"],
["CE3b", "Daughter cysts in solid matrix", "Transitional"],
["CE4", '"Bag of wool" — no daughter cysts', "Inactive"],
["CE5", "Partial/complete calcification", "Inactive"]],
[COL_W*0.15, COL_W*0.63, COL_W*0.22], TEAL), sp(0.8)]
items += [
p("<b>CT</b> (best for planning): smooth SOL + septa; daughter cysts; calcification; depth/location"),
p("<b>MRCP</b>: biliary communication • <b>ERCP</b>: delineate + drain biliary tree pre-op"),
sp(1),
]
# DIFF Dx
items += [sec("DIFF Dx — LIVER CYSTS", PURPLE), sp(1)]
items += [tbl(["Type", "Wall", "Contents / Key Feature"],
[["<b>Hydatid</b>", "Thick", "Clear/bilious; daughter cysts; 50% calcified"],
["Pyogenic", "Thick", "Pus + blood; single/multiple; fever, ↑WCC"],
["Amebic", "Thick", "Anchovy paste (red-brown); travel Hx"],
["Congenital", "Thin", "Clear, water density; smooth"],
["Cystadenoma", "Variable", "Green-brown mucinous; irregular septations"]],
[COL_W*0.26, COL_W*0.14, COL_W*0.60], PURPLE), sp(1)]
# TREATMENT
items += [sec("TREATMENT ALGORITHM", RED), sp(1)]
items += [
p("<b>Inactive (CE4/CE5) or small <4 cm deep asymptomatic</b> → Watchful waiting + USG monitoring"),
p("<b>Elderly + densely calcified + asymptomatic</b> → Conservative"),
p("<b>Active cysts</b> → <b>Albendazole ≥3 months pre-op</b> → PAIR or Surgery"),
sp(0.8),
p("<b>PAIR</b> (Puncture, Aspiration, Injection, Re-aspiration):"),
b("USG/CT-guided • Puncture → Aspirate → Inject contrast (check biliary comm.) → Scolicidal agent → Re-aspirate"),
b("Scolicidal agents: 15–20% hypertonic saline (1st choice) | 95% ethanol | 5% povidone-iodine"),
rbox("CONTRAINDICATED if biliary communication — risk of sclerosing cholangitis"),
sp(0.8),
]
items += [p("<b>Surgical options:</b>")]
items += [tbl(["Approach", "Procedures"],
[["Conservative (drainage)", "Marsupialization/deroofing + omentoplasty; laparoscopic deroofing; external drainage"],
["Radical (resection)", "Total/partial pericystectomy; hepatic segmentectomy; partial hepatectomy"]],
[COL_W*0.3, COL_W*0.7], RED), sp(0.8)]
items += [
p("<b>Operative principles:</b> Pack off abdomen before opening cyst (anaphylaxis + seeding) • Aspiration via closed suction • Scolicidal flush • Epinephrine + steroids on standby • Biliary communication → suture repair ± ERCP"),
p("<b>Recurrence:</b> 1–20%; <5% in expert centres"),
sp(1),
]
# MEDICAL THERAPY
items += [sec("MEDICAL THERAPY", BLUE), sp(1)]
items += [tbl(["Drug", "Notes"],
[["<b>Albendazole</b> (1st choice)", "Well absorbed; liver-activated to active metabolite; given ≥3 mo pre-op; ≥1 mo post-op if spillage/biliary rupture"],
["Mebendazole (2nd choice)", "Poorly absorbed; liver-inactivated; less effective"],
["Praziquantel (adjunct)", "Named-patient basis; combined with albendazole in some protocols"]],
[COL_W*0.32, COL_W*0.68], BLUE), sp(0.5)]
items += [rbox("Alone only ~30% successful — always combine with PAIR or surgery"), sp(1)]
# HIGH YIELD
items += [sec("HIGH-YIELD EXAM FACTS 🎯", colors.HexColor("#7b5800")), sp(1)]
items += [kv_tbl([
("<b>Definitive host</b>", "Dog"),
("<b>Accidental intermediate host</b>", "Humans (end-stage — no H-to-H transmission)"),
("<b>#1 organ affected</b>", "Liver — right lobe 75% | solitary 75%"),
("<b>#2 organ</b>", "Lungs (20–25%)"),
("<b>Pathognomonic USG</b>", "Daughter cysts / rosette / water lily sign"),
("<b>Calcification</b>", "Does NOT always mean dead cyst"),
("<b>Drug of choice</b>", "Albendazole"),
("<b>Best scolicidal agent</b>", "Hypertonic saline 15–20%"),
("<b>PAIR contraindication</b>", "Biliary communication"),
("<b>Emergency presentation</b>", "Anaphylactic shock from rupture"),
("<b>CE4 appearance</b>", '"Bag of wool" = inactive stage'),
("<b>E. multilocularis</b>", "Ectogenic vesiculation; invasive; no sequestration; often lethal"),
("<b>Prevention</b>", "Handwashing; no ground vegetables in endemic areas; don't feed entrails to dogs"),
], 0.44), sp(1)]
return items
# ── Page decorations ───────────────────────────────────────────────────────────
def draw_page(canvas, doc):
canvas.saveState()
# Header
canvas.setFillColor(NAVY)
canvas.rect(0, PAGE_H - 15*mm, PAGE_W, 15*mm, fill=1, stroke=0)
canvas.setFillColor(AMBER)
canvas.rect(0, PAGE_H - 16.5*mm, PAGE_W, 1.5*mm, fill=1, stroke=0)
canvas.setFont("Helvetica-Bold", 13)
canvas.setFillColor(WHITE)
canvas.drawCentredString(PAGE_W/2, PAGE_H - 8.5*mm, "HYDATID CYST | Quick Reference Card")
canvas.setFont("Helvetica", 6.8)
canvas.setFillColor(colors.HexColor("#90bcd8"))
canvas.drawCentredString(PAGE_W/2, PAGE_H - 13*mm,
"Surgical Residency Final Exam • Echinococcosis • Sabiston | Bailey & Love | Maingot's")
# Footer
canvas.setFillColor(NAVY)
canvas.rect(0, 0, PAGE_W, 8*mm, fill=1, stroke=0)
canvas.setFont("Helvetica", 5.8)
canvas.setFillColor(colors.HexColor("#90bcd8"))
canvas.drawString(ML, 2.8*mm, "Orris Medical • For educational use only • Verify all clinical details with current references")
canvas.drawRightString(PAGE_W - MR, 2.8*mm, "July 2026")
# Divider
mid_x = ML + COL_W + GAP/2
canvas.setStrokeColor(MGRAY)
canvas.setLineWidth(0.4)
canvas.line(mid_x, MB, mid_x, PAGE_H - MT)
canvas.restoreState()
# ── Build PDF ──────────────────────────────────────────────────────────────────
def main():
doc = BaseDocTemplate(OUTPUT, pagesize=A4,
leftMargin=ML, rightMargin=MR,
topMargin=MT, bottomMargin=MB)
f_left = Frame(ML, MB, COL_W, PAGE_H - MT - MB,
leftPadding=0, rightPadding=0, topPadding=0, bottomPadding=0, id="left")
f_right = Frame(ML+COL_W+GAP, MB, COL_W, PAGE_H - MT - MB,
leftPadding=0, rightPadding=0, topPadding=0, bottomPadding=0, id="right")
doc.addPageTemplates([PageTemplate(id="two_col",
frames=[f_left, f_right],
onPage=draw_page)])
story = left_col() + [FrameBreak()] + right_col()
doc.build(story)
print(f"Done: {OUTPUT}")
if __name__ == "__main__":
main()
~/hydatid-cyst/make_card.py
Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/hydatid-cyst/make_card.py'
cat > /home/daytona/workspace/hydatid-cyst/make_card.py << 'PYEOF' 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, Frame, PageTemplate, Paragraph, Table, TableStyle, Spacer, FrameBreak ) OUTPUT = "/home/daytona/workspace/hydatid-cyst/Hydatid_Cyst_Quick_Reference.pdf" NAVY = colors.HexColor("#1a3a5c") TEAL = colors.HexColor("#0d7377") AMBER = colors.HexColor("#c8860e") RED = colors.HexColor("#b71c1c") PURPLE = colors.HexColor("#4a148c") DGREEN = colors.HexColor("#1b5e20") BLUE = colors.HexColor("#01579b") LGRAY = colors.HexColor("#f2f5f9") MGRAY = colors.HexColor("#ccd5e0") WHITE = colors.white BLACK = colors.HexColor("#111111") WARN = colors.HexColor("#fdecea") WARNB = colors.HexColor("#b71c1c") HILY = colors.HexColor("#fff8e1") PAGE_W, PAGE_H = A4 MT = 18*mm MB = 9*mm ML = 10*mm MR = 10*mm GAP = 4*mm COL_W = (PAGE_W - ML - MR - GAP) / 2 def S(name, **kw): defaults = dict(fontName="Helvetica", fontSize=6.4, leading=8.2, textColor=BLACK) defaults.update(kw) return ParagraphStyle(name, **defaults) s_body = S("body") s_bullet = S("bullet", leftIndent=7, firstLineIndent=-5) s_hdr = S("hdr", fontName="Helvetica-Bold", fontSize=7.2, leading=9, textColor=WHITE) s_warn = S("warn", fontName="Helvetica-Bold", fontSize=6.2, leading=7.8, textColor=RED) s_th = S("th", fontName="Helvetica-Bold", fontSize=6.2, leading=7.5, textColor=WHITE) s_td = S("td", fontName="Helvetica", fontSize=6.2, leading=7.5, textColor=BLACK) s_tdkey = S("tdkey", fontName="Helvetica-Bold", fontSize=6.2, leading=7.5, textColor=NAVY) def sp(h=1.5): return Spacer(1, h*mm) def sec(title, color=NAVY): t = Table([[Paragraph(" " + title, s_hdr)]], colWidths=[COL_W]) t.setStyle(TableStyle([ ("BACKGROUND", (0,0),(-1,-1), color), ("LEFTPADDING", (0,0),(-1,-1), 3), ("RIGHTPADDING", (0,0),(-1,-1), 3), ("TOPPADDING", (0,0),(-1,-1), 2), ("BOTTOMPADDING", (0,0),(-1,-1), 2), ])) return t def tbl(headers, rows, widths, hcolor=NAVY): data = [[Paragraph(h, s_th) for h in headers]] for r in rows: data.append([Paragraph(str(c), s_td) for c in r]) t = Table(data, colWidths=widths) t.setStyle(TableStyle([ ("BACKGROUND", (0,0), (-1, 0), hcolor), ("ROWBACKGROUNDS",(0,1), (-1,-1), [WHITE, LGRAY]), ("GRID", (0,0), (-1,-1), 0.25, MGRAY), ("LEFTPADDING", (0,0), (-1,-1), 2), ("RIGHTPADDING", (0,0), (-1,-1), 2), ("TOPPADDING", (0,0), (-1,-1), 1.5), ("BOTTOMPADDING", (0,0), (-1,-1), 1.5), ("VALIGN", (0,0), (-1,-1), "TOP"), ])) return t def kv_tbl(rows, c1w=0.42): data = [[Paragraph(k, s_tdkey), Paragraph(v, s_td)] for k,v in rows] t = Table(data, colWidths=[COL_W*c1w, COL_W*(1-c1w)]) t.setStyle(TableStyle([ ("ROWBACKGROUNDS",(0,0),(-1,-1),[WHITE,LGRAY]), ("GRID", (0,0),(-1,-1), 0.25, MGRAY), ("LEFTPADDING", (0,0),(-1,-1), 2), ("RIGHTPADDING", (0,0),(-1,-1), 2), ("TOPPADDING", (0,0),(-1,-1), 1.5), ("BOTTOMPADDING",(0,0),(-1,-1), 1.5), ("VALIGN", (0,0),(-1,-1), "TOP"), ])) return t def box(text, bg=HILY, bc=AMBER, style=None): st = style or S("bx", fontName="Helvetica-Bold", fontSize=6.2, leading=7.8, textColor=BLACK) inner = Table([[Paragraph(text, st)]], colWidths=[COL_W - 6]) inner.setStyle(TableStyle([ ("BACKGROUND", (0,0),(-1,-1), bg), ("BOX", (0,0),(-1,-1), 0.8, bc), ("LEFTPADDING", (0,0),(-1,-1), 4), ("RIGHTPADDING", (0,0),(-1,-1), 4), ("TOPPADDING", (0,0),(-1,-1), 2), ("BOTTOMPADDING",(0,0),(-1,-1), 2), ])) return inner def rbox(text): return box("WARNING: " + text, bg=WARN, bc=WARNB, style=S("rb", fontName="Helvetica-Bold", fontSize=6.2, leading=7.8, textColor=RED)) def b(text): return Paragraph("* " + text, s_bullet) def p(text): return Paragraph(text, s_body) def left_col(): items = [] items += [sec("OVERVIEW | AETIOLOGY", NAVY), sp(1)] items += [kv_tbl([ ("<b>Disease</b>", "Echinococcosis / Hydatid disease (Zoonosis)"), ("<b>Organism</b>", "<i>E. granulosus</i> (most common) | <i>E. multilocularis</i> (alveolar, lethal) | <i>E. ligartus</i>"), ("<b>Definitive host</b>", "Dog — adult tapeworm in proximal small bowel; thousands of ova/day"), ("<b>Intermediate host</b>","Sheep (natural) | Humans (accidental, end-stage)"), ("<b>Transmission</b>", "Ingestion of ova from dog feces; contaminated water/vegetables; NO H-to-H"), ("<b>Endemic</b>", "Mediterranean, Middle East, Far East, S. America, Australia, NZ, E. Africa"), ]), sp(1)] items += [sec("LIFE CYCLE", TEAL), sp(1)] for i, step in enumerate([ "Dog excretes ova in feces", "Human ingests ova -> duodenum -> oncosphere released", "Oncosphere penetrates mucosa -> portal blood", "Lodges in liver (1st filter) or lungs -> larval stage = Hydatid cyst", "Grows 1 cm/first 6 months, then 2-3 cm/year", ], 1): items.append(Paragraph("<b>" + str(i) + ".</b> " + step, s_bullet)) items.append(sp(1)) items += [sec("CYST WALL -- 3 LAYERS", PURPLE), sp(1)] items += [tbl(["Layer", "Description"], [["<b>Pericyst</b> (outermost, host)", "Fibrous adventitial reaction; bile ducts + vessels incorporated; calcifies over time"], ["<b>Ectocyst</b> (middle)", "Chitinous laminated membrane ~0.5 cm; bluish-white; bacterial barrier; no nuclei"], ["<b>Endocyst</b> (inner, parasite)", "Germinal layer 10-25 um; produces fluid, brood capsules, scoleces, daughter cysts"]], [COL_W*0.32, COL_W*0.68], PURPLE), sp(0.8)] items += [ p("* <b>Hydatid sand</b> = brood capsules + protoscoleces + calcareous bodies (~400,000 scoleces/mL)"), p("* <b>Daughter cysts</b> = replicas of mother cyst (endogenic vesiculation)"), p("* <b>E. multilocularis</b>: ectogenic vesiculation -> invasive, multilocular, necrotic centre, often lethal"), sp(1), ] items += [sec("DISTRIBUTION", DGREEN), sp(1)] items += [tbl(["Site", "Freq.", "Notes"], [["Liver", "50-75%", "Right lobe 75% | Solitary 75% | 50% of solitary have daughter cysts"], ["Lungs", "20-25%", "2nd most common"], ["Other (kidney, spleen, brain)", "5-10%", "--"]], [COL_W*0.42, COL_W*0.14, COL_W*0.44], DGREEN), sp(0.8)] items += [box("<b>Most common cause of liver cysts worldwide</b> -- Echinococcal infection"), sp(1)] items += [sec("CLINICAL FEATURES", RED), sp(1)] items += [p("<b>Usually asymptomatic until complications</b> | Age ~45 yrs | Equal M:F"), sp(0.5)] items += [p("<b>Elective:</b> Abdominal pain (most common) | Dyspepsia/vomiting | Hepatomegaly (most frequent sign) | Jaundice ~8% | Fever ~8%")] items += [sp(0.5), p("<b>Emergency:</b> Anaphylactic shock from rupture | Coughing scoleces from hepato-bronchial rupture | Severe pain after minor trauma")] items += [sp(0.5), p("<b>Complications:</b> Biliary rupture (jaundice, cholangitis) | Bronchial rupture (haemoptysis) | Peritoneal rupture (disseminated disease) | Bacterial superinfection | Calcification (does NOT always = dead cyst)")] return items def right_col(): items = [] items += [sec("INVESTIGATIONS", TEAL), sp(1)] items += [ p("<b>Blood:</b> Eosinophilia | ELISA | Immunoelectrophoresis (limited sensitivity/specificity)"), sp(0.5), p("<b>USG</b> (1st choice worldwide, ~90% specificity): hydatid sand (hyperechoic foci) | Water lily sign (floating membrane) | Rosette/honeycomb (daughter cysts) | Eggshell calcification"), sp(0.8), p("<b>WHO/Gharbi CE Staging:</b>"), ] items += [tbl(["Stage", "USG Description", "Status"], [["CL", "Unilocular anechoic, no septations", "Active"], ["CE1", "Fine echoes -- hydatid sand", "Active"], ["CE2", "Honeycomb/rosette -- daughter cysts", "Active"], ["CE3a", "Detached laminated membrane (water lily)", "Transitional"], ["CE3b", "Daughter cysts in solid matrix", "Transitional"], ["CE4", "Bag of wool -- no daughter cysts", "Inactive"], ["CE5", "Partial/complete calcification", "Inactive"]], [COL_W*0.15, COL_W*0.63, COL_W*0.22], TEAL), sp(0.8)] items += [ p("<b>CT</b> (best for planning): smooth SOL + septa; daughter cysts; calcification; depth/location"), p("<b>MRCP</b>: biliary communication | <b>ERCP</b>: delineate + drain biliary tree pre-op"), sp(1), ] items += [sec("DIFF Dx -- LIVER CYSTS", PURPLE), sp(1)] items += [tbl(["Type", "Wall", "Contents / Key Feature"], [["<b>Hydatid</b>", "Thick", "Clear/bilious; daughter cysts; 50% calcified"], ["Pyogenic", "Thick", "Pus + blood; fever; raised WCC"], ["Amebic", "Thick", "Anchovy paste (red-brown); travel Hx"], ["Congenital", "Thin", "Clear, water density; smooth"], ["Cystadenoma", "Var.", "Green-brown mucinous; irregular septations"]], [COL_W*0.26, COL_W*0.14, COL_W*0.60], PURPLE), sp(1)] items += [sec("TREATMENT ALGORITHM", RED), sp(1)] items += [ p("<b>Inactive (CE4/CE5) or small <4 cm deep asymptomatic</b> --> Watchful waiting + USG monitoring"), p("<b>Active cysts</b> --> <b>Albendazole >=3 months pre-op</b> --> PAIR or Surgery"), sp(0.8), p("<b>PAIR</b> (Puncture, Aspiration, Injection, Re-aspiration):"), b("USG/CT-guided | Puncture -> Aspirate -> Inject contrast (check biliary comm.) -> Scolicidal agent -> Re-aspirate"), b("Scolicidal agents: 15-20% hypertonic saline (1st choice) | 95% ethanol | 5% povidone-iodine"), rbox("CONTRAINDICATED if biliary communication -- risk of sclerosing cholangitis"), sp(0.8), p("<b>Surgical options:</b>"), ] items += [tbl(["Approach", "Procedures"], [["Conservative", "Marsupialization/deroofing + omentoplasty; laparoscopic deroofing"], ["Radical (resection)", "Total/partial pericystectomy; hepatic segmentectomy; partial hepatectomy"]], [COL_W*0.3, COL_W*0.7], RED), sp(0.8)] items += [ p("<b>Operative principles:</b> Pack off abdomen (anaphylaxis + seeding risk) | Closed-suction aspiration | Scolicidal flush | Epinephrine + steroids on standby | Biliary communication --> suture repair +/- ERCP"), p("<b>Recurrence:</b> 1-20%; <5% in expert centres"), sp(1), ] items += [sec("MEDICAL THERAPY", BLUE), sp(1)] items += [tbl(["Drug", "Notes"], [["<b>Albendazole</b> (1st choice)", "Well absorbed; liver-activated; >=3 mo pre-op; >=1 mo post-op if spillage"], ["Mebendazole (2nd choice)", "Poorly absorbed; liver-inactivated; less effective"], ["Praziquantel (adjunct)", "Named-patient basis; used with albendazole in some protocols"]], [COL_W*0.32, COL_W*0.68], BLUE), sp(0.5)] items += [rbox("Alone only ~30% successful -- always combine with PAIR or surgery"), sp(1)] items += [sec("HIGH-YIELD EXAM FACTS", colors.HexColor("#7b5800")), sp(1)] items += [kv_tbl([ ("<b>Definitive host</b>", "Dog"), ("<b>Accidental intermediate host</b>", "Humans (end-stage -- no H-to-H)"), ("<b>#1 organ</b>", "Liver -- right lobe 75% | solitary 75%"), ("<b>#2 organ</b>", "Lungs (20-25%)"), ("<b>Most common cause liver cysts</b>","Echinococcal infection worldwide"), ("<b>Pathognomonic USG</b>", "Daughter cysts / rosette / water lily sign"), ("<b>Calcification</b>", "Does NOT always mean dead cyst"), ("<b>Drug of choice</b>", "Albendazole"), ("<b>Best scolicidal agent</b>", "Hypertonic saline 15-20%"), ("<b>PAIR contraindication</b>", "Biliary communication"), ("<b>Emergency presentation</b>", "Anaphylactic shock from rupture"), ("<b>CE4 appearance</b>", "Bag of wool = inactive stage"), ("<b>E. multilocularis</b>", "Ectogenic vesiculation; invasive; often lethal"), ("<b>Prevention</b>", "Handwashing; no ground vegetables; don't feed entrails to dogs"), ], 0.44)] return items def draw_page(canvas, doc): canvas.saveState() canvas.setFillColor(NAVY) canvas.rect(0, PAGE_H - 15*mm, PAGE_W, 15*mm, fill=1, stroke=0) canvas.setFillColor(AMBER) canvas.rect(0, PAGE_H - 16.5*mm, PAGE_W, 1.5*mm, fill=1, stroke=0) canvas.setFont("Helvetica-Bold", 13) canvas.setFillColor(WHITE) canvas.drawCentredString(PAGE_W/2, PAGE_H - 8.5*mm, "HYDATID CYST | Quick Reference Card") canvas.setFont("Helvetica", 6.8) canvas.setFillColor(colors.HexColor("#90bcd8")) canvas.drawCentredString(PAGE_W/2, PAGE_H - 13*mm, "Surgical Residency Final Exam - Echinococcosis - Sabiston | Bailey & Love | Maingot's") canvas.setFillColor(NAVY) canvas.rect(0, 0, PAGE_W, 8*mm, fill=1, stroke=0) canvas.setFont("Helvetica", 5.8) canvas.setFillColor(colors.HexColor("#90bcd8")) canvas.drawString(ML, 2.8*mm, "Orris Medical - For educational use only - Verify all clinical details with current references") canvas.drawRightString(PAGE_W - MR, 2.8*mm, "July 2026") mid_x = ML + COL_W + GAP/2 canvas.setStrokeColor(MGRAY) canvas.setLineWidth(0.4) canvas.line(mid_x, MB, mid_x, PAGE_H - MT) canvas.restoreState() def main(): doc = BaseDocTemplate(OUTPUT, pagesize=A4, leftMargin=ML, rightMargin=MR, topMargin=MT, bottomMargin=MB) f_left = Frame(ML, MB, COL_W, PAGE_H-MT-MB, leftPadding=0, rightPadding=0, topPadding=0, bottomPadding=0, id="left") f_right = Frame(ML+COL_W+GAP, MB, COL_W, PAGE_H-MT-MB, leftPadding=0, rightPadding=0, topPadding=0, bottomPadding=0, id="right") doc.addPageTemplates([PageTemplate(id="two_col", frames=[f_left, f_right], onPage=draw_page)]) story = left_col() + [FrameBreak()] + right_col() doc.build(story) print("Done:", OUTPUT) if __name__ == "__main__": main() PYEOF echo "written"
cd /home/daytona/workspace/hydatid-cyst && python3 make_card.py 2>&1
cd /home/daytona/workspace/hydatid-cyst && python3 -c " from pypdf import PdfReader r = PdfReader('Hydatid_Cyst_Quick_Reference.pdf') print('Pages:', len(r.pages)) import os print('Size:', os.path.getsize('Hydatid_Cyst_Quick_Reference.pdf'), 'bytes') "
Hydatid Cyst Quick Reference
PDF Document · PDF