~/dka-notes/make_dka_pdf.py
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm, mm
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, KeepTogether
)
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus import PageBreak
OUTPUT = "/tmp/workspace/dka-notes/DKA_Notes.pdf"
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
rightMargin=1.8*cm,
leftMargin=1.8*cm,
topMargin=2*cm,
bottomMargin=2*cm,
)
# ── Colour palette ──────────────────────────────────────────────
C_NAVY = colors.HexColor("#1A3A5C")
C_TEAL = colors.HexColor("#0B6E6E")
C_ORANGE = colors.HexColor("#D45A00")
C_RED = colors.HexColor("#B82020")
C_YELLOW = colors.HexColor("#FFF3B0")
C_LTBLUE = colors.HexColor("#E8F4FD")
C_LTGREEN = colors.HexColor("#E8F8F0")
C_LTRED = colors.HexColor("#FDF0F0")
C_LTORANGE= colors.HexColor("#FFF5E6")
C_GREY = colors.HexColor("#F4F4F4")
C_WHITE = colors.white
C_BLACK = colors.HexColor("#1A1A1A")
styles = getSampleStyleSheet()
def sty(name, **kwargs):
return ParagraphStyle(name, **kwargs)
TITLE_STYLE = sty("Title",
fontSize=22, fontName="Helvetica-Bold",
textColor=C_WHITE, alignment=TA_CENTER,
spaceAfter=4, spaceBefore=4)
SUBTITLE_STYLE = sty("Subtitle",
fontSize=11, fontName="Helvetica",
textColor=C_WHITE, alignment=TA_CENTER,
spaceAfter=2)
H1 = sty("H1",
fontSize=13, fontName="Helvetica-Bold",
textColor=C_WHITE, alignment=TA_LEFT,
spaceBefore=10, spaceAfter=2,
leftIndent=4)
H2 = sty("H2",
fontSize=11, fontName="Helvetica-Bold",
textColor=C_NAVY,
spaceBefore=8, spaceAfter=3)
BODY = sty("Body",
fontSize=9.5, fontName="Helvetica",
textColor=C_BLACK,
spaceBefore=2, spaceAfter=2,
leading=14)
BODY_BOLD = sty("BodyBold",
fontSize=9.5, fontName="Helvetica-Bold",
textColor=C_NAVY,
spaceBefore=2, spaceAfter=2,
leading=14)
SMALL = sty("Small",
fontSize=8.5, fontName="Helvetica",
textColor=C_BLACK, leading=12,
spaceBefore=1, spaceAfter=1)
SMALL_BOLD = sty("SmallBold",
fontSize=8.5, fontName="Helvetica-Bold",
textColor=C_NAVY, leading=12)
WARN = sty("Warn",
fontSize=9, fontName="Helvetica-Bold",
textColor=C_RED, leading=13)
NOTE = sty("Note",
fontSize=8.5, fontName="Helvetica-Oblique",
textColor=C_TEAL, leading=12)
TABLE_HEADER = sty("TH",
fontSize=9, fontName="Helvetica-Bold",
textColor=C_WHITE, alignment=TA_CENTER, leading=12)
TABLE_CELL = sty("TC",
fontSize=8.5, fontName="Helvetica",
textColor=C_BLACK, alignment=TA_LEFT, leading=12)
TABLE_CELL_C = sty("TCC",
fontSize=8.5, fontName="Helvetica",
textColor=C_BLACK, alignment=TA_CENTER, leading=12)
TABLE_CELL_BOLD = sty("TCB",
fontSize=8.5, fontName="Helvetica-Bold",
textColor=C_NAVY, alignment=TA_LEFT, leading=12)
# ── Helper builders ─────────────────────────────────────────────
def section_header(text, bg=C_NAVY):
t = Table([[Paragraph(text, H1)]], colWidths=[doc.width])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), bg),
("ROWPADDING", (0,0), (-1,-1), 6),
("ROUNDEDCORNERS", [4,4,4,4]),
]))
return t
def hr():
return HRFlowable(width="100%", thickness=1, color=C_TEAL, spaceAfter=4, spaceBefore=4)
def sp(h=6):
return Spacer(1, h)
def p(text, style=BODY):
return Paragraph(text, style)
def bullet(items, style=BODY):
out = []
for item in items:
out.append(Paragraph(f"<bullet>\u2022</bullet> {item}", style))
return out
def key_val_table(rows, col1=6.5*cm, col2=None):
if col2 is None:
col2 = doc.width - col1
data = []
for k, v in rows:
data.append([
Paragraph(k, TABLE_CELL_BOLD),
Paragraph(v, TABLE_CELL)
])
t = Table(data, colWidths=[col1, col2])
t.setStyle(TableStyle([
("VALIGN", (0,0), (-1,-1), "TOP"),
("GRID", (0,0), (-1,-1), 0.4, colors.HexColor("#CCCCCC")),
("ROWBACKGROUNDS", (0,0), (-1,-1), [C_WHITE, C_GREY]),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
]))
return t
def generic_table(headers, rows, col_widths, row_colors=None, header_bg=C_NAVY):
data = [[Paragraph(h, TABLE_HEADER) for h in headers]]
for i, row in enumerate(rows):
data.append([Paragraph(str(c), TABLE_CELL) for c in row])
t = Table(data, colWidths=col_widths)
style_cmds = [
("BACKGROUND", (0,0), (-1,0), header_bg),
("VALIGN", (0,0), (-1,-1), "TOP"),
("GRID", (0,0), (-1,-1), 0.4, colors.HexColor("#AAAAAA")),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 5),
("RIGHTPADDING", (0,0), (-1,-1), 5),
]
if row_colors:
for i, bg in enumerate(row_colors):
style_cmds.append(("BACKGROUND", (0, i+1), (-1, i+1), bg))
else:
for i in range(len(rows)):
bg = C_WHITE if i % 2 == 0 else C_GREY
style_cmds.append(("BACKGROUND", (0, i+1), (-1, i+1), bg))
t.setStyle(TableStyle(style_cmds))
return t
def info_box(text, bg=C_LTBLUE, border=C_TEAL):
t = Table([[Paragraph(text, BODY)]], colWidths=[doc.width])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), bg),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
("TOPPADDING", (0,0), (-1,-1), 6),
("BOTTOMPADDING", (0,0), (-1,-1), 6),
("BOX", (0,0), (-1,-1), 1.5, border),
("ROUNDEDCORNERS", [4,4,4,4]),
]))
return t
def warn_box(text):
return info_box(text, bg=C_LTRED, border=C_RED)
def tip_box(text):
return info_box(text, bg=C_LTORANGE, border=C_ORANGE)
# ═══════════════════════════════════════════════════════════════
# BUILD CONTENT
# ═══════════════════════════════════════════════════════════════
story = []
# ── TITLE BANNER ────────────────────────────────────────────────
title_data = [[
Paragraph("DIABETIC KETOACIDOSIS", TITLE_STYLE),
Paragraph("DKA", sty("TL", fontSize=28, fontName="Helvetica-Bold",
textColor=colors.HexColor("#FFFFFF66"),
alignment=TA_CENTER))
]]
title_table = Table(title_data, colWidths=[doc.width - 3*cm, 3*cm])
title_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), C_NAVY),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("TOPPADDING", (0,0), (-1,-1), 12),
("BOTTOMPADDING", (0,0), (-1,-1), 12),
("LEFTPADDING", (0,0), (-1,-1), 12),
("ROUNDEDCORNERS", [6,6,6,6]),
]))
story.append(title_table)
sub_data = [[
Paragraph("Marrow + Textbook Notes | Final Year Theory Exam | ISPAD Guidelines", SUBTITLE_STYLE)
]]
sub_table = Table(sub_data, colWidths=[doc.width])
sub_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), C_TEAL),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
]))
story.append(sub_table)
story.append(sp(10))
# ═══════════════════════════════════════════════════════
# SECTION 1: DIFFERENTIAL DIAGNOSIS
# ═══════════════════════════════════════════════════════
story.append(section_header("1. DIFFERENTIAL DIAGNOSIS — WHEN TO SUSPECT DKA"))
story.append(sp(6))
story.append(p("DKA must be considered in the differential diagnosis of the following presentations:", BODY))
story.append(sp(4))
dd_data = [
[Paragraph("PRESENTATION", TABLE_HEADER),
Paragraph("CONDITIONS TO RULE OUT", TABLE_HEADER),
Paragraph("DKA CLUE", TABLE_HEADER)],
[Paragraph("Encephalopathy / altered sensorium", TABLE_CELL_BOLD),
Paragraph("CNS infections, severe malaria, poisoning", TABLE_CELL),
Paragraph("Hyperglycemia + ketones", TABLE_CELL)],
[Paragraph("Acute abdomen", TABLE_CELL_BOLD),
Paragraph("Pancreatitis, appendicitis", TABLE_CELL),
Paragraph("Abdominal pain d/t acidosis", TABLE_CELL)],
[Paragraph("Dehydration", TABLE_CELL_BOLD),
Paragraph("Gastroenteritis", TABLE_CELL),
Paragraph("No diarrhoea; osmotic diuresis", TABLE_CELL)],
[Paragraph("Tachypnea / respiratory distress", TABLE_CELL_BOLD),
Paragraph("Bronchial asthma, pneumonia", TABLE_CELL),
Paragraph("Kussmaul breathing (deep, not wheeze)", TABLE_CELL)],
[Paragraph("Hyperglycemia + acidosis, NO ketones", TABLE_CELL_BOLD),
Paragraph("Renal failure, septicemia", TABLE_CELL),
Paragraph("Ketonuria absent; BUN very high", TABLE_CELL)],
[Paragraph("Ketoacidosis, NO hyperglycemia", TABLE_CELL_BOLD),
Paragraph("Starvation, salicylate poisoning, organic acidemia", TABLE_CELL),
Paragraph("Glucose normal / low", TABLE_CELL)],
]
dd_table = Table(dd_data, colWidths=[4.5*cm, 6.5*cm, doc.width-11*cm])
dd_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), C_NAVY),
("VALIGN", (0,0), (-1,-1), "TOP"),
("GRID", (0,0), (-1,-1), 0.4, colors.HexColor("#AAAAAA")),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 5),
("RIGHTPADDING", (0,0), (-1,-1), 5),
*[("BACKGROUND", (0,i), (-1,i), C_WHITE if i%2==1 else C_GREY) for i in range(1,8)],
]))
story.append(dd_table)
story.append(sp(8))
# ═══════════════════════════════════════════════════════
# SECTION 2: INVESTIGATIONS
# ═══════════════════════════════════════════════════════
story.append(section_header("2. INVESTIGATIONS", bg=C_TEAL))
story.append(sp(6))
inv_data = [
[Paragraph("INVESTIGATION", TABLE_HEADER),
Paragraph("FINDING IN DKA", TABLE_HEADER),
Paragraph("SIGNIFICANCE / ACTION", TABLE_HEADER)],
[Paragraph("Serum Na⁺", TABLE_CELL_BOLD),
Paragraph("LOW (falsely - pseudohyponatremia)\nTrue deficit: 4-6 mEq/kg", TABLE_CELL),
Paragraph("Use corrected Na⁺. Rapid fall in Na⁺ = risk for cerebral edema", TABLE_CELL)],
[Paragraph("Serum K⁺", TABLE_CELL_BOLD),
Paragraph("NORMAL or HIGH at presentation\nTrue deficit: 3-6 mEq/kg (ICF depleted)", TABLE_CELL),
Paragraph("Becomes dangerously LOW once insulin started. Monitor carefully.", TABLE_CELL)],
[Paragraph("Serum Phosphate", TABLE_CELL_BOLD),
Paragraph("LOW (significant deficit)", TABLE_CELL),
Paragraph("Treat if phosphate < 1 mg/dL", TABLE_CELL)],
[Paragraph("Blood glucose", TABLE_CELL_BOLD),
Paragraph("> 200 mg/dL", TABLE_CELL),
Paragraph("Confirm diagnosis; monitor hourly", TABLE_CELL)],
[Paragraph("Blood gas (VBG)", TABLE_CELL_BOLD),
Paragraph("pH < 7.3, HCO₃ < 15 mEq/L", TABLE_CELL),
Paragraph("Confirms acidosis; severity assessment", TABLE_CELL)],
[Paragraph("Urine/blood ketones", TABLE_CELL_BOLD),
Paragraph("Ketonuria ≥ 2+ on dipstick\nor β-HB ≥ 5.3 mmol/L", TABLE_CELL),
Paragraph("Confirms ketosis", TABLE_CELL)],
[Paragraph("WBC count", TABLE_CELL_BOLD),
Paragraph("Transient leukocytosis = NORMAL in DKA", TABLE_CELL),
Paragraph("PERSISTENT leukocytosis + fever = suspect infection", TABLE_CELL)],
[Paragraph("Blood urea / BUN", TABLE_CELL_BOLD),
Paragraph("High BUN", TABLE_CELL),
Paragraph("Indicator of SEVERE DKA", TABLE_CELL)],
[Paragraph("ECG", TABLE_CELL_BOLD),
Paragraph("May show hypo/hyperkalemia changes", TABLE_CELL),
Paragraph("Peaked T = hyperkalemia; Flat T, U wave = hypokalemia", TABLE_CELL)],
[Paragraph("Serum osmolality", TABLE_CELL_BOLD),
Paragraph("High (due to hyperglycemia)", TABLE_CELL),
Paragraph("If very high: extend fluid correction to 72 hr", TABLE_CELL)],
[Paragraph("Anion gap", TABLE_CELL_BOLD),
Paragraph("Elevated (> 12 mEq/L)", TABLE_CELL),
Paragraph("Should resolve by 12 hr of treatment", TABLE_CELL)],
]
inv_table = Table(inv_data, colWidths=[3.8*cm, 5.8*cm, doc.width-9.6*cm])
inv_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), C_TEAL),
("VALIGN", (0,0), (-1,-1), "TOP"),
("GRID", (0,0), (-1,-1), 0.4, colors.HexColor("#AAAAAA")),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 5),
("RIGHTPADDING", (0,0), (-1,-1), 5),
*[("BACKGROUND", (0,i), (-1,i), C_WHITE if i%2==1 else C_LTBLUE) for i in range(1,13)],
]))
story.append(inv_table)
story.append(sp(6))
story.append(tip_box(
"<b>KEY POINT (High Yield):</b> Serum K⁺ is normal/HIGH at presentation despite total body K⁺ depletion "
"because acidosis drives K⁺ out of cells (ICF → ECF). Once insulin corrects acidosis, "
"K⁺ shifts back into cells → life-threatening HYPOKALEMIA."
))
story.append(sp(10))
# ═══════════════════════════════════════════════════════
# SECTION 3: MANAGEMENT
# ═══════════════════════════════════════════════════════
story.append(section_header("3. MANAGEMENT", bg=C_ORANGE))
story.append(sp(6))
story.append(p("<b>3 Pillars of Management:</b>", BODY_BOLD))
pillars_data = [[
Paragraph("1\nCorrect the Pathology\n(Insulin for hyperglycemia)", sty("P1",
fontSize=9, fontName="Helvetica-Bold", textColor=C_WHITE,
alignment=TA_CENTER, leading=13)),
Paragraph("2\nCorrect Dehydration\n(Fluid restoration)", sty("P2",
fontSize=9, fontName="Helvetica-Bold", textColor=C_WHITE,
alignment=TA_CENTER, leading=13)),
Paragraph("3\nIdentify & Treat\nPrecipitating Event", sty("P3",
fontSize=9, fontName="Helvetica-Bold", textColor=C_WHITE,
alignment=TA_CENTER, leading=13)),
]]
pillars_table = Table(pillars_data, colWidths=[doc.width/3]*3)
pillars_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (0,0), C_NAVY),
("BACKGROUND", (1,0), (1,0), C_TEAL),
("BACKGROUND", (2,0), (2,0), C_ORANGE),
("TOPPADDING", (0,0), (-1,-1), 10),
("BOTTOMPADDING", (0,0), (-1,-1), 10),
("ALIGN", (0,0), (-1,-1), "CENTER"),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("INNERGRID", (0,0), (-1,-1), 2, C_WHITE),
("ROUNDEDCORNERS", [4,4,4,4]),
]))
story.append(pillars_table)
story.append(sp(8))
# ICU criteria
story.append(warn_box(
"<b>ICU ADMISSION CRITERIA:</b> Age < 2 years | Severe DKA (pH < 7.1) "
"| Altered consciousness"
))
story.append(sp(6))
# ── 3A. Initial Stabilization
story.append(p("<b>A. INITIAL STABILIZATION</b>", H2))
init_items = [
"Assess Airway, Breathing, Circulation (ABC)",
"Oxygen and respiratory support if needed",
"IV access; send blood for investigations",
"Insert NG tube + urinary catheter if <b>unconscious</b>",
"<b>Defer intubation</b> - worsens CNS acidosis (IMPORTANT)",
"Nil by mouth",
"Neurological evaluation: pupils, cranial nerves (6th nerve palsy = cerebral edema), DTR",
]
for item in init_items:
story.append(Paragraph(f"<bullet>\u2022</bullet> {item}", BODY))
story.append(sp(6))
# ── 3B. Fluid Therapy
story.append(p("<b>B. FLUID THERAPY (Mainstay of treatment)</b>", H2))
fluid_data = [
[Paragraph("CONDITION", TABLE_HEADER),
Paragraph("FEATURES", TABLE_HEADER),
Paragraph("FLUID REGIME", TABLE_HEADER)],
[Paragraph("Minimal dehydration (Mild DKA)", TABLE_CELL_BOLD),
Paragraph("Tolerates oral fluids", TABLE_CELL),
Paragraph("Oral fluids; S/C insulin", TABLE_CELL)],
[Paragraph("Moderate/Severe dehydration (not in shock)", TABLE_CELL_BOLD),
Paragraph("Vomiting, intolerant to oral, dehydrated", TABLE_CELL),
Paragraph("10-20 mL/kg IV NS (0.9%) over 20-30 min", TABLE_CELL)],
[Paragraph("Shock", TABLE_CELL_BOLD),
Paragraph("Thready pulse, altered consciousness/coma", TABLE_CELL),
Paragraph("Secure ABC; IV bolus 20 mL/kg 0.9% NS", TABLE_CELL)],
]
fluid_table = Table(fluid_data, colWidths=[4.5*cm, 5*cm, doc.width-9.5*cm])
fluid_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), C_ORANGE),
("VALIGN", (0,0), (-1,-1), "TOP"),
("GRID", (0,0), (-1,-1), 0.4, colors.HexColor("#AAAAAA")),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 5),
*[("BACKGROUND", (0,i), (-1,i), C_WHITE if i%2==1 else C_LTORANGE) for i in range(1,4)],
]))
story.append(fluid_table)
story.append(sp(5))
fluid_details = [
("Deficit", "5-10% body weight in most children"),
("Rate", "3-3.5 L/m²/day. <b>Never exceed 4 L/m²/day</b> (cerebral edema risk)"),
("Duration", "Correct over <b>48 hours</b> (72 hr if high plasma osmolality)"),
("IV fluid choice", "<b>Normal saline (NS) for first 6 hours</b>, then Na content 77-154 mEq/L"),
("Add dextrose when", "Blood glucose < <b>270 mg/dL</b> — add 5% dextrose to IV fluid"),
("Important note", "Count fluids already given at referring centre in total calculation"),
]
story.append(key_val_table(fluid_details, col1=5*cm))
story.append(sp(5))
story.append(warn_box(
"<b>WARNING:</b> Rapid and excessive fluid = risk for CEREBRAL EDEMA. "
"Slow rise in serum Na with rapid fall in glucose = cerebral edema risk."
))
story.append(sp(8))
# ── 3C. Insulin Therapy
story.append(p("<b>C. INSULIN THERAPY</b>", H2))
story.append(info_box(
"<b>RULE:</b> Start insulin <b>1 hour AFTER</b> starting fluids. "
"Early insulin = drastic fall in osmolality + hypokalemia + cerebral edema risk."
))
story.append(sp(5))
insulin_details = [
("Route", "<b>IV continuous infusion</b> (preferred). Flush tubing with insulin first — insulin binds to plastic."),
("Standard dose", "<b>0.05-0.1 u/kg/hr</b> IV infusion"),
("Infants / mild DKA", "0.05 u/kg/hr"),
("Increase dose if", "Glucose not falling ≥ 50 mg/dL/hr OR acidosis not resolving → increase by <b>0.02 u/kg/hr</b>"),
("NEVER reduce insulin", "When glucose falls — <b>ADD dextrose instead</b> (reducing insulin prolongs acidosis)"),
("No bolus", "<b>NO initial insulin bolus</b> — C/I due to cerebral edema risk"),
("If no IV available", "IM regular insulin: <b>0.3 u/kg loading</b>, then <b>0.1 u/kg/hr</b>"),
("SC insulin", "<b>NOT recommended</b> in moderate/severe DKA — poor perfusion = poor absorption"),
]
story.append(key_val_table(insulin_details, col1=4.5*cm))
story.append(sp(8))
# ── 3D. Potassium Protocol
story.append(p("<b>D. POTASSIUM REPLACEMENT PROTOCOL (High Yield)</b>", H2))
k_data = [
[Paragraph("SERUM K⁺ LEVEL", TABLE_HEADER),
Paragraph("ACTION", TABLE_HEADER),
Paragraph("REASON", TABLE_HEADER)],
[Paragraph("< 3.5 mEq/L", sty("kl", fontSize=9.5, fontName="Helvetica-Bold",
textColor=C_RED, leading=12)),
Paragraph("<b>Give K⁺ BEFORE starting insulin</b>", TABLE_CELL),
Paragraph("Insulin will drop K⁺ further - dangerous", TABLE_CELL)],
[Paragraph("3.5 - 6.0 mEq/L", TABLE_CELL_BOLD),
Paragraph("Add <b>40 mEq/L KCl</b> to IV fluid at time of starting insulin", TABLE_CELL),
Paragraph("Standard protocol", TABLE_CELL)],
[Paragraph("> 6.0 mEq/L\nOR anuric\nOR ECG hyperkalemia changes", sty("kh", fontSize=9.5,
fontName="Helvetica-Bold", textColor=C_RED, leading=12)),
Paragraph("<b>DO NOT give K⁺</b>", TABLE_CELL),
Paragraph("Risk of fatal hyperkalemia", TABLE_CELL)],
]
k_table = Table(k_data, colWidths=[3.8*cm, 7*cm, doc.width-10.8*cm])
k_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), C_NAVY),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("GRID", (0,0), (-1,-1), 0.4, colors.HexColor("#AAAAAA")),
("TOPPADDING", (0,0), (-1,-1), 6),
("BOTTOMPADDING", (0,0), (-1,-1), 6),
("LEFTPADDING", (0,0), (-1,-1), 6),
("BACKGROUND", (0,1), (-1,1), C_LTRED),
("BACKGROUND", (0,2), (-1,2), C_LTGREEN),
("BACKGROUND", (0,3), (-1,3), C_LTRED),
]))
story.append(k_table)
story.append(sp(8))
# ── 3E. Acid-base / Bicarbonate
story.append(p("<b>E. BICARBONATE THERAPY</b>", H2))
story.append(warn_box(
"<b>AVOID bicarbonate routinely</b> — risks: cerebral edema, lactic acidosis, hypokalemia, worsens CNS acidosis."
))
story.append(sp(4))
story.append(info_box(
"<b>EXCEPTION — Give bicarbonate ONLY IF:</b><br/>"
"1. pH < 6.9 WITH hemodynamic compromise, OR<br/>"
"2. Severe hyperkalemia: K⁺ > 6.5 mEq/L with ECG changes"
))
story.append(sp(8))
# ── 3F. Dextrose rule
story.append(p("<b>F. DEXTROSE RULE</b>", H2))
story.append(info_box(
"Add <b>5% dextrose</b> to IV fluids once blood glucose < <b>270-300 mg/dL</b>.<br/>"
"Hyperglycemia resolves BEFORE acidosis — so do NOT stop insulin; add dextrose to maintain infusion."
))
story.append(sp(8))
# ── 3G. Transition / Stopping treatment
story.append(p("<b>G. DISCONTINUATION OF ACUTE TREATMENT</b>", H2))
transition_items = [
"Switch to S/C insulin when: <b>conscious + tolerating oral feeds + acidosis resolved</b>",
"Give <b>regular/rapid-acting insulin 0.25 u/kg</b> S/C 30 minutes BEFORE first meal",
"Stop IV insulin infusion <b>30 minutes AFTER</b> S/C dose (overlap prevents rebound hyperglycemia)",
"Start basal-bolus or mix-split SC insulin regime",
]
for item in transition_items:
story.append(Paragraph(f"<bullet>\u2022</bullet> {item}", BODY))
story.append(sp(10))
# ═══════════════════════════════════════════════════════
# SECTION 4: COMPLICATIONS
# ═══════════════════════════════════════════════════════
story.append(section_header("4. COMPLICATIONS OF DKA", bg=C_RED))
story.append(sp(6))
# Cerebral edema box
ce_header = Table([[Paragraph(
"CEREBRAL EDEMA — Most common cause of death in DKA",
sty("CEH", fontSize=11, fontName="Helvetica-Bold", textColor=C_WHITE, alignment=TA_CENTER)
)]], colWidths=[doc.width])
ce_header.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), C_RED),
("TOPPADDING", (0,0), (-1,-1), 6),
("BOTTOMPADDING", (0,0), (-1,-1), 6),
]))
story.append(ce_header)
story.append(sp(4))
ce_left = [
[Paragraph("TIMING", TABLE_CELL_BOLD)],
[Paragraph("Usually <b>4-12 hours</b> after starting treatment (rarely at diagnosis)", TABLE_CELL)],
[Paragraph("", TABLE_CELL)],
[Paragraph("RISK FACTORS (mnemonic: ASHIC)", TABLE_CELL_BOLD)],
[Paragraph("<b>A</b>ge < 5 years", TABLE_CELL)],
[Paragraph("<b>S</b>evere acidosis", TABLE_CELL)],
[Paragraph("<b>H</b>ydration excessive (rapid/excess IV fluids)", TABLE_CELL)],
[Paragraph("<b>I</b>nsulin bolus given", TABLE_CELL)],
[Paragraph("<b>C</b>O₂ < 10 mmol/L; alkali treatment", TABLE_CELL)],
[Paragraph("Rapid fall in serum Na⁺ or osmolality", TABLE_CELL)],
]
ce_right = [
[Paragraph("EARLY SIGNS", TABLE_CELL_BOLD)],
[Paragraph("Headache, vomiting, drowsiness, irritability", TABLE_CELL)],
[Paragraph("<b>Hypertension + Bradycardia</b> (Cushing's triad)", TABLE_CELL)],
[Paragraph("", TABLE_CELL)],
[Paragraph("LATE/SEVERE SIGNS", TABLE_CELL_BOLD)],
[Paragraph("Unconsciousness", TABLE_CELL)],
[Paragraph("Focal neurological deficits", TABLE_CELL)],
[Paragraph("<b>Papilledema</b>, fixed dilated pupils", TABLE_CELL)],
[Paragraph("<b>6th nerve palsy</b> (early sign)", TABLE_CELL)],
[Paragraph("Brisk DTR (raised ICP)", TABLE_CELL)],
]
ce_cols_left = Table(ce_left, colWidths=[doc.width/2 - 0.5*cm])
ce_cols_right = Table(ce_right, colWidths=[doc.width/2 - 0.5*cm])
ce_cols_left.setStyle(TableStyle([
("VALIGN", (0,0), (-1,-1), "TOP"),
("LEFTPADDING", (0,0), (-1,-1), 4),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
]))
ce_cols_right.setStyle(TableStyle([
("VALIGN", (0,0), (-1,-1), "TOP"),
("LEFTPADDING", (0,0), (-1,-1), 4),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
]))
ce_two_col = Table([[ce_cols_left, ce_cols_right]], colWidths=[doc.width/2, doc.width/2])
ce_two_col.setStyle(TableStyle([
("VALIGN", (0,0), (-1,-1), "TOP"),
("BOX", (0,0), (-1,-1), 1, C_RED),
("INNERGRID", (0,0), (-1,-1), 0.5, colors.HexColor("#FFCCCC")),
("BACKGROUND", (0,0), (-1,-1), C_LTRED),
("LEFTPADDING", (0,0), (-1,-1), 4),
("TOPPADDING", (0,0), (-1,-1), 6),
("BOTTOMPADDING", (0,0), (-1,-1), 6),
]))
story.append(ce_two_col)
story.append(sp(5))
story.append(info_box(
"<b>TREATMENT OF CEREBRAL EDEMA:</b><br/>"
"1. IV <b>Mannitol 5 mL/kg</b> stat (osmotic agent)<br/>"
"2. <b>Fluid restriction</b><br/>"
"3. <b>Head elevation</b><br/>"
"4. Diagnosis is CLINICAL — do not wait for imaging<br/>"
"5. Hypertonic saline (3%) 2.5-5 mL/kg may be used as alternative to mannitol",
bg=C_LTRED, border=C_RED
))
story.append(sp(8))
# Other complications
story.append(p("<b>OTHER COMPLICATIONS</b>", H2))
other_comp = [
("Hypokalemia", "Life-threatening; occurs after insulin starts; prevent with K⁺ protocol"),
("Hypoglycemia", "Add dextrose when glucose < 270 mg/dL"),
("Hypernatremia", "From excessive NS; monitor corrected Na⁺"),
("Infection (bacterial/fungal)", "Persistent fever + leukocytosis. Watch for:<br/>- <b>Black nasal discharge</b> = Rhinocerebral mucormycosis<br/>- <b>Hemoptysis</b> = Pulmonary aspergillosis"),
("Cerebral thrombosis", "Rare; due to dehydration and hyperviscosity"),
("Aspiration pneumonia", "From vomiting; hence NG tube if unconscious"),
("Acute renal failure", "From severe dehydration; high BUN indicates severity"),
]
story.append(key_val_table(other_comp, col1=4.5*cm))
story.append(sp(10))
# ═══════════════════════════════════════════════════════
# SECTION 5: MONITORING
# ═══════════════════════════════════════════════════════
story.append(section_header("5. MONITORING", bg=C_TEAL))
story.append(sp(6))
story.append(p("<b>Clinical Monitoring (Hourly):</b>", BODY_BOLD))
clin_items = [
"Neurological status (GCS, pupils, cranial nerves)",
"Heart rate, blood pressure",
"Fluid input/output",
"Signs of cerebral edema: headache, vomiting, drowsiness, bradycardia",
]
for item in clin_items:
story.append(Paragraph(f"<bullet>\u2022</bullet> {item}", BODY))
story.append(sp(5))
story.append(p("<b>Laboratory Monitoring:</b>", BODY_BOLD))
lab_freq = [
[Paragraph("PARAMETER", TABLE_HEADER),
Paragraph("FREQUENCY", TABLE_HEADER)],
[Paragraph("Blood glucose", TABLE_CELL),
Paragraph("Every 1 hour", TABLE_CELL)],
[Paragraph("Blood ketones", TABLE_CELL),
Paragraph("Every 4 hours", TABLE_CELL)],
[Paragraph("pH, bicarbonate, electrolytes", TABLE_CELL),
Paragraph("Every 4 hours", TABLE_CELL)],
[Paragraph("Serum sodium (corrected)", TABLE_CELL),
Paragraph("Every 4 hours", TABLE_CELL)],
]
lab_table = Table(lab_freq, colWidths=[doc.width*0.55, doc.width*0.45])
lab_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), C_TEAL),
("GRID", (0,0), (-1,-1), 0.4, colors.HexColor("#AAAAAA")),
("ROWBACKGROUNDS", (0,1), (-1,-1), [C_WHITE, C_LTBLUE]),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
story.append(lab_table)
story.append(sp(8))
# Response to treatment table (high yield)
story.append(p("<b>RESPONSE TO TREATMENT TABLE (Table 18.36 - High Yield for Exams)</b>", BODY_BOLD))
story.append(sp(4))
rtt_data = [
[Paragraph("PARAMETER", TABLE_HEADER),
Paragraph("EXPECTED RESPONSE", TABLE_HEADER),
Paragraph("CONCERN", TABLE_HEADER),
Paragraph("ACTION", TABLE_HEADER)],
[Paragraph("Blood glucose", TABLE_CELL_BOLD),
Paragraph("Decrease 50-100 mg/dL/hr", TABLE_CELL),
Paragraph("Fall > 100 mg/dL/hr\nFall < 50 mg/dL/hr", TABLE_CELL),
Paragraph("Add dextrose to IV fluid\nPrepare fresh insulin; flush tubing", TABLE_CELL)],
[Paragraph("Blood pH", TABLE_CELL_BOLD),
Paragraph("Resolution by 12 hours", TABLE_CELL),
Paragraph("Persistent at 12 hours", TABLE_CELL),
Paragraph("Exclude infection, shock, lactic acidosis", TABLE_CELL)],
[Paragraph("Serum Na⁺", TABLE_CELL_BOLD),
Paragraph("Should increase", TABLE_CELL),
Paragraph("Rise < 2 mmol/L/hr", TABLE_CELL),
Paragraph("Increase Na concentration in IV fluid", TABLE_CELL)],
[Paragraph("Serum K⁺", TABLE_CELL_BOLD),
Paragraph("Gradual decrease", TABLE_CELL),
Paragraph("Hypokalemia", TABLE_CELL),
Paragraph("Increase K⁺ in IV fluid", TABLE_CELL)],
[Paragraph("Anion gap", TABLE_CELL_BOLD),
Paragraph("Resolution by 12 hours", TABLE_CELL),
Paragraph("Elevated at 12 hours", TABLE_CELL),
Paragraph("Exclude lactic acidosis, consider infection", TABLE_CELL)],
[Paragraph("Plasma osmolality", TABLE_CELL_BOLD),
Paragraph("Stable", TABLE_CELL),
Paragraph("Decrease > 2 mOsm/kg/hr", TABLE_CELL),
Paragraph("Increase Na concentration; decrease fluid rate", TABLE_CELL)],
[Paragraph("Blood urea", TABLE_CELL_BOLD),
Paragraph("Should decrease", TABLE_CELL),
Paragraph("Persistently elevated", TABLE_CELL),
Paragraph("Exclude renal failure", TABLE_CELL)],
]
rtt_col = [3.2*cm, 4*cm, 4*cm, doc.width-11.2*cm]
rtt_table = Table(rtt_data, colWidths=rtt_col)
rtt_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), C_TEAL),
("VALIGN", (0,0), (-1,-1), "TOP"),
("GRID", (0,0), (-1,-1), 0.4, colors.HexColor("#AAAAAA")),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 5),
("RIGHTPADDING", (0,0), (-1,-1), 5),
*[("BACKGROUND", (0,i), (-1,i), C_WHITE if i%2==1 else C_LTBLUE) for i in range(1,9)],
]))
story.append(rtt_table)
story.append(sp(10))
# ── FOOTER
footer_data = [[
Paragraph(
"DKA Notes | Marrow + Essential Pediatrics | ISPAD Guidelines | Final Year Theory Exam",
sty("FT", fontSize=8, fontName="Helvetica", textColor=C_WHITE, alignment=TA_CENTER)
)
]]
footer = Table(footer_data, colWidths=[doc.width])
footer.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), C_NAVY),
("TOPPADDING", (0,0), (-1,-1), 6),
("BOTTOMPADDING", (0,0), (-1,-1), 6),
]))
story.append(footer)
# BUILD
doc.build(story)
print(f"PDF created: {OUTPUT}")