Approach to Abdominal pain in pediatrics pdf
"abdominal pain" AND "pediatric" AND "approach"
mkdir -p /home/daytona/workspace/pediatric-abdominal-pain && ls /home/daytona/wo…
~/pediatric-abdominal-pain/generate_pdf.py
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm, mm
from reportlab.lib.colors import HexColor, white, black, Color
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, KeepTogether, PageBreak
)
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
from reportlab.pdfgen import canvas
from reportlab.platypus.doctemplate import PageTemplate, BaseDocTemplate, Frame
from reportlab.lib import colors
import io
OUTPUT = "/home/daytona/workspace/pediatric-abdominal-pain/Pediatric_Abdominal_Pain.pdf"
# Color palette
DARK_BLUE = HexColor("#1A3A5C")
MID_BLUE = HexColor("#2C5F8A")
LIGHT_BLUE = HexColor("#D6E8F7")
ACCENT_TEAL = HexColor("#1E7A8C")
ACCENT_GREEN = HexColor("#2E7D52")
ACCENT_RED = HexColor("#C0392B")
ACCENT_GOLD = HexColor("#D4A017")
PALE_GRAY = HexColor("#F5F5F5")
BORDER_GRAY = HexColor("#CCCCCC")
TEXT_DARK = HexColor("#1A1A2E")
PAGE_W, PAGE_H = A4
# ─── Custom Doc Template with header/footer ────────────────────────────────
class MedDocTemplate(BaseDocTemplate):
def __init__(self, filename, **kw):
super().__init__(filename, **kw)
frame = Frame(
self.leftMargin, self.bottomMargin,
self.width, self.height,
id='normal'
)
template = PageTemplate(id='main', frames=frame, onPage=self._draw_page)
self.addPageTemplates([template])
def _draw_page(self, canv, doc):
canv.saveState()
# Header bar
canv.setFillColor(DARK_BLUE)
canv.rect(0, PAGE_H - 22*mm, PAGE_W, 22*mm, fill=1, stroke=0)
canv.setFont("Helvetica-Bold", 10)
canv.setFillColor(white)
canv.drawString(1.5*cm, PAGE_H - 14*mm, "APPROACH TO ABDOMINAL PAIN IN PEDIATRICS")
canv.setFont("Helvetica", 9)
canv.drawRightString(PAGE_W - 1.5*cm, PAGE_H - 14*mm, "Clinical Reference Guide")
# Footer bar
canv.setFillColor(DARK_BLUE)
canv.rect(0, 0, PAGE_W, 12*mm, fill=1, stroke=0)
canv.setFont("Helvetica", 8)
canv.setFillColor(white)
canv.drawString(1.5*cm, 4*mm, "Sources: Tintinalli's Emergency Medicine | Grainger & Allison's Diagnostic Radiology")
canv.drawRightString(PAGE_W - 1.5*cm, 4*mm, f"Page {doc.page}")
canv.restoreState()
def build_styles():
base = getSampleStyleSheet()
styles = {}
styles['title'] = ParagraphStyle(
'title', parent=base['Title'],
fontName='Helvetica-Bold', fontSize=26, textColor=white,
alignment=TA_CENTER, spaceAfter=4
)
styles['subtitle'] = ParagraphStyle(
'subtitle', parent=base['Normal'],
fontName='Helvetica', fontSize=13, textColor=HexColor("#B0D0F0"),
alignment=TA_CENTER, spaceAfter=2
)
styles['h1'] = ParagraphStyle(
'h1', parent=base['Heading1'],
fontName='Helvetica-Bold', fontSize=14, textColor=white,
spaceAfter=4, spaceBefore=8,
backColor=MID_BLUE, leftIndent=-6, rightIndent=-6,
borderPad=5
)
styles['h2'] = ParagraphStyle(
'h2', parent=base['Heading2'],
fontName='Helvetica-Bold', fontSize=12, textColor=DARK_BLUE,
spaceAfter=3, spaceBefore=6,
borderPad=2
)
styles['h3'] = ParagraphStyle(
'h3', parent=base['Heading3'],
fontName='Helvetica-Bold', fontSize=10.5, textColor=ACCENT_TEAL,
spaceAfter=2, spaceBefore=4
)
styles['body'] = ParagraphStyle(
'body', parent=base['Normal'],
fontName='Helvetica', fontSize=9.5, textColor=TEXT_DARK,
spaceAfter=5, leading=14, alignment=TA_JUSTIFY
)
styles['bullet'] = ParagraphStyle(
'bullet', parent=base['Normal'],
fontName='Helvetica', fontSize=9.5, textColor=TEXT_DARK,
spaceAfter=3, leading=13, leftIndent=12, bulletIndent=0
)
styles['bullet2'] = ParagraphStyle(
'bullet2', parent=base['Normal'],
fontName='Helvetica', fontSize=9, textColor=TEXT_DARK,
spaceAfter=2, leading=12, leftIndent=24, bulletIndent=12
)
styles['alert_red'] = ParagraphStyle(
'alert_red', parent=base['Normal'],
fontName='Helvetica-Bold', fontSize=9.5, textColor=white,
backColor=ACCENT_RED, spaceAfter=2, leading=13,
leftIndent=6, rightIndent=6, borderPad=4
)
styles['alert_green'] = ParagraphStyle(
'alert_green', parent=base['Normal'],
fontName='Helvetica', fontSize=9.5, textColor=TEXT_DARK,
backColor=HexColor("#D5F5E3"), spaceAfter=2, leading=13,
leftIndent=6, rightIndent=6, borderPad=4
)
styles['note'] = ParagraphStyle(
'note', parent=base['Normal'],
fontName='Helvetica-Oblique', fontSize=9, textColor=HexColor("#555555"),
backColor=HexColor("#FFF9C4"), spaceAfter=3, leading=13,
leftIndent=6, rightIndent=6, borderPad=4
)
styles['caption'] = ParagraphStyle(
'caption', parent=base['Normal'],
fontName='Helvetica-Oblique', fontSize=8, textColor=HexColor("#666666"),
alignment=TA_CENTER, spaceAfter=4
)
styles['toc_entry'] = ParagraphStyle(
'toc_entry', parent=base['Normal'],
fontName='Helvetica', fontSize=10, textColor=TEXT_DARK,
spaceAfter=4, leading=14
)
return styles
def h1_para(text, styles):
return Paragraph(f" {text}", styles['h1'])
def h2_para(text, styles):
return Paragraph(text, styles['h2'])
def h3_para(text, styles):
return Paragraph(text, styles['h3'])
def body(text, styles):
return Paragraph(text, styles['body'])
def bullet(text, styles, level=1):
marker = "•" if level == 1 else "◦"
s = styles['bullet'] if level == 1 else styles['bullet2']
return Paragraph(f"{marker} {text}", s)
def spacer(h=4):
return Spacer(1, h*mm)
def divider(color=BORDER_GRAY):
return HRFlowable(width="100%", thickness=0.5, color=color, spaceAfter=3*mm, spaceBefore=1*mm)
def section_box(title, content_paras, styles, bg=LIGHT_BLUE, border=MID_BLUE):
"""Wrap content in a colored box table."""
header_para = Paragraph(f"<b>{title}</b>", ParagraphStyle(
'boxhead', parent=getSampleStyleSheet()['Normal'],
fontName='Helvetica-Bold', fontSize=10, textColor=white
))
header_cell = Table([[header_para]], colWidths=['100%'])
header_cell.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), border),
('TOPPADDING', (0,0), (-1,-1), 5),
('BOTTOMPADDING', (0,0), (-1,-1), 5),
('LEFTPADDING', (0,0), (-1,-1), 8),
]))
body_cell = Table([[p] for p in content_paras], colWidths=['100%'])
body_cell.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), bg),
('TOPPADDING', (0,0), (-1,-1), 3),
('BOTTOMPADDING', (0,0), (-1,-1), 3),
('LEFTPADDING', (0,0), (-1,-1), 8),
('RIGHTPADDING', (0,0), (-1,-1), 8),
('BOX', (0,0), (-1,-1), 0.5, border),
]))
return [header_cell, body_cell, spacer(4)]
def make_cover(styles):
story = []
# Cover block
cover_table = Table([
[Paragraph("<b>CLINICAL REFERENCE GUIDE</b>", ParagraphStyle(
'cov_tag', fontName='Helvetica-Bold', fontSize=9, textColor=HexColor("#B0D0F0"), alignment=TA_CENTER
))],
[Paragraph("APPROACH TO<br/>ABDOMINAL PAIN<br/>IN PEDIATRICS", ParagraphStyle(
'cov_title', fontName='Helvetica-Bold', fontSize=30, textColor=white,
alignment=TA_CENTER, leading=36, spaceAfter=6
))],
[Paragraph("A Comprehensive Age-Based Diagnostic Framework", ParagraphStyle(
'cov_sub', fontName='Helvetica', fontSize=13, textColor=HexColor("#B0D0F0"), alignment=TA_CENTER
))],
[Spacer(1, 8*mm)],
[Paragraph("Covering: History | Examination | Investigations | Imaging | Age-Based Differentials | Management", ParagraphStyle(
'cov_note', fontName='Helvetica', fontSize=9.5, textColor=HexColor("#C8E6FA"), alignment=TA_CENTER
))],
[Spacer(1, 10*mm)],
[Paragraph("Sources: Tintinalli's Emergency Medicine (9th Ed.) | Grainger & Allison's Diagnostic Radiology | Sleisenger & Fordtran GI Disease", ParagraphStyle(
'cov_src', fontName='Helvetica-Oblique', fontSize=8, textColor=HexColor("#8ABED8"), alignment=TA_CENTER
))],
], colWidths=[16*cm])
cover_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), DARK_BLUE),
('ALIGN', (0,0), (-1,-1), 'CENTER'),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('TOPPADDING', (0,0), (-1,-1), 8),
('BOTTOMPADDING', (0,0), (-1,-1), 8),
('LEFTPADDING', (0,0), (-1,-1), 20),
('RIGHTPADDING', (0,0), (-1,-1), 20),
('ROUNDEDCORNERS', [8, 8, 8, 8]),
]))
story.append(Spacer(1, 35*mm))
story.append(cover_table)
story.append(PageBreak())
return story
def make_toc(styles):
story = [h1_para("TABLE OF CONTENTS", styles), spacer(3)]
entries = [
("1.", "Introduction & Overview"),
("2.", "Initial Assessment Framework"),
("3.", "History Taking"),
("4.", "Physical Examination"),
("5.", "Laboratory Investigations"),
("6.", "Diagnostic Imaging"),
("7.", "Age-Based Differential Diagnosis"),
("8.", "Specific Conditions by Age Group"),
(" 8a.", "Neonates & Young Infants (0-3 months)"),
(" 8b.", "Older Infants & Toddlers (3 months - 3 years)"),
(" 8c.", "Children & Adolescents (3-15 years)"),
("9.", "Surgical Emergencies: Red Flags"),
("10.", "Pain Management"),
("11.", "Functional / Recurrent Abdominal Pain"),
("12.", "Imaging Approach Summary"),
("13.", "Discharge Criteria & Follow-up"),
]
toc_data = []
for num, text in entries:
row = [
Paragraph(f"<b>{num}</b>", ParagraphStyle('t1', fontName='Helvetica-Bold', fontSize=10, textColor=MID_BLUE)),
Paragraph(text, ParagraphStyle('t2', fontName='Helvetica', fontSize=10, textColor=TEXT_DARK)),
]
toc_data.append(row)
toc_table = Table(toc_data, colWidths=[1.5*cm, 14*cm])
toc_table.setStyle(TableStyle([
('ROWBACKGROUNDS', (0,0), (-1,-1), [PALE_GRAY, white]),
('TOPPADDING', (0,0), (-1,-1), 5),
('BOTTOMPADDING', (0,0), (-1,-1), 5),
('LEFTPADDING', (0,0), (-1,-1), 6),
('LINEBELOW', (0,0), (-1,-1), 0.3, BORDER_GRAY),
]))
story.append(toc_table)
story.append(PageBreak())
return story
def make_intro(styles):
story = []
story.append(h1_para("1. INTRODUCTION & OVERVIEW", styles))
story.append(body(
"Abdominal pain is one of the most common presenting complaints in pediatric emergency medicine. "
"The diagnostic challenge lies in the fact that emergent surgical conditions in children may present "
"atypically - with vomiting, fever, irritability, or lethargy rather than overt pain. "
"The largest single group will have no definite diagnosis, receiving a diagnosis of exclusion: "
"nonspecific abdominal pain.", styles
))
story.append(body(
"Gastroenteritis is the most common cause of abdominal pain across all pediatric age groups. "
"However, unsubstantiated diagnoses (e.g., 'gastritis', 'constipation') should NOT be assigned "
"without strong clinical support. Children discharged without a clear diagnosis must have a "
"planned re-examination.", styles
))
# Key principles box
kp_paras = [
bullet("The age of the child significantly narrows the differential diagnosis", styles),
bullet("Bilious vomiting in an infant is a surgical emergency until proven otherwise (27-51% require surgery)", styles),
bullet("Stillness suggests peritoneal irritation (e.g., appendicitis); writhing suggests obstruction (e.g., intussusception)", styles),
bullet("Analgesics do NOT mask surgical conditions and actually improve clinical assessment", styles),
bullet("Always examine extra-abdominal causes: pharyngitis, pneumonia, genital/scrotal pathology", styles),
]
story += section_box("KEY PRINCIPLES", kp_paras, styles, bg=LIGHT_BLUE, border=MID_BLUE)
story.append(spacer(2))
return story
def make_initial_assessment(styles):
story = []
story.append(h1_para("2. INITIAL ASSESSMENT FRAMEWORK", styles))
story.append(body(
"A systematic approach begins with rapid triage to identify life-threatening conditions, "
"followed by a thorough age-directed history, physical examination, and targeted investigations.", styles
))
# Triage priority table
triage_data = [
[Paragraph("<b>Priority</b>", ParagraphStyle('th', fontName='Helvetica-Bold', fontSize=9.5, textColor=white)),
Paragraph("<b>Presentation</b>", ParagraphStyle('th', fontName='Helvetica-Bold', fontSize=9.5, textColor=white)),
Paragraph("<b>Likely Diagnosis</b>", ParagraphStyle('th', fontName='Helvetica-Bold', fontSize=9.5, textColor=white))],
[Paragraph("IMMEDIATE", ParagraphStyle('td_r', fontName='Helvetica-Bold', fontSize=9, textColor=ACCENT_RED)),
Paragraph("Shock, bilious vomiting, distended rigid abdomen, altered mental status", ParagraphStyle('td', fontName='Helvetica', fontSize=9)),
Paragraph("Volvulus, NEC, incarcerated hernia, peritonitis", ParagraphStyle('td', fontName='Helvetica', fontSize=9))],
[Paragraph("URGENT", ParagraphStyle('td_o', fontName='Helvetica-Bold', fontSize=9, textColor=ACCENT_GOLD)),
Paragraph("Colicky pain + vomiting + currant-jelly stool, or RIF pain + fever", ParagraphStyle('td', fontName='Helvetica', fontSize=9)),
Paragraph("Intussusception, appendicitis, testicular/ovarian torsion", ParagraphStyle('td', fontName='Helvetica', fontSize=9))],
[Paragraph("SEMI-URGENT", ParagraphStyle('td_g', fontName='Helvetica-Bold', fontSize=9, textColor=ACCENT_GREEN)),
Paragraph("Pain + dysuria, pain + diarrhea/vomiting, pharyngitis + pain", ParagraphStyle('td', fontName='Helvetica', fontSize=9)),
Paragraph("UTI, gastroenteritis, mesenteric adenitis, strep pharyngitis", ParagraphStyle('td', fontName='Helvetica', fontSize=9))],
]
triage_table = Table(triage_data, colWidths=[3*cm, 7*cm, 6*cm])
triage_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), DARK_BLUE),
('ROWBACKGROUNDS', (0,1), (-1,-1), [PALE_GRAY, white]),
('GRID', (0,0), (-1,-1), 0.5, BORDER_GRAY),
('TOPPADDING', (0,0), (-1,-1), 5),
('BOTTOMPADDING', (0,0), (-1,-1), 5),
('LEFTPADDING', (0,0), (-1,-1), 6),
('VALIGN', (0,0), (-1,-1), 'TOP'),
]))
story.append(triage_table)
story.append(spacer(4))
return story
def make_history(styles):
story = []
story.append(h1_para("3. HISTORY TAKING", styles))
cols = [
["PAIN CHARACTERIZATION", [
"Onset: sudden vs. gradual",
"Location: periumbilical, RIF, generalized",
"Character: colicky (obstruction) vs. constant (peritoneal)",
"Severity: 0-10 scale (use FACES for young children)",
"Radiation: to back (pancreatitis), to groin (renal colic)",
"Aggravating/relieving factors",
]],
["ASSOCIATED SYMPTOMS", [
"Vomiting: bilious (surgical emergency)?",
"Diarrhea: bloody? (intussusception, IBD, infectious)",
"Fever: low-grade vs. high",
"Dysuria/hematuria: UTI, renal stones",
"Rash: HSP (purpuric lower limbs)",
"Sore throat: mesenteric adenitis",
]],
["PAST HISTORY & OTHER", [
"Birth history (neonates: perinatal asphyxia, prematurity)",
"Previous similar episodes",
"Bowel habit: constipation history",
"Menstrual history in adolescent girls",
"Sexual activity (ectopic pregnancy, PID)",
"Family history: IBD, sickle cell, malignancy",
]],
]
col_paras = []
for title, items in cols:
header = Paragraph(f"<b>{title}</b>", ParagraphStyle(
'ch', fontName='Helvetica-Bold', fontSize=9, textColor=white
))
content = [header] + [Paragraph(f"• {i}", ParagraphStyle(
'ci', fontName='Helvetica', fontSize=8.5, textColor=TEXT_DARK, leading=13
)) for i in items]
col_paras.append(content)
col_tables = []
for cp in col_paras:
ct = Table([[p] for p in cp], colWidths=['100%'])
ct.setStyle(TableStyle([
('BACKGROUND', (0,0), (0,0), MID_BLUE),
('BACKGROUND', (0,1), (-1,-1), PALE_GRAY),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 3),
('LEFTPADDING', (0,0), (-1,-1), 6),
('BOX', (0,0), (-1,-1), 0.5, MID_BLUE),
]))
col_tables.append(ct)
three_col = Table([col_tables], colWidths=[5.2*cm, 5.2*cm, 5.2*cm])
three_col.setStyle(TableStyle([
('VALIGN', (0,0), (-1,-1), 'TOP'),
('LEFTPADDING', (0,0), (-1,-1), 3),
('RIGHTPADDING', (0,0), (-1,-1), 3),
]))
story.append(three_col)
story.append(spacer(4))
return story
def make_examination(styles):
story = []
story.append(h1_para("4. PHYSICAL EXAMINATION", styles))
story.append(body(
"Assessment of young children depends on observation for subtle clues. "
"The approach must be gentle and adapted to the child's developmental stage.", styles
))
story.append(h2_para("Examination Sequence", styles))
steps = [
("1. General Inspection", "Posture (stillness = peritonism; writhing = obstruction), pallor, lethargy, inconsolability, skin color"),
("2. Vital Signs", "Temperature, heart rate, respiratory rate, blood pressure, SpO2, capillary refill time"),
("3. Abdominal Inspection", "Distension, visible peristalsis, scars, bruising (consider non-accidental trauma)"),
("4. Auscultation", "Bowel sounds (absent = ileus/peritonitis; high-pitched = obstruction) - BEFORE palpation"),
("5. Palpation", "Start AWAY from area of maximal tenderness. Bring knees up to relax muscles. Guarding, rigidity, rebound"),
("6. Peritoneal Signs", "Cough test, heel-tap test, gentle percussion tenderness. Observe walking or jumping in toddlers"),
("7. Extra-abdominal", "Oropharynx (pharyngitis), chest (pneumonia), hip (psoas irritation), scrotal/genital exam"),
("8. Rectal Exam", "If indicated: blood, constipation, anal abnormalities (not routinely required in all children)"),
]
step_data = []
for step, desc in steps:
step_data.append([
Paragraph(f"<b>{step}</b>", ParagraphStyle('st', fontName='Helvetica-Bold', fontSize=9, textColor=MID_BLUE)),
Paragraph(desc, ParagraphStyle('sd', fontName='Helvetica', fontSize=9, leading=13)),
])
step_table = Table(step_data, colWidths=[5*cm, 11*cm])
step_table.setStyle(TableStyle([
('ROWBACKGROUNDS', (0,0), (-1,-1), [LIGHT_BLUE, white]),
('GRID', (0,0), (-1,-1), 0.3, BORDER_GRAY),
('TOPPADDING', (0,0), (-1,-1), 5),
('BOTTOMPADDING', (0,0), (-1,-1), 5),
('LEFTPADDING', (0,0), (-1,-1), 6),
('VALIGN', (0,0), (-1,-1), 'TOP'),
]))
story.append(step_table)
story.append(spacer(3))
story.append(Paragraph(
"<b>Note:</b> In toddlers (stranger anxiety), ask the parent to palpate the abdomen while "
"you observe. Adolescents may give additional history with parents out of the room.",
styles['note']
))
story.append(spacer(4))
return story
def make_investigations(styles):
story = []
story.append(h1_para("5. LABORATORY INVESTIGATIONS", styles))
story.append(body(
"Not all children require laboratory investigations. Test selection should be guided by "
"clinical suspicion and the age/appearance of the child.", styles
))
inv_data = [
[Paragraph("<b>Test</b>", ParagraphStyle('th', fontName='Helvetica-Bold', fontSize=9.5, textColor=white)),
Paragraph("<b>Indication</b>", ParagraphStyle('th', fontName='Helvetica-Bold', fontSize=9.5, textColor=white)),
Paragraph("<b>Notes</b>", ParagraphStyle('th', fontName='Helvetica-Bold', fontSize=9.5, textColor=white))],
["Bedside glucose", "Ill-appearing child; persistent vomiting; poor intake",
"First step in any seriously unwell child - rule out DKA hypoglycemia"],
["Urinalysis + urine hCG", "Dysuria, flank pain; any adolescent female",
"Identifies UTI, haematuria, pregnancy. Urine hCG in all post-menarchal girls"],
["FBC (CBC)", "Suspected infection, anaemia",
"WBC count is a POOR screening test for undifferentiated abdominal pain"],
["CRP / ESR", "Suspected appendicitis, IBD, sepsis",
"CRP also poor in isolation; use alongside clinical scoring"],
["Chemistry / Electrolytes", "Ill-appearing, vomiting, neonates <6 months",
"Sodium/metabolic abnormalities more common in early infancy"],
["LFTs, Amylase, Lipase", "RUQ pain, back radiation, vomiting",
"Pancreatitis, cholecystitis, hepatitis"],
["Blood cultures", "Sepsis, fever with peritonism",
"Before antibiotics in NEC, peritonitis"],
["Coagulation / Type & Cross", "Surgical emergency (volvulus, NEC, peritonitis)",
"Obtain before emergency surgery"],
["Stool culture / microscopy", "Bloody diarrhoea, suspected infectious colitis",
"E. coli O157, Salmonella, Campylobacter, ova & cysts"],
]
for i in range(1, len(inv_data)):
inv_data[i] = [
Paragraph(str(inv_data[i][0]), ParagraphStyle('td1', fontName='Helvetica-Bold', fontSize=9, textColor=DARK_BLUE)),
Paragraph(str(inv_data[i][1]), ParagraphStyle('td2', fontName='Helvetica', fontSize=9, leading=13)),
Paragraph(str(inv_data[i][2]), ParagraphStyle('td3', fontName='Helvetica', fontSize=9, leading=13)),
]
inv_table = Table(inv_data, colWidths=[4*cm, 6*cm, 6*cm])
inv_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), DARK_BLUE),
('ROWBACKGROUNDS', (0,1), (-1,-1), [PALE_GRAY, white]),
('GRID', (0,0), (-1,-1), 0.4, BORDER_GRAY),
('TOPPADDING', (0,0), (-1,-1), 5),
('BOTTOMPADDING', (0,0), (-1,-1), 5),
('LEFTPADDING', (0,0), (-1,-1), 6),
('VALIGN', (0,0), (-1,-1), 'TOP'),
('TEXTCOLOR', (0,0), (-1,0), white),
]))
story.append(inv_table)
story.append(spacer(4))
return story
def make_imaging(styles):
story = []
story.append(h1_para("6. DIAGNOSTIC IMAGING", styles))
story.append(body(
"Ultrasound (US) is the first-line imaging modality for pediatric abdominal pain. "
"Children have less body fat, making them ideally suited for high-quality US imaging. "
"CT should be used restrictively due to radiation sensitivity; children are more "
"radiosensitive than adults.", styles
))
img_data = [
[Paragraph("<b>Modality</b>", ParagraphStyle('th', fontName='Helvetica-Bold', fontSize=9.5, textColor=white)),
Paragraph("<b>First-Line Indications</b>", ParagraphStyle('th', fontName='Helvetica-Bold', fontSize=9.5, textColor=white)),
Paragraph("<b>Key Notes</b>", ParagraphStyle('th', fontName='Helvetica-Bold', fontSize=9.5, textColor=white))],
[Paragraph("<b>Ultrasound</b>", ParagraphStyle('td1', fontName='Helvetica-Bold', fontSize=9, textColor=MID_BLUE)),
Paragraph("Appendicitis, pyloric stenosis, intussusception, testicular torsion, biliary/gynecologic pathology, renal colic, mesenteric adenitis", ParagraphStyle('td', fontName='Helvetica', fontSize=9, leading=13)),
Paragraph("No radiation, fast, cheap. Graded compression technique for appendix. Doppler for torsion. Sensitivity 93-100% for malrotation diagnosis.", ParagraphStyle('td', fontName='Helvetica', fontSize=9, leading=13))],
[Paragraph("<b>Plain X-Ray</b>", ParagraphStyle('td1', fontName='Helvetica-Bold', fontSize=9, textColor=MID_BLUE)),
Paragraph("Suspected obstruction or ileus; free air (perforation); NEC (pneumatosis intestinalis, portal gas)", ParagraphStyle('td', fontName='Helvetica', fontSize=9, leading=13)),
Paragraph("1/600th radiation dose of CT. AP + left lateral decubitus views for free air. NOT indicated for routine constipation workup.", ParagraphStyle('td', fontName='Helvetica', fontSize=9, leading=13))],
[Paragraph("<b>Upper GI Series</b>", ParagraphStyle('td1', fontName='Helvetica-Bold', fontSize=9, textColor=MID_BLUE)),
Paragraph("Malrotation / midgut volvulus (test of choice); pyloric stenosis if US inconclusive", ParagraphStyle('td', fontName='Helvetica', fontSize=9, leading=13)),
Paragraph("'Bird's beak' or corkscrew sign in volvulus. Normal D-J junction at level of duodenal bulb, LEFT of spine. Use water-soluble contrast if perforation suspected.", ParagraphStyle('td', fontName='Helvetica', fontSize=9, leading=13))],
[Paragraph("<b>CT Abdomen</b>", ParagraphStyle('td1', fontName='Helvetica-Bold', fontSize=9, textColor=MID_BLUE)),
Paragraph("Equivocal US in appendicitis; complicated periappendiceal abscess; abdominal trauma; tumor evaluation", ParagraphStyle('td', fontName='Helvetica', fontSize=9, leading=13)),
Paragraph("Most sensitive for appendicitis and intra-abdominal abscesses. Use ONLY when other modalities fail. IV contrast risk: allergy, nephropathy. Restrictive use essential.", ParagraphStyle('td', fontName='Helvetica', fontSize=9, leading=13))],
[Paragraph("<b>MRI</b>", ParagraphStyle('td1', fontName='Helvetica-Bold', fontSize=9, textColor=MID_BLUE)),
Paragraph("IBD evaluation; equivocal US in appendicitis (alternative to CT); perianal disease", ParagraphStyle('td', fontName='Helvetica', fontSize=9, leading=13)),
Paragraph("No radiation. Useful when CT would be repeated. Limited by availability and time. May need sedation in young children.", ParagraphStyle('td', fontName='Helvetica', fontSize=9, leading=13))],
[Paragraph("<b>Contrast Enema</b>", ParagraphStyle('td1', fontName='Helvetica-Bold', fontSize=9, textColor=MID_BLUE)),
Paragraph("Intussusception (diagnostic + therapeutic); Hirschsprung's disease evaluation", ParagraphStyle('td', fontName='Helvetica', fontSize=9, leading=13)),
Paragraph("Air enema preferred for intussusception reduction. Barium enema: classic 'claw sign' in intussusception, 'transition zone' in Hirschsprung's.", ParagraphStyle('td', fontName='Helvetica', fontSize=9, leading=13))],
]
img_table = Table(img_data, colWidths=[3.5*cm, 6.5*cm, 6*cm])
img_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), DARK_BLUE),
('ROWBACKGROUNDS', (0,1), (-1,-1), [PALE_GRAY, white]),
('GRID', (0,0), (-1,-1), 0.4, BORDER_GRAY),
('TOPPADDING', (0,0), (-1,-1), 5),
('BOTTOMPADDING', (0,0), (-1,-1), 5),
('LEFTPADDING', (0,0), (-1,-1), 6),
('VALIGN', (0,0), (-1,-1), 'TOP'),
('TEXTCOLOR', (0,0), (-1,0), white),
]))
story.append(img_table)
story.append(spacer(2))
story.append(Paragraph(
"<b>IDSA 2024 Guideline:</b> Ultrasound should be performed before CT for suspected acute appendicitis "
"in children. A comprehensive US examination makes CT redundant in most cases (PMID: 38963819).",
styles['note']
))
story.append(spacer(4))
return story
def make_age_based_dd(styles):
story = []
story.append(h1_para("7. AGE-BASED DIFFERENTIAL DIAGNOSIS", styles))
story.append(body(
"The age of the child is the single most powerful filter for narrowing the differential diagnosis. "
"Many conditions cross age categories; this table represents the most characteristic presentations.", styles
))
# Table headers
header = [
Paragraph("<b>Age Group</b>", ParagraphStyle('th', fontName='Helvetica-Bold', fontSize=9.5, textColor=white)),
Paragraph("<b>EMERGENT / Surgical</b>", ParagraphStyle('th', fontName='Helvetica-Bold', fontSize=9.5, textColor=white)),
Paragraph("<b>Non-Emergent / Medical</b>", ParagraphStyle('th', fontName='Helvetica-Bold', fontSize=9.5, textColor=white)),
]
rows = [
[
Paragraph("<b>0-3 months</b>\nNeonates &\nYoung Infants", ParagraphStyle('ag', fontName='Helvetica-Bold', fontSize=9, textColor=DARK_BLUE, leading=13)),
Paragraph("• Necrotizing enterocolitis (NEC)\n• Malrotation with midgut volvulus\n• Incarcerated inguinal hernia\n• Testicular torsion\n• Non-accidental trauma\n• Hirschsprung's enterocolitis", ParagraphStyle('e', fontName='Helvetica', fontSize=8.5, leading=13)),
Paragraph("• Colic\n• Constipation\n• Acute gastroenteritis\n• Gastroesophageal reflux\n• Pyloric stenosis (painless vomiting)", ParagraphStyle('n', fontName='Helvetica', fontSize=8.5, leading=13)),
],
[
Paragraph("<b>3 months - 3 years</b>\nOlder Infants &\nToddlers", ParagraphStyle('ag', fontName='Helvetica-Bold', fontSize=9, textColor=DARK_BLUE, leading=13)),
Paragraph("• Intussusception\n• Malrotation/volvulus\n• Testicular torsion\n• Appendicitis (uncommon <2y)\n• Vaso-occlusive crisis (sickle cell)\n• Incarcerated hernia", ParagraphStyle('e', fontName='Helvetica', fontSize=8.5, leading=13)),
Paragraph("• Urinary tract infection (UTI)\n• Constipation\n• Henoch-Schönlein purpura (HSP)\n• Acute gastroenteritis\n• Mesenteric adenitis\n• Accidental/inflicted injury", ParagraphStyle('n', fontName='Helvetica', fontSize=8.5, leading=13)),
],
[
Paragraph("<b>3-15 years</b>\nChildren &\nAdolescents", ParagraphStyle('ag', fontName='Helvetica-Bold', fontSize=9, textColor=DARK_BLUE, leading=13)),
Paragraph("• Appendicitis (most common surgical emergency)\n• Testicular / ovarian torsion\n• Ectopic pregnancy\n• Diabetic ketoacidosis\n• Vaso-occlusive crisis\n• Cholecystitis / Pancreatitis\n• Tumor / malignancy\n• Bowel obstruction", ParagraphStyle('e', fontName='Helvetica', fontSize=8.5, leading=13)),
Paragraph("• Acute gastroenteritis\n• UTI / renal stones\n• Streptococcal pharyngitis\n• Mesenteric adenitis\n• IBD (Crohn's, UC)\n• HSP\n• Peptic ulcer disease\n• Ovarian cysts\n• Functional abdominal pain\n• Constipation\n• Pregnancy / PID", ParagraphStyle('n', fontName='Helvetica', fontSize=8.5, leading=13)),
],
]
dd_table = Table([header] + rows, colWidths=[3.5*cm, 7*cm, 6*cm])
dd_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), DARK_BLUE),
('BACKGROUND', (0,1), (0,1), HexColor("#FDE8E8")),
('BACKGROUND', (0,2), (0,2), HexColor("#FFF5D9")),
('BACKGROUND', (0,3), (0,3), HexColor("#E8F5E9")),
('ROWBACKGROUNDS', (1,1), (1,-1), [HexColor("#FDECEA"), white]),
('GRID', (0,0), (-1,-1), 0.4, BORDER_GRAY),
('TOPPADDING', (0,0), (-1,-1), 6),
('BOTTOMPADDING', (0,0), (-1,-1), 6),
('LEFTPADDING', (0,0), (-1,-1), 6),
('VALIGN', (0,0), (-1,-1), 'TOP'),
('TEXTCOLOR', (0,0), (-1,0), white),
]))
story.append(dd_table)
story.append(PageBreak())
return story
def make_specific_conditions(styles):
story = []
story.append(h1_para("8. SPECIFIC CONDITIONS BY AGE GROUP", styles))
# 8a - Neonates
story.append(h2_para("8a. Neonates & Young Infants (0-3 months)", styles))
nec_data = [
[Paragraph("<b>NECROTIZING ENTEROCOLITIS (NEC)</b>", ParagraphStyle('cond', fontName='Helvetica-Bold', fontSize=10, textColor=white)),
Paragraph("<b>MALROTATION / MIDGUT VOLVULUS</b>", ParagraphStyle('cond', fontName='Helvetica-Bold', fontSize=10, textColor=white))],
[
Paragraph(
"<b>Epidemiology:</b> Primarily premature infants. Overall mortality 15-30%. Mean onset 2-9 days old. "
"Predisposing factors: prematurity, CHD, sepsis, asphyxia, polycythemia, hypotension.<br/><br/>"
"<b>Features:</b> Poor feeding, lethargy, abdominal distension, bilious vomiting, "
"temperature instability, apnea, tenderness. Gross/occult rectal blood increases likelihood.<br/><br/>"
"<b>Imaging:</b> AP + left lateral decubitus X-ray. Pneumatosis intestinalis (pathognomonic), "
"portal venous gas, fixed dilated loops. Free air = perforation.<br/><br/>"
"<b>Management:</b> NBM, NG decompression, IV fluids, broad-spectrum antibiotics "
"(ampicillin + gentamicin/cefotaxime + metronidazole), surgical consultation. ICU admission.",
ParagraphStyle('cc', fontName='Helvetica', fontSize=8.5, leading=13)
),
Paragraph(
"<b>Epidemiology:</b> 80% present in first month; 90% within first year. Can occur at any age. "
"Associated with Down syndrome, heterotaxy, duodenal atresia.<br/><br/>"
"<b>Pathophysiology:</b> Abnormal gut rotation leaves cecum high; Ladd's bands cross duodenum. "
"Volvulus causes complete midgut ischemia within hours - a true surgical emergency.<br/><br/>"
"<b>Features:</b> No prior history, sudden onset constant pain, bilious vomiting, "
"distension, irritability. Signs of shock. Diffuse tenderness and rigidity.<br/><br/>"
"<b>Imaging:</b> Upper GI series (gold standard; 93-100% sensitive for malrotation). "
"D-J junction low/right of spine. 'Bird's beak' or corkscrew in volvulus.<br/><br/>"
"<b>Management:</b> Do NOT delay surgery for imaging. Resuscitation + immediate surgical Ladd's procedure.",
ParagraphStyle('cc', fontName='Helvetica', fontSize=8.5, leading=13)
),
]
]
nec_table = Table(nec_data, colWidths=[8*cm, 8*cm])
nec_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), MID_BLUE),
('BACKGROUND', (0,1), (-1,-1), PALE_GRAY),
('GRID', (0,0), (-1,-1), 0.5, BORDER_GRAY),
('TOPPADDING', (0,0), (-1,-1), 6),
('BOTTOMPADDING', (0,0), (-1,-1), 6),
('LEFTPADDING', (0,0), (-1,-1), 8),
('VALIGN', (0,0), (-1,-1), 'TOP'),
('TEXTCOLOR', (0,0), (-1,0), white),
]))
story.append(nec_table)
story.append(spacer(3))
# Hirschsprung box
hirsch = section_box(
"HIRSCHSPRUNG'S ENTEROCOLITIS",
[
bullet("Absence of ganglion cells in distal colon causing functional obstruction", styles),
bullet("Suspect if no meconium passage within 24-48 hours of life", styles),
bullet("Enterocolitis presents: explosive diarrhea, fever, vomiting, distension - can occur before or after surgical repair", styles),
bullet("Imaging: contrast enema shows transition zone (narrow aganglionic bowel to dilated proximal bowel)", styles),
bullet("Management: rectal washouts, antibiotics, surgical pull-through procedure", styles),
],
styles, bg=HexColor("#FDE8E8"), border=ACCENT_RED
)
story += hirsch
# 8b - Infants/Toddlers
story.append(h2_para("8b. Older Infants & Toddlers (3 months - 3 years)", styles))
intuss_data = [
[Paragraph("<b>INTUSSUSCEPTION</b>", ParagraphStyle('cond', fontName='Helvetica-Bold', fontSize=10, textColor=white))],
[Paragraph(
"<b>Epidemiology:</b> Most common cause of intestinal obstruction in children under 2 years. Male:Female = 2:1. "
"Peak age: 5-9 months. Rare before 2 months. In >2 years, pathologic lead point (Meckel's, polyp, lymphoma) must be considered.<br/><br/>"
"<b>Pathophysiology:</b> Proximal bowel telescopes into distal segment (ileocaecal most common). "
"Venous obstruction leads to edema, hemorrhage ('currant jelly stool'), then ischemia.<br/><br/>"
"<b>Classic Triad (only 25% have all 3):</b> Colicky abdominal pain + vomiting + currant-jelly stool<br/><br/>"
"<b>Clinical Features:</b> Episodic colicky pain (child draws up knees, screams, then appears well between episodes). "
"Sausage-shaped RUQ/epigastric mass. Altered consciousness (intussusception encephalopathy). Lethargy may be the only sign.<br/><br/>"
"<b>Imaging:</b> US first-line - 'target sign' (concentric rings) or 'pseudokidney sign' on transverse/longitudinal view. "
"Contrast/air enema is both diagnostic and therapeutic (up to 75-90% success). Avoid if perforation suspected.<br/><br/>"
"<b>Management:</b> IV access, fluid resuscitation. Air/hydrostatic enema reduction. "
"Surgery if enema fails, peritoneal signs, or perforation. Recurrence rate ~10%.",
ParagraphStyle('intuss', fontName='Helvetica', fontSize=9, leading=14)
)],
]
intuss_table = Table(intuss_data, colWidths=['100%'])
intuss_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), ACCENT_TEAL),
('BACKGROUND', (0,1), (-1,-1), HexColor("#E8F8F5")),
('BOX', (0,0), (-1,-1), 0.5, ACCENT_TEAL),
('TOPPADDING', (0,0), (-1,-1), 7),
('BOTTOMPADDING', (0,0), (-1,-1), 7),
('LEFTPADDING', (0,0), (-1,-1), 10),
('RIGHTPADDING', (0,0), (-1,-1), 10),
('TEXTCOLOR', (0,0), (-1,0), white),
]))
story.append(intuss_table)
story.append(spacer(3))
# 8c - Children/Adolescents
story.append(h2_para("8c. Children & Adolescents (3-15 years)", styles))
append_data = [
[Paragraph("<b>ACUTE APPENDICITIS</b> - Most common pediatric surgical emergency", ParagraphStyle('cond2', fontName='Helvetica-Bold', fontSize=10, textColor=white))],
[Paragraph(
"<b>Clinical Features:</b> Periumbilical pain migrating to RIF (McBurney's point), anorexia, low-grade fever, nausea/vomiting. "
"30-40% of children do NOT present with the typical picture. Preschool children: atypical features, rapid progression, higher perforation rate at presentation.<br/><br/>"
"<b>Examination Signs:</b> Rovsing's sign (RIF pain on LIF pressure), psoas sign (pain on right hip extension), "
"obturator sign (pain on internal rotation of hip), guarding and rebound tenderness in RIF.<br/><br/>"
"<b>Scoring (Pediatric Appendicitis Score / Alvarado Score):</b> Combine symptoms, signs, WBC, CRP.<br/><br/>"
"<b>Imaging:</b> US first (graded compression; appendix >6mm diameter, non-compressible, "
"hyperechoic periappendiceal fat, appendicolith). CT if US equivocal + high clinical suspicion.<br/><br/>"
"<b>Differentials to exclude:</b> Mesenteric adenitis, terminal ileitis, ovarian pathology (salpingitis, "
"ovarian cyst/torsion in adolescent girls), HSP, Meckel's diverticulitis, pyelonephritis.<br/><br/>"
"<b>Management:</b> Surgical appendicectomy (laparoscopic). IV antibiotics pre-op. "
"Non-operative management with antibiotics: emerging evidence in selected uncomplicated cases.",
ParagraphStyle('apx', fontName='Helvetica', fontSize=9, leading=14)
)],
]
append_table = Table(append_data, colWidths=['100%'])
append_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), ACCENT_RED),
('BACKGROUND', (0,1), (-1,-1), HexColor("#FEF0EF")),
('BOX', (0,0), (-1,-1), 0.5, ACCENT_RED),
('TOPPADDING', (0,0), (-1,-1), 7),
('BOTTOMPADDING', (0,0), (-1,-1), 7),
('LEFTPADDING', (0,0), (-1,-1), 10),
('RIGHTPADDING', (0,0), (-1,-1), 10),
('TEXTCOLOR', (0,0), (-1,0), white),
]))
story.append(append_table)
story.append(spacer(3))
# Other important conditions in children
other_conds = [
("<b>DIABETIC KETOACIDOSIS (DKA)</b>",
"Abdominal pain, vomiting, polyuria/polydipsia, Kussmaul breathing, fruity odour. "
"Bedside glucose is the critical first test. Can mimic acute abdomen. Manage with IV fluids, insulin; avoid surgery."),
("<b>HENOCH-SCHONLEIN PURPURA (HSP/IgAV)</b>",
"Purpuric rash on lower limbs/buttocks, colicky abdominal pain, arthritis, haematuria. "
"Can cause intussusception (lead point). Renal involvement requires monitoring."),
("<b>TESTICULAR TORSION</b>",
"Can present as abdominal pain without testicular symptoms, especially in younger children. "
"Always examine genitalia. Doppler US urgently. 6-hour window for salvage. Surgical emergency."),
("<b>OVARIAN TORSION</b>",
"Acute pelvic/lower abdominal pain in adolescent females. Associated with cysts. "
"Doppler US (absent flow not always present). Surgical emergency - laparoscopic de-torsion."),
("<b>MESENTERIC ADENITIS</b>",
"Common mimicker of appendicitis. Often follows URTI. Tender RIF or periumbilical. "
"US: multiple enlarged mesenteric lymph nodes with preserved fatty hilum and normal Doppler. "
"Diagnosis of exclusion. Self-limiting."),
("<b>PELVIC INFLAMMATORY DISEASE (PID)</b>",
"Sexually active adolescent females. Lower abdominal pain, cervical motion tenderness, vaginal discharge. "
"Treat with antibiotics (ceftriaxone + doxycycline +/- metronidazole)."),
]
for title, desc in other_conds:
cond_data = [[
Paragraph(title, ParagraphStyle('ot', fontName='Helvetica-Bold', fontSize=9.5, textColor=DARK_BLUE)),
Paragraph(desc, ParagraphStyle('od', fontName='Helvetica', fontSize=9, leading=13)),
]]
ct = Table(cond_data, colWidths=[5.5*cm, 10.5*cm])
ct.setStyle(TableStyle([
('BACKGROUND', (0,0), (0,0), LIGHT_BLUE),
('BACKGROUND', (1,0), (1,0), white),
('BOX', (0,0), (-1,-1), 0.4, MID_BLUE),
('LINEAFTER', (0,0), (0,-1), 1, MID_BLUE),
('TOPPADDING', (0,0), (-1,-1), 6),
('BOTTOMPADDING', (0,0), (-1,-1), 6),
('LEFTPADDING', (0,0), (-1,-1), 8),
('VALIGN', (0,0), (-1,-1), 'TOP'),
]))
story.append(ct)
story.append(spacer(1))
story.append(PageBreak())
return story
def make_red_flags(styles):
story = []
story.append(h1_para("9. SURGICAL EMERGENCIES: RED FLAGS", styles))
story.append(body(
"The following features should trigger urgent surgical consultation regardless of other findings:", styles
))
red_flags = [
("Bilious (green) vomiting", "Surgical emergency in any infant until malrotation/volvulus excluded"),
("Rigid / board-like abdomen", "Peritonitis - perforation, NEC, severe appendicitis"),
("Signs of shock", "Tachycardia, hypotension, poor perfusion - volvulus, NEC, incarcerated hernia"),
("Pneumatosis intestinalis on X-ray", "Pathognomonic of NEC"),
("Free air on X-ray", "Perforation - requires emergency surgery"),
("Absent bowel sounds + distension", "Ileus from peritonitis or obstruction"),
("Palpable abdominal mass", "Volvulus, intussusception, tumor, abscess"),
("Currant jelly stool", "Intussusception with mucosal ischemia"),
("Testicular asymmetry or pain", "Torsion - 6-hour window for salvage"),
("Bilious vomiting + down syndrome", "High risk of malrotation, duodenal atresia"),
]
rf_data = [[
Paragraph("<b>Red Flag</b>", ParagraphStyle('rh', fontName='Helvetica-Bold', fontSize=9.5, textColor=white)),
Paragraph("<b>Significance</b>", ParagraphStyle('rh2', fontName='Helvetica-Bold', fontSize=9.5, textColor=white)),
]]
for flag, sig in red_flags:
rf_data.append([
Paragraph(f"<b>! {flag}</b>", ParagraphStyle('rf', fontName='Helvetica-Bold', fontSize=9, textColor=ACCENT_RED)),
Paragraph(sig, ParagraphStyle('rs', fontName='Helvetica', fontSize=9, leading=13)),
])
rf_table = Table(rf_data, colWidths=[7*cm, 9*cm])
rf_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), ACCENT_RED),
('ROWBACKGROUNDS', (0,1), (-1,-1), [HexColor("#FEF0EF"), white]),
('GRID', (0,0), (-1,-1), 0.4, BORDER_GRAY),
('TOPPADDING', (0,0), (-1,-1), 5),
('BOTTOMPADDING', (0,0), (-1,-1), 5),
('LEFTPADDING', (0,0), (-1,-1), 8),
('VALIGN', (0,0), (-1,-1), 'TOP'),
('TEXTCOLOR', (0,0), (-1,0), white),
]))
story.append(rf_table)
story.append(spacer(4))
return story
def make_pain_management(styles):
story = []
story.append(h1_para("10. PAIN MANAGEMENT", styles))
story.append(Paragraph(
"<b>KEY PRINCIPLE:</b> Analgesics do NOT mask surgical conditions. "
"Adequate analgesia actually IMPROVES the physician's ability to assess abdominal pain "
"and does not worsen clinical outcomes. Withholding analgesia is not justified.",
styles['alert_green']
))
story.append(spacer(3))
pm_data = [
[Paragraph("<b>Agent</b>", ParagraphStyle('th', fontName='Helvetica-Bold', fontSize=9.5, textColor=white)),
Paragraph("<b>Dose</b>", ParagraphStyle('th', fontName='Helvetica-Bold', fontSize=9.5, textColor=white)),
Paragraph("<b>Route</b>", ParagraphStyle('th', fontName='Helvetica-Bold', fontSize=9.5, textColor=white)),
Paragraph("<b>Notes</b>", ParagraphStyle('th', fontName='Helvetica-Bold', fontSize=9.5, textColor=white))],
["Paracetamol (Acetaminophen)", "15 mg/kg q4-6h (max 75 mg/kg/day)", "PO / IV / PR", "First-line mild-moderate pain; avoid in hepatic failure"],
["Ibuprofen", "10 mg/kg q6-8h (max 40 mg/kg/day)", "PO", ">3 months; avoid in renal impairment or dehydration"],
["Morphine", "0.05-0.1 mg/kg q2-4h PRN", "IV / SC", "Severe pain; titrate carefully; monitor respiratory status"],
["Fentanyl", "1-2 mcg/kg", "IV / IN", "Fast onset; IN useful when IV access unavailable"],
["Ondansetron", "0.15 mg/kg (max 8mg) q4-8h", "PO / IV", "Anti-emetic; facilitates oral analgesia"],
]
for i in range(1, len(pm_data)):
pm_data[i] = [
Paragraph(str(pm_data[i][0]), ParagraphStyle('p1', fontName='Helvetica-Bold', fontSize=9, textColor=MID_BLUE)),
Paragraph(str(pm_data[i][1]), ParagraphStyle('p2', fontName='Helvetica', fontSize=9)),
Paragraph(str(pm_data[i][2]), ParagraphStyle('p3', fontName='Helvetica', fontSize=9)),
Paragraph(str(pm_data[i][3]), ParagraphStyle('p4', fontName='Helvetica', fontSize=9, leading=13)),
]
pm_table = Table(pm_data, colWidths=[4.5*cm, 4.5*cm, 2.5*cm, 4.5*cm])
pm_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), DARK_BLUE),
('ROWBACKGROUNDS', (0,1), (-1,-1), [PALE_GRAY, white]),
('GRID', (0,0), (-1,-1), 0.4, BORDER_GRAY),
('TOPPADDING', (0,0), (-1,-1), 5),
('BOTTOMPADDING', (0,0), (-1,-1), 5),
('LEFTPADDING', (0,0), (-1,-1), 6),
('VALIGN', (0,0), (-1,-1), 'TOP'),
('TEXTCOLOR', (0,0), (-1,0), white),
]))
story.append(pm_table)
story.append(spacer(4))
return story
def make_functional(styles):
story = []
story.append(h1_para("11. FUNCTIONAL / RECURRENT ABDOMINAL PAIN", styles))
story.append(body(
"Functional abdominal pain (FAP) is very common in childhood. It encompasses conditions such as "
"irritable bowel syndrome (IBS), functional dyspepsia, and functional abdominal pain - not otherwise "
"specified (FAP-NOS). Diagnosis is clinical after excluding organic causes.", styles
))
story.append(h2_para("Rome IV Criteria (FAP-NOS)", styles))
rome_paras = [
bullet("Episodic or continuous abdominal pain at least 4 days per month for at least 2 months", styles),
bullet("Does NOT meet criteria for irritable bowel syndrome, functional dyspepsia, or abdominal migraine", styles),
bullet("Not fully explained by another medical condition", styles),
bullet("Associated with some loss of daily functioning", styles),
]
story += section_box("ROME IV DIAGNOSTIC CRITERIA", rome_paras, styles, bg=HexColor("#E8F5E9"), border=ACCENT_GREEN)
story.append(h2_para("Organic ('Alarm') Features Suggesting Non-Functional Cause", styles))
alarm_data = [
[Paragraph("<b>Feature</b>", ParagraphStyle('th', fontName='Helvetica-Bold', fontSize=9.5, textColor=white)),
Paragraph("<b>Consider</b>", ParagraphStyle('th', fontName='Helvetica-Bold', fontSize=9.5, textColor=white))],
["Involuntary weight loss", "Malignancy, IBD, malabsorption"],
["Nocturnal diarrhea / pain waking from sleep", "IBD, peptic ulcer disease"],
["Blood in stool (visible or occult)", "IBD, polyps, malignancy, intussusception"],
["Perianal disease (fissures, tags, fistulae)", "Crohn's disease"],
["Persistent RUQ or RIF pain", "Gallbladder disease, appendiceal pathology, Crohn's"],
["Unexplained fever", "IBD, infection, malignancy"],
["Family history of IBD, celiac, peptic ulcer", "Genetic predisposition"],
["Elevated ESR, CRP, or anaemia", "Inflammatory or infiltrative pathology"],
["Dysuria, haematuria", "UTI, urolithiasis, renal pathology"],
]
for i in range(1, len(alarm_data)):
alarm_data[i] = [
Paragraph(f"• {alarm_data[i][0]}", ParagraphStyle('a1', fontName='Helvetica', fontSize=9, textColor=ACCENT_RED)),
Paragraph(str(alarm_data[i][1]), ParagraphStyle('a2', fontName='Helvetica', fontSize=9, leading=13)),
]
alarm_table = Table(alarm_data, colWidths=[7*cm, 9*cm])
alarm_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), ACCENT_TEAL),
('ROWBACKGROUNDS', (0,1), (-1,-1), [PALE_GRAY, white]),
('GRID', (0,0), (-1,-1), 0.4, BORDER_GRAY),
('TOPPADDING', (0,0), (-1,-1), 5),
('BOTTOMPADDING', (0,0), (-1,-1), 5),
('LEFTPADDING', (0,0), (-1,-1), 8),
('VALIGN', (0,0), (-1,-1), 'TOP'),
('TEXTCOLOR', (0,0), (-1,0), white),
]))
story.append(alarm_table)
story.append(spacer(3))
story.append(h2_para("Management of FAP", styles))
fap_mgmt = [
bullet("Thorough history and examination to establish positive diagnosis; acknowledge and validate the child's pain", styles),
bullet("Reassure that functional pain is real pain - not 'imagined'; explain brain-gut connection", styles),
bullet("Dietary modifications: trial of low-FODMAP diet; adequate fibre and fluid intake", styles),
bullet("Soluble fibre supplementation may reduce pain frequency", styles),
bullet("Psychological interventions: cognitive behavioral therapy (CBT) is the most evidence-based approach", styles),
bullet("Peppermint oil capsules: may reduce IBS-type symptoms in older children", styles),
bullet("Tricyclic antidepressants (amitriptyline): low dose for refractory cases (caution in younger children)", styles),
bullet("ESPGHAN/NASPGHAN 2025 guidelines endorse CBT and gut-directed hypnotherapy as first-line for functional pain", styles),
]
story += fap_mgmt
story.append(spacer(4))
return story
def make_discharge(styles):
story = []
story.append(h1_para("12. IMAGING APPROACH SUMMARY", styles))
story.append(body(
"The following decision framework summarizes imaging selection in pediatric abdominal pain:", styles
))
flow_data = [
[Paragraph("<b>Clinical Scenario</b>", ParagraphStyle('th', fontName='Helvetica-Bold', fontSize=9.5, textColor=white)),
Paragraph("<b>First-Line Imaging</b>", ParagraphStyle('th', fontName='Helvetica-Bold', fontSize=9.5, textColor=white)),
Paragraph("<b>Second-Line if Needed</b>", ParagraphStyle('th', fontName='Helvetica-Bold', fontSize=9.5, textColor=white))],
["Suspected appendicitis", "Ultrasound (graded compression)", "CT abdomen if US equivocal"],
["Bilious vomiting / suspected volvulus", "Upper GI contrast series", "CT if GI series inconclusive"],
["Suspected intussusception", "Ultrasound (target sign)", "Air/hydrostatic enema (therapeutic)"],
["Neonatal abdominal distension / NEC", "Plain AXR (AP + left lateral decubitus)", "US for portal venous gas"],
["Testicular torsion", "Doppler US testicles", "Surgical exploration if Doppler equivocal"],
["RUQ pain / cholecystitis", "Ultrasound", "HIDA scan if US equivocal"],
["Flank pain / renal stone", "Ultrasound", "Low-dose CT KUB"],
["Ovarian pathology", "Pelvic ultrasound + Doppler", "MRI pelvis"],
["IBD evaluation", "MR enterography / colonoscopy", "US bowel (Crohn's monitoring)"],
["Trauma", "CT abdomen/pelvis with contrast", "FAST US for initial screening"],
]
for i in range(1, len(flow_data)):
flow_data[i] = [
Paragraph(str(flow_data[i][0]), ParagraphStyle('f1', fontName='Helvetica', fontSize=9, textColor=TEXT_DARK)),
Paragraph(f"<b>{flow_data[i][1]}</b>", ParagraphStyle('f2', fontName='Helvetica-Bold', fontSize=9, textColor=ACCENT_TEAL)),
Paragraph(str(flow_data[i][2]), ParagraphStyle('f3', fontName='Helvetica', fontSize=9, textColor=HexColor("#555555"))),
]
flow_table = Table(flow_data, colWidths=[5.5*cm, 5.5*cm, 5*cm])
flow_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), DARK_BLUE),
('ROWBACKGROUNDS', (0,1), (-1,-1), [LIGHT_BLUE, white]),
('GRID', (0,0), (-1,-1), 0.4, BORDER_GRAY),
('TOPPADDING', (0,0), (-1,-1), 5),
('BOTTOMPADDING', (0,0), (-1,-1), 5),
('LEFTPADDING', (0,0), (-1,-1), 6),
('VALIGN', (0,0), (-1,-1), 'TOP'),
('TEXTCOLOR', (0,0), (-1,0), white),
]))
story.append(flow_table)
story.append(spacer(6))
story.append(h1_para("13. DISCHARGE CRITERIA & FOLLOW-UP", styles))
disc_safe = section_box(
"SAFE TO DISCHARGE (with follow-up)",
[
bullet("Well-appearing, tolerating oral fluids, no peritoneal signs", styles),
bullet("Diagnosis of non-specific abdominal pain, gastroenteritis, constipation, or viral syndrome", styles),
bullet("Pain adequately controlled with oral analgesia", styles),
bullet("Reliable caregiver with clear return precautions given", styles),
],
styles, bg=HexColor("#D5F5E3"), border=ACCENT_GREEN
)
story += disc_safe
disc_admit = section_box(
"ADMIT / REFER (do NOT discharge)",
[
bullet("Any surgical red flag (see Section 9)", styles),
bullet("Persistent vomiting unable to tolerate fluids", styles),
bullet("Signs of dehydration or haemodynamic instability", styles),
bullet("Diagnosis not established and symptoms progressing", styles),
bullet("Unreliable social circumstances or inability to return for follow-up", styles),
],
styles, bg=HexColor("#FDE8E8"), border=ACCENT_RED
)
story += disc_admit
story.append(Paragraph(
"<b>Important:</b> Patients discharged without a clear diagnosis MUST have a planned re-examination. "
"Parents should be given explicit written return precautions: worsening pain, bilious vomiting, "
"high fever, blood in stool, distension, or child appears more unwell.",
styles['note']
))
story.append(spacer(4))
return story
def build_pdf():
doc = MedDocTemplate(
OUTPUT,
pagesize=A4,
leftMargin=1.5*cm,
rightMargin=1.5*cm,
topMargin=2.8*cm,
bottomMargin=1.8*cm,
)
styles = build_styles()
story = []
story += make_cover(styles)
story += make_toc(styles)
story += make_intro(styles)
story += make_initial_assessment(styles)
story += make_history(styles)
story += make_examination(styles)
story += make_investigations(styles)
story += make_imaging(styles)
story += make_age_based_dd(styles)
story += make_specific_conditions(styles)
story += make_red_flags(styles)
story += make_pain_management(styles)
story += make_functional(styles)
story += make_discharge(styles)
doc.build(story)
print(f"PDF generated: {OUTPUT}")
if __name__ == "__main__":
build_pdf()
cd /home/daytona/workspace/pediatric-abdominal-pain && python generate_pdf.py 2>…
ls -lh /home/daytona/workspace/pediatric-abdominal-pain/Pediatric_Abdominal_Pain…
Pediatric Abdominal Pain
PDF Document · PDF
| # | Section | Key Content |
|---|---|---|
| 1 | Introduction & Overview | Core principles, gastroenteritis as most common cause, nonspecific pain as diagnosis of exclusion |
| 2 | Initial Assessment Framework | Triage table: Immediate / Urgent / Semi-urgent with likely diagnoses |
| 3 | History Taking | Three-column layout: pain characterization, associated symptoms, past history |
| 4 | Physical Examination | 8-step examination sequence adapted for developmental stage |
| 5 | Laboratory Investigations | Table: test, indication, clinical notes (9 key tests) |
| 6 | Diagnostic Imaging | US first-line, plain X-ray, upper GI series, CT, MRI, contrast enema |
| 7 | Age-Based Differentials | Three-age-group table: 0-3 mo / 3 mo-3 yr / 3-15 yr; emergent vs. non-emergent |
| 8 | Specific Conditions | NEC, volvulus, Hirschsprung's, intussusception (classic triad, target sign), appendicitis (30-40% atypical), DKA, HSP, testicular/ovarian torsion, mesenteric adenitis, PID |
| 9 | Surgical Red Flags | 10 red flag features with significance (bilious vomiting, pneumatosis, free air, currant jelly stool, etc.) |
| 10 | Pain Management | Analgesia does NOT mask surgical disease; dosing table for paracetamol, ibuprofen, morphine, fentanyl |
| 11 | Functional/Recurrent Pain | Rome IV criteria, organic alarm features, CBT as first-line (ESPGHAN/NASPGHAN 2025) |
| 12 | Imaging Summary Table | 10 clinical scenarios matched to first- and second-line imaging |
| 13 | Discharge vs. Admit Criteria | Safe discharge checklist; admit criteria; mandatory re-examination if no clear diagnosis |