Hypotension during dialysis what should be the management

Reading File
Finding Sources
Finding Sources
Searching PubMed

intradialytic hypotension management hemodialysis

Reading File
Reading File
Reading File
Reading File
Reading File
Finding Sources
Reading File
Reading File
I now have comprehensive, detailed information from three major nephrology textbooks. Let me compile the full answer.

Intradialytic Hypotension (IDH) - Management

Intradialytic hypotension is the most common acute complication of hemodialysis, occurring in 15-50% of treatments depending on the definition. It ranges from asymptomatic episodes to severe organ hypoperfusion causing myocardial ischemia, arrhythmias, vascular thrombosis, seizures, or death. Recurrent IDH is an independent risk factor for mortality.

Definition

A fall in systolic BP of ≥20 mmHg, or a drop to <90 mmHg with symptoms, during or immediately after dialysis.

Pathophysiology / Risk Factors

FactorMechanism
Excessive ultrafiltration (UF)Intravascular volume depletion exceeding refilling capacity
High interdialytic weight gain (IDWG)Forces rapid fluid removal
Impaired autonomic/vasoactive responsesDiabetic or uremic autonomic dysfunction
Osmolar shiftsRapid solute removal reduces plasma osmolality
Antihypertensive drugsReduce arteriolar tone before/during dialysis
Reduced cardiac reserveHeart failure, ischemia, pericardial effusion
Eating during dialysisSplanchnic vasodilatation shunts blood to gut
High UF rate>13 mL/kg/h strongly associated with IDH and mortality
Early IDH (first half of session) is particularly ominous - often triggered independent of volume changes, associated with worse outcomes. Late IDH is typically due to high UF rates from large IDWGs. - Comprehensive Clinical Nephrology, 7th Ed.

Immediate / Acute Management

When hypotension occurs during a session:
  1. Place patient in Trendelenburg position (legs elevated) - improves venous return immediately
  2. Reduce or stop ultrafiltration - this is the primary step
  3. Infuse 0.9% isotonic saline in boluses of ≥100 mL (typically 100-250 mL), repeating as needed
  4. Salt-poor albumin is an alternative if saline is insufficient, though it has no proven advantage over saline and is expensive - generally unavailable in outpatient settings
  5. Do NOT routinely reduce blood flow rate - this does not help and will compromise solute clearance (Kt/V)
Rule out cardiac cause: If hypotension accompanies chest pain or dyspnea, obtain a 12-lead ECG and serum troponin to exclude ischemia. If recurrent unexplained IDH occurs, consider echocardiography to rule out pericarditis or pericardial effusion.
  • Harrison's Principles of Internal Medicine, 22E (2025)
  • Comprehensive Clinical Nephrology, 7th Ed.

Prevention Strategies

A. Patient-Related Measures

InterventionDetails
Assess dry weight accuratelyReassess regularly; overestimated dry weight leads to excessive UF
Restrict dietary sodiumTarget ~5-6 g salt/day (100 mmol Na/day) to reduce interdialytic weight gain
Avoid antihypertensives before dialysisEspecially those reducing arteriolar tone; prefer longer-acting agents taken after dialysis on treatment days
No food during dialysisEating causes splanchnic vasodilatation - high-risk diabetics should eat after dialysis
Correct anemiaImproves cardiac output and hemodynamic tolerance
Treat heart failure and arrhythmiasOptimise cardiac reserve
Counsel on IDWG limitationEducation on fluid and sodium restriction

B. Dialysis Prescription Modifications

InterventionDetails
Cool dialysateReduce dialysate temperature by 0.5-1°C below body temperature (stay above 35°C); increases vascular resistance and cardiac contractility via sympathetic activation; may reduce myocardial stunning and brain ischemia
Extend treatment time or increase frequencyMore time = lower required UF rate; 6× vs 3× weekly dialysis improves BP control and hemodynamics
Bicarbonate dialysateUse bicarbonate buffer instead of acetate - improves hemodynamic stability
Volumetric UF controlUF profiling - remove more fluid early in session when plasma refilling is greatest
Sequential dialysis (isolated UF then HD)Separation of UF from diffusion may improve tolerability, though evidence is limited
Lower dialysate sodiumMatch dialysate sodium to patient's own serum sodium to avoid positive sodium balance and IDWG; avoid sodium modeling/profiling routinely as it is associated with increased all-cause mortality in one study
UF rate ≤13 mL/kg/hHigher UF rates independently increase mortality risk
  • Comprehensive Clinical Nephrology, 7th Ed., p. 1298-1299
  • Brenner and Rector's The Kidney, Box 63.3

C. Pharmacological Prevention (if above fail)

Midodrine - the primary pharmacological option:
  • Oral selective α1-adrenergic agonist
  • Dose: 2.5-10 mg given 15-30 minutes before dialysis
  • Peak levels at ~60 minutes
  • Partially removed by HD - a second, smaller dose may be needed mid-session
  • Side effects: supine hypertension, urinary retention, pilomotor reactions, GI complaints
  • A meta-analysis confirmed benefit in resistant IDH and autonomic insufficiency
  • Comprehensive Clinical Nephrology, 7th Ed.
  • Brenner and Rector's The Kidney

Summary Algorithm

IDH occurs during session
        |
        ↓
1. Trendelenburg position
2. Stop/reduce ultrafiltration
3. IV 0.9% saline bolus 100-250 mL
4. Repeat saline bolus if needed
        |
        ↓
If chest pain/dyspnea → ECG + Troponin (rule out ACS)
        |
        ↓
If recurrent IDH → Echocardiography (pericardial effusion, LV dysfunction)
        |
        ↓
Prevention:
- Assess dry weight, reduce IDWG
- Cool dialysate
- Extend/increase dialysis sessions
- Hold antihypertensives pre-dialysis
- No food during dialysis
- If persistent → Midodrine 2.5-10 mg pre-dialysis

Key Takeaways

  • IDH is the most common acute complication of HD and carries significant mortality risk
  • Immediate treatment: Trendelenburg + stop UF + isotonic saline bolus; saline is as effective as albumin
  • Do NOT reduce blood flow rate - this worsens dialysis adequacy without benefit
  • Always consider cardiac cause (ischemia, pericardial effusion) in recurrent or atypical IDH
  • Midodrine 2.5-10 mg pre-dialysis is the pharmacological option when other strategies fail
  • A UF rate >13 mL/kg/h is independently associated with increased mortality and should be avoided
  • Cool dialysate and extended/frequent dialysis are key procedural preventive strategies
Sources: Harrison's Principles of Internal Medicine 22E (2025); Comprehensive Clinical Nephrology, 7th Ed.; Brenner and Rector's The Kidney

Create a quick reference guide (PDF) on IDH management for dialysis units

Reading File
Reading File
Running Command

ls -la /home/daytona/workspace/ 2>/dev/null || echo "workspace does not exist"

Running Command

mkdir -p /home/daytona/workspace/idh-guide && python3 -c "import reportlab; prin…

Writing File

~/idh-guide/generate_idh_pdf.py

from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import mm, cm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    HRFlowable, KeepTogether
)
from reportlab.platypus.flowables import Flowable
from reportlab.lib.colors import HexColor

OUTPUT = "/home/daytona/workspace/idh-guide/IDH_Quick_Reference_Guide.pdf"

# ── Colour palette ────────────────────────────────────────────────────────────
NAVY       = HexColor("#1B3A5C")
TEAL       = HexColor("#1A7F8E")
RED_ALERT  = HexColor("#C0392B")
AMBER      = HexColor("#E67E22")
GREEN_DARK = HexColor("#1E8449")
LIGHT_BLUE = HexColor("#D6EAF8")
LIGHT_TEAL = HexColor("#D1F2EB")
LIGHT_AMBER= HexColor("#FDEBD0")
LIGHT_RED  = HexColor("#FADBD8")
LIGHT_GREY = HexColor("#F2F3F4")
MID_GREY   = HexColor("#BDC3C7")
WHITE      = colors.white
BLACK      = colors.black

PAGE_W, PAGE_H = A4
MARGIN = 18 * mm

# ── Document ──────────────────────────────────────────────────────────────────
doc = SimpleDocTemplate(
    OUTPUT,
    pagesize=A4,
    leftMargin=MARGIN, rightMargin=MARGIN,
    topMargin=15*mm, bottomMargin=15*mm,
    title="IDH Quick Reference Guide",
    author="Dialysis Unit Protocol",
)

# ── Styles ────────────────────────────────────────────────────────────────────
base = getSampleStyleSheet()

def S(name, **kw):
    return ParagraphStyle(name, **kw)

TITLE_STYLE = S("Title",
    fontName="Helvetica-Bold", fontSize=20, textColor=WHITE,
    alignment=TA_CENTER, spaceAfter=2)

SUBTITLE_STYLE = S("Subtitle",
    fontName="Helvetica", fontSize=10, textColor=HexColor("#D6EAF8"),
    alignment=TA_CENTER, spaceAfter=0)

SECTION_HDR = S("SecHdr",
    fontName="Helvetica-Bold", fontSize=11, textColor=WHITE,
    alignment=TA_LEFT, spaceAfter=0, spaceBefore=0,
    leftIndent=4, leading=14)

BODY = S("Body",
    fontName="Helvetica", fontSize=8.5, textColor=BLACK,
    leading=13, spaceAfter=2)

BODY_BOLD = S("BodyBold",
    fontName="Helvetica-Bold", fontSize=8.5, textColor=BLACK,
    leading=13, spaceAfter=2)

SMALL = S("Small",
    fontName="Helvetica", fontSize=7.5, textColor=HexColor("#555555"),
    leading=11, spaceAfter=1)

ALERT = S("Alert",
    fontName="Helvetica-Bold", fontSize=9, textColor=RED_ALERT,
    leading=12)

BULLET = S("Bullet",
    fontName="Helvetica", fontSize=8.5, textColor=BLACK,
    leading=13, leftIndent=10, bulletIndent=2, spaceAfter=1)

NOTE = S("Note",
    fontName="Helvetica-Oblique", fontSize=7.5, textColor=HexColor("#2C3E50"),
    leading=11)

# ── Helper: coloured section header ──────────────────────────────────────────
def section_header(text, bg=NAVY, width=None):
    w = width or (PAGE_W - 2*MARGIN)
    data = [[Paragraph(text, SECTION_HDR)]]
    t = Table(data, colWidths=[w])
    t.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), bg),
        ("TOPPADDING",    (0,0), (-1,-1), 5),
        ("BOTTOMPADDING", (0,0), (-1,-1), 5),
        ("LEFTPADDING",   (0,0), (-1,-1), 8),
        ("RIGHTPADDING",  (0,0), (-1,-1), 8),
        ("VALIGN",        (0,0), (-1,-1), "MIDDLE"),
    ]))
    return t

def hline(color=MID_GREY, thickness=0.5):
    return HRFlowable(width="100%", thickness=thickness, color=color, spaceAfter=4, spaceBefore=4)

def sp(h=4):
    return Spacer(1, h)

def bp(text, bold_part=""):
    if bold_part:
        return Paragraph(f"<bullet>&bull;</bullet> <b>{bold_part}</b> {text}", BULLET)
    return Paragraph(f"<bullet>&bull;</bullet> {text}", BULLET)

# ── TITLE BANNER ──────────────────────────────────────────────────────────────
def title_banner():
    data = [[
        Paragraph("INTRADIALYTIC HYPOTENSION (IDH)", TITLE_STYLE),
        Paragraph("Quick Reference Guide for Dialysis Units", SUBTITLE_STYLE),
    ]]
    # Stack vertically in one cell
    inner = [
        [Paragraph("INTRADIALYTIC HYPOTENSION (IDH)", TITLE_STYLE)],
        [Paragraph("Quick Reference Guide  |  Hemodialysis Unit Protocol", SUBTITLE_STYLE)],
    ]
    t = Table(inner, colWidths=[PAGE_W - 2*MARGIN])
    t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,-1), NAVY),
        ("TOPPADDING",    (0,0), (-1,-1), 10),
        ("BOTTOMPADDING", (0,0), (-1,-1), 10),
        ("LEFTPADDING",   (0,0), (-1,-1), 12),
        ("RIGHTPADDING",  (0,0), (-1,-1), 12),
        ("ALIGN",         (0,0), (-1,-1), "CENTER"),
        ("ROUNDEDCORNERS",(0,0), (-1,-1), [6,6,6,6]),  # not supported but harmless
    ]))
    return t

# ── DEFINITION BOX ────────────────────────────────────────────────────────────
def definition_box():
    def_style = S("Def", fontName="Helvetica", fontSize=8.5, textColor=NAVY,
                  leading=13, alignment=TA_CENTER)
    def_bold  = S("DefB", fontName="Helvetica-Bold", fontSize=9, textColor=NAVY,
                  leading=14, alignment=TA_CENTER)
    data = [
        [Paragraph("DEFINITION", S("DH", fontName="Helvetica-Bold", fontSize=9,
                                    textColor=TEAL, alignment=TA_CENTER))],
        [Paragraph(
            "Systolic BP fall <b>&ge;20 mmHg</b> during dialysis, "
            "<i>OR</i> BP drop to <b>&lt;90 mmHg</b> with symptoms",
            S("DB2", fontName="Helvetica", fontSize=9, textColor=NAVY,
              leading=14, alignment=TA_CENTER))],
        [Paragraph(
            "Incidence: <b>15 - 50%</b> of HD sessions  |  Independent risk factor for mortality",
            SMALL)],
    ]
    t = Table(data, colWidths=[PAGE_W - 2*MARGIN])
    t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,-1), LIGHT_BLUE),
        ("TOPPADDING",    (0,0), (-1,-1), 6),
        ("BOTTOMPADDING", (0,0), (-1,-1), 6),
        ("LEFTPADDING",   (0,0), (-1,-1), 10),
        ("RIGHTPADDING",  (0,0), (-1,-1), 10),
        ("ALIGN",         (0,0), (-1,-1), "CENTER"),
        ("LINEBELOW",     (0,0), (0,0), 0.5, TEAL),
    ]))
    return t

# ── TWO-COLUMN LAYOUT HELPER ──────────────────────────────────────────────────
def two_col(left_items, right_items, col_ratio=(1,1), bg_left=LIGHT_GREY, bg_right=LIGHT_GREY):
    total_w = PAGE_W - 2*MARGIN - 4  # 4mm gutter
    lw = total_w * col_ratio[0] / sum(col_ratio)
    rw = total_w * col_ratio[1] / sum(col_ratio)
    data = [[left_items, right_items]]
    t = Table(data, colWidths=[lw, rw], hAlign="LEFT")
    t.setStyle(TableStyle([
        ("VALIGN",        (0,0), (-1,-1), "TOP"),
        ("TOPPADDING",    (0,0), (-1,-1), 0),
        ("BOTTOMPADDING", (0,0), (-1,-1), 0),
        ("LEFTPADDING",   (0,0), (-1,-1), 0),
        ("RIGHTPADDING",  (0,0), (-1,-1), 4),
    ]))
    return t

# ── RISK FACTORS TABLE ────────────────────────────────────────────────────────
def risk_factors_table():
    header_style = S("RH", fontName="Helvetica-Bold", fontSize=8,
                     textColor=WHITE, alignment=TA_CENTER)
    cell_style   = S("RC", fontName="Helvetica", fontSize=8,
                     textColor=BLACK, leading=11)

    data = [
        [Paragraph("RISK FACTOR / CAUSE", header_style),
         Paragraph("MECHANISM", header_style)],
        ["Excessive ultrafiltration rate (>13 mL/kg/h)",
         "UF rate exceeds intravascular refilling"],
        ["High interdialytic weight gain (IDWG)",
         "Requires rapid, large-volume fluid removal"],
        ["Diabetic / uremic autonomic dysfunction",
         "Impaired vasopressor response"],
        ["Antihypertensives taken pre-dialysis",
         "Reduced arteriolar tone during session"],
        ["Food / eating during dialysis",
         "Splanchnic vasodilatation shunts blood to gut"],
        ["Reduced cardiac reserve (HF, ischemia)",
         "Inability to augment cardiac output"],
        ["Pericardial effusion / pericarditis",
         "Tamponade physiology; impaired filling"],
        ["Anaemia",
         "Reduced oxygen delivery; poor compensation"],
        ["Osmolar shifts (rapid solute removal)",
         "Reduced plasma osmolality; volume redistribution"],
    ]

    col_w = (PAGE_W - 2*MARGIN) / 2
    t = Table(data, colWidths=[col_w, col_w])
    style = TableStyle([
        ("BACKGROUND",    (0,0), (1,0), NAVY),
        ("TEXTCOLOR",     (0,0), (1,0), WHITE),
        ("FONTNAME",      (0,0), (1,0), "Helvetica-Bold"),
        ("FONTSIZE",      (0,0), (-1,-1), 8),
        ("TOPPADDING",    (0,0), (-1,-1), 4),
        ("BOTTOMPADDING", (0,0), (-1,-1), 4),
        ("LEFTPADDING",   (0,0), (-1,-1), 6),
        ("RIGHTPADDING",  (0,0), (-1,-1), 6),
        ("ROWBACKGROUNDS",(0,1), (-1,-1), [WHITE, LIGHT_GREY]),
        ("GRID",          (0,0), (-1,-1), 0.4, MID_GREY),
        ("VALIGN",        (0,0), (-1,-1), "MIDDLE"),
        ("ALIGN",         (0,0), (1,0), "CENTER"),
    ])
    t.setStyle(style)
    return t

# ── IMMEDIATE MANAGEMENT STEPS ────────────────────────────────────────────────
def immediate_mgmt_table():
    step_hdr = S("StH", fontName="Helvetica-Bold", fontSize=9,
                 textColor=WHITE, alignment=TA_CENTER)
    step_num = S("SN",  fontName="Helvetica-Bold", fontSize=14,
                 textColor=WHITE, alignment=TA_CENTER, leading=18)
    step_act = S("SA",  fontName="Helvetica-Bold", fontSize=9,
                 textColor=NAVY, leading=13)
    step_det = S("SD",  fontName="Helvetica", fontSize=8,
                 textColor=HexColor("#333333"), leading=12)

    steps = [
        ("1", RED_ALERT,   "TRENDELENBURG POSITION",
         "Lay patient supine, elevate legs immediately to improve venous return"),
        ("2", HexColor("#A93226"), "STOP / REDUCE ULTRAFILTRATION",
         "This is the primary intervention. Cease or reduce UF to zero"),
        ("3", AMBER,       "IV ISOTONIC SALINE BOLUS",
         "0.9% NaCl 100-250 mL IV bolus. Repeat as needed until BP stabilises"),
        ("4", HexColor("#1A5276"), "REASSESS & MONITOR",
         "Re-check BP every 5 min; assess for chest pain, dyspnoea, arrhythmia"),
        ("5", GREEN_DARK,  "ECG + TROPONIN if chest pain / dyspnoea",
         "Rule out ACS. Consider echo if recurrent unexplained IDH (pericardial effusion?)"),
    ]

    rows = []
    for num, col, action, detail in steps:
        rows.append([
            Paragraph(num, step_num),
            [Paragraph(action, step_act), Paragraph(detail, step_det)]
        ])

    t = Table(rows, colWidths=[18*mm, PAGE_W - 2*MARGIN - 18*mm - 4])
    bg_colors = [s[1] for s in steps]
    ts = TableStyle([
        ("TOPPADDING",    (0,0), (-1,-1), 6),
        ("BOTTOMPADDING", (0,0), (-1,-1), 6),
        ("LEFTPADDING",   (0,0), (-1,-1), 6),
        ("RIGHTPADDING",  (0,0), (-1,-1), 8),
        ("VALIGN",        (0,0), (-1,-1), "MIDDLE"),
        ("GRID",          (0,0), (-1,-1), 0.3, MID_GREY),
    ])
    for i, (_, col, _, _) in enumerate(steps):
        ts.add("BACKGROUND", (0, i), (0, i), col)
    t.setStyle(ts)
    return t

# ── DO NOT box ────────────────────────────────────────────────────────────────
def do_not_box():
    warn = S("Warn", fontName="Helvetica-Bold", fontSize=8.5,
             textColor=RED_ALERT, leading=13)
    data = [
        [Paragraph("DO NOT reduce blood flow rate to manage IDH", warn)],
        [Paragraph("Reducing Qb does NOT improve haemodynamics and will compromise solute clearance (Kt/V). "
                   "Saline bolus is as effective as albumin and far more practical.", SMALL)],
    ]
    t = Table(data, colWidths=[PAGE_W - 2*MARGIN])
    t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,-1), LIGHT_RED),
        ("TOPPADDING",    (0,0), (-1,-1), 5),
        ("BOTTOMPADDING", (0,0), (-1,-1), 5),
        ("LEFTPADDING",   (0,0), (-1,-1), 10),
        ("RIGHTPADDING",  (0,0), (-1,-1), 10),
        ("LINEAFTER",     (0,0), (0,-1), 4, RED_ALERT),
    ]))
    return t

# ── PREVENTION TABLE ──────────────────────────────────────────────────────────
def prevention_table():
    cat_style = S("Cat", fontName="Helvetica-Bold", fontSize=8,
                  textColor=TEAL, leading=12)
    act_style = S("PA",  fontName="Helvetica-Bold", fontSize=8,
                  textColor=BLACK, leading=12)
    det_style = S("PD",  fontName="Helvetica", fontSize=7.5,
                  textColor=HexColor("#333333"), leading=11)
    hdr_style = S("PH",  fontName="Helvetica-Bold", fontSize=8,
                  textColor=WHITE, alignment=TA_CENTER)

    rows = [
        [Paragraph("CATEGORY", hdr_style),
         Paragraph("INTERVENTION", hdr_style),
         Paragraph("DETAIL / TARGET", hdr_style)],

        # Patient measures
        [Paragraph("Patient\nMeasures", cat_style),
         Paragraph("Accurate dry weight assessment", act_style),
         Paragraph("Reassess regularly; overestimation drives excess UF", det_style)],
        ["", Paragraph("Dietary sodium restriction", act_style),
         Paragraph("Target ~5-6 g salt/day (100 mmol Na/day); reduces IDWG", det_style)],
        ["", Paragraph("Restrict interdialytic fluid intake", act_style),
         Paragraph("Counsel patients on sodium-fluid relationship; IDWG <2.5 kg target", det_style)],
        ["", Paragraph("Hold antihypertensives before dialysis", act_style),
         Paragraph("Especially vasodilators; prefer long-acting agents taken after dialysis", det_style)],
        ["", Paragraph("No food during dialysis", act_style),
         Paragraph("Eating causes splanchnic vasodilatation; eat after session", det_style)],
        ["", Paragraph("Correct anaemia", act_style),
         Paragraph("Target Hb 10-12 g/dL; improves haemodynamic tolerance", det_style)],
        ["", Paragraph("Treat cardiac disease", act_style),
         Paragraph("Optimise HF, arrhythmias; echo if recurrent unexplained IDH", det_style)],

        # Dialysis prescription
        [Paragraph("Dialysis\nPrescription", cat_style),
         Paragraph("Cool dialysate temperature", act_style),
         Paragraph("Reduce 0.5-1 degC below body temp (stay >35 degC); increases SVR via sympathetic activation", det_style)],
        ["", Paragraph("Limit UF rate", act_style),
         Paragraph("Target <13 mL/kg/h; higher rates independently increase mortality risk", det_style)],
        ["", Paragraph("UF profiling / modelling", act_style),
         Paragraph("Remove more fluid early in session when refilling is maximal", det_style)],
        ["", Paragraph("Extend treatment time or increase frequency", act_style),
         Paragraph("More time = lower UF rate; 3x to 5-6x/week reduces IDH significantly", det_style)],
        ["", Paragraph("Bicarbonate dialysate", act_style),
         Paragraph("Avoid acetate buffer; bicarbonate improves haemodynamic stability", det_style)],
        ["", Paragraph("Sequential dialysis (isolated UF then HD)", act_style),
         Paragraph("Separate filtration from diffusion; limited evidence but reasonable option", det_style)],
        ["", Paragraph("Match dialysate sodium to serum sodium", act_style),
         Paragraph("Avoid positive Na balance; avoid routine sodium profiling (associated with mortality risk)", det_style)],

        # Pharmacological
        [Paragraph("Pharmacological\n(if above fail)", cat_style),
         Paragraph("Midodrine", act_style),
         Paragraph("2.5-10 mg PO, 15-30 min pre-dialysis. Alpha-1 agonist. Second dose mid-session if needed (partial HD removal). SE: supine HTN, urinary retention", det_style)],
    ]

    col_w = [(PAGE_W - 2*MARGIN) * r for r in [0.15, 0.30, 0.55]]
    t = Table(rows, colWidths=col_w, repeatRows=1)
    style = TableStyle([
        ("BACKGROUND",    (0,0), (2,0), TEAL),
        ("TEXTCOLOR",     (0,0), (2,0), WHITE),
        ("FONTNAME",      (0,0), (2,0), "Helvetica-Bold"),
        ("FONTSIZE",      (0,0), (-1,-1), 8),
        ("TOPPADDING",    (0,0), (-1,-1), 4),
        ("BOTTOMPADDING", (0,0), (-1,-1), 4),
        ("LEFTPADDING",   (0,0), (-1,-1), 5),
        ("RIGHTPADDING",  (0,0), (-1,-1), 5),
        ("VALIGN",        (0,0), (-1,-1), "TOP"),
        ("GRID",          (0,0), (-1,-1), 0.4, MID_GREY),
        ("ALIGN",         (0,0), (2,0), "CENTER"),
        # Patient measures rows 1-7
        ("BACKGROUND",    (0,1), (2,7), WHITE),
        ("BACKGROUND",    (0,2), (2,2), LIGHT_GREY),
        ("BACKGROUND",    (0,4), (2,4), LIGHT_GREY),
        ("BACKGROUND",    (0,6), (2,6), LIGHT_GREY),
        # Dialysis prescription rows 8-14
        ("BACKGROUND",    (0,8), (2,14), WHITE),
        ("BACKGROUND",    (0,9), (2,9), LIGHT_TEAL),
        ("BACKGROUND",    (0,11), (2,11), LIGHT_TEAL),
        ("BACKGROUND",    (0,13), (2,13), LIGHT_TEAL),
        # Pharmacological row 15
        ("BACKGROUND",    (0,15), (2,15), LIGHT_AMBER),
        # Span first column for grouped rows
        ("SPAN",          (0,1), (0,7)),
        ("SPAN",          (0,8), (0,14)),
        ("SPAN",          (0,15), (0,15)),
        ("VALIGN",        (0,1), (0,15), "MIDDLE"),
        ("ALIGN",         (0,1), (0,15), "CENTER"),
    ])
    t.setStyle(style)
    return t

# ── SPECIAL SITUATIONS BOX ────────────────────────────────────────────────────
def special_situations():
    head = S("SSH", fontName="Helvetica-Bold", fontSize=8.5,
             textColor=AMBER, leading=13)
    body_s = S("SSB", fontName="Helvetica", fontSize=8,
               textColor=BLACK, leading=12)

    rows = [
        [Paragraph("EARLY IDH (first half of session)", head),
         Paragraph("RECURRENT / UNEXPLAINED IDH", head),
         Paragraph("IDH IN DIABETIC / AUTONOMIC FAILURE", head)],
        [Paragraph("Often cardiac in origin (stunned myocardium), not just volume. "
                   "Particularly ominous - associated with worse outcomes. "
                   "Obtain ECG and troponin. Consider echo.", body_s),
         Paragraph("Rule out: pericardial effusion, LV dysfunction, access high-output failure. "
                   "Echocardiography is warranted. Review dry weight and IDWG. "
                   "Consider adding midodrine.", body_s),
         Paragraph("Autonomic neuropathy impairs vasopressor response. "
                   "Cool dialysate strongly recommended. Midodrine particularly useful. "
                   "Avoid food before and during dialysis. Frequent sessions preferred.", body_s)],
    ]

    t = Table(rows, colWidths=[(PAGE_W - 2*MARGIN)/3]*3)
    t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,0), HexColor("#1C2833")),
        ("BACKGROUND",    (0,1), (-1,-1), LIGHT_AMBER),
        ("TOPPADDING",    (0,0), (-1,-1), 5),
        ("BOTTOMPADDING", (0,0), (-1,-1), 5),
        ("LEFTPADDING",   (0,0), (-1,-1), 6),
        ("RIGHTPADDING",  (0,0), (-1,-1), 6),
        ("VALIGN",        (0,0), (-1,-1), "TOP"),
        ("GRID",          (0,0), (-1,-1), 0.5, MID_GREY),
    ]))
    return t

# ── MIDODRINE DETAIL BOX ──────────────────────────────────────────────────────
def midodrine_box():
    title_s = S("MT", fontName="Helvetica-Bold", fontSize=9,
                textColor=NAVY, leading=14)
    body_s  = S("MB", fontName="Helvetica", fontSize=8,
                textColor=BLACK, leading=12)

    data = [
        [Paragraph("MIDODRINE - Pharmacological Prevention", title_s)],
        [Paragraph(
            "<b>Dose:</b> 2.5-10 mg orally, 15-30 minutes before dialysis  "
            "|  <b>Mechanism:</b> Selective oral alpha-1 adrenergic agonist  "
            "|  <b>Peak level:</b> ~60 minutes<br/>"
            "<b>Important:</b> Midodrine is partially removed by HD - a second, smaller dose may be required mid-session.<br/>"
            "<b>Side effects:</b> Supine hypertension, urinary retention, pilomotor reactions, GI complaints<br/>"
            "<b>Indication:</b> Resistant IDH despite all non-pharmacological strategies; autonomic insufficiency",
            body_s)],
    ]
    t = Table(data, colWidths=[PAGE_W - 2*MARGIN])
    t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,-1), LIGHT_BLUE),
        ("TOPPADDING",    (0,0), (-1,-1), 5),
        ("BOTTOMPADDING", (0,0), (-1,-1), 5),
        ("LEFTPADDING",   (0,0), (-1,-1), 10),
        ("RIGHTPADDING",  (0,0), (-1,-1), 10),
        ("LINEAFTER",     (0,0), (0,-1), 4, NAVY),
        ("LINEBELOW",     (0,0), (0,0), 0.5, NAVY),
    ]))
    return t

# ── FOOTER ────────────────────────────────────────────────────────────────────
def footer_table():
    footer_s = S("Foot", fontName="Helvetica-Oblique", fontSize=6.5,
                 textColor=HexColor("#7F8C8D"), alignment=TA_CENTER, leading=10)
    data = [[Paragraph(
        "Sources: Harrison's Principles of Internal Medicine 22E (2025, McGraw-Hill); "
        "Comprehensive Clinical Nephrology 7th Ed (Elsevier); Brenner & Rector's The Kidney (Elsevier)  |  "
        "For clinical decisions, always refer to current institutional protocols and patient clinical context.",
        footer_s)]]
    t = Table(data, colWidths=[PAGE_W - 2*MARGIN])
    t.setStyle(TableStyle([
        ("TOPPADDING",    (0,0), (-1,-1), 4),
        ("BOTTOMPADDING", (0,0), (-1,-1), 2),
        ("LINEABOVE",     (0,0), (0,0), 0.5, MID_GREY),
    ]))
    return t

# ── BUILD STORY ───────────────────────────────────────────────────────────────
story = []

# Title banner
story.append(title_banner())
story.append(sp(6))

# Definition
story.append(definition_box())
story.append(sp(8))

# Risk factors
story.append(KeepTogether([
    section_header("  CAUSES & RISK FACTORS", bg=NAVY),
    sp(4),
    risk_factors_table(),
    sp(6),
]))

# Immediate management
story.append(KeepTogether([
    section_header("  IMMEDIATE MANAGEMENT - STEP BY STEP", bg=RED_ALERT),
    sp(4),
    immediate_mgmt_table(),
    sp(4),
    do_not_box(),
    sp(8),
]))

# Special situations
story.append(KeepTogether([
    section_header("  SPECIAL CLINICAL SITUATIONS", bg=HexColor("#1C2833")),
    sp(4),
    special_situations(),
    sp(8),
]))

# Prevention
story.append(section_header("  PREVENTION STRATEGIES", bg=TEAL))
story.append(sp(4))
story.append(prevention_table())
story.append(sp(6))

# Midodrine box
story.append(midodrine_box())
story.append(sp(6))

# Footer
story.append(footer_table())

# ── BUILD ─────────────────────────────────────────────────────────────────────
doc.build(story)
print(f"PDF created: {OUTPUT}")
Running Command

cd /home/daytona/workspace/idh-guide && python3 generate_idh_pdf.py 2>&1

Running Command

ls -lh /home/daytona/workspace/idh-guide/IDH_Quick_Reference_Guide.pdf

Running Command

cd /home/daytona/workspace/idh-guide && python3 -c " from pypdf import PdfReader…

Writing File

~/idh-guide/generate_idh_pdf.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_LEFT, TA_CENTER
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    HRFlowable, KeepTogether
)
from reportlab.lib.colors import HexColor

OUTPUT = "/home/daytona/workspace/idh-guide/IDH_Quick_Reference_Guide.pdf"

# ── Colour palette ────────────────────────────────────────────────────────────
NAVY        = HexColor("#1B3A5C")
TEAL        = HexColor("#1A7F8E")
RED_ALERT   = HexColor("#C0392B")
AMBER       = HexColor("#E67E22")
GREEN_DARK  = HexColor("#1E8449")
DARK_BG     = HexColor("#1C2833")
LIGHT_BLUE  = HexColor("#D6EAF8")
LIGHT_TEAL  = HexColor("#D1F2EB")
LIGHT_AMBER = HexColor("#FDEBD0")
LIGHT_RED   = HexColor("#FADBD8")
LIGHT_GREY  = HexColor("#F2F3F4")
MID_GREY    = HexColor("#BDC3C7")
WHITE       = colors.white
BLACK       = colors.black

PAGE_W, PAGE_H = A4
MARGIN = 18 * mm
CONTENT_W = PAGE_W - 2 * MARGIN

# ── Style factory ─────────────────────────────────────────────────────────────
def S(name, **kw):
    return ParagraphStyle(name, **kw)

# ── Reusable styles ───────────────────────────────────────────────────────────
TITLE1  = S("T1", fontName="Helvetica-Bold",   fontSize=20, textColor=WHITE,  alignment=TA_CENTER, leading=24)
TITLE2  = S("T2", fontName="Helvetica",        fontSize=10, textColor=HexColor("#AED6F1"), alignment=TA_CENTER, leading=14)
HDR_W   = S("HW", fontName="Helvetica-Bold",   fontSize=9,  textColor=WHITE,  alignment=TA_CENTER, leading=13)
HDR_T   = S("HT", fontName="Helvetica-Bold",   fontSize=9,  textColor=TEAL,   leading=13)
BODY    = S("Bo", fontName="Helvetica",        fontSize=8.5,textColor=BLACK,  leading=13)
BODY_B  = S("BB", fontName="Helvetica-Bold",   fontSize=8.5,textColor=BLACK,  leading=13)
BODY_S  = S("BS", fontName="Helvetica",        fontSize=8,  textColor=BLACK,  leading=12)
BODY_SB = S("SB", fontName="Helvetica-Bold",   fontSize=8,  textColor=BLACK,  leading=12)
SMALL   = S("Sm", fontName="Helvetica",        fontSize=7.5,textColor=HexColor("#555555"), leading=11)
NOTE    = S("No", fontName="Helvetica-Oblique",fontSize=7.5,textColor=HexColor("#2C3E50"),leading=11, alignment=TA_CENTER)
FOOT    = S("Ft", fontName="Helvetica-Oblique",fontSize=6.5,textColor=HexColor("#7F8C8D"), alignment=TA_CENTER, leading=10)
CAT_S   = S("CS", fontName="Helvetica-Bold",   fontSize=8,  textColor=WHITE,  alignment=TA_CENTER, leading=12)
STEP_N  = S("SN", fontName="Helvetica-Bold",   fontSize=15, textColor=WHITE,  alignment=TA_CENTER, leading=20)
STEP_A  = S("SA", fontName="Helvetica-Bold",   fontSize=9,  textColor=NAVY,   leading=13)
STEP_D  = S("SD", fontName="Helvetica",        fontSize=8,  textColor=HexColor("#333333"), leading=12)
WARN    = S("Wn", fontName="Helvetica-Bold",   fontSize=9,  textColor=RED_ALERT, leading=13)
AMBER_S = S("AS", fontName="Helvetica-Bold",   fontSize=8.5,textColor=AMBER,  leading=13)

def sp(h=5):
    return Spacer(1, h)

# ── Section header ────────────────────────────────────────────────────────────
def sec_hdr(text, bg=NAVY):
    p = Paragraph(f"<b>{text}</b>", S("SH", fontName="Helvetica-Bold", fontSize=10,
                                        textColor=WHITE, leading=14, leftIndent=6))
    t = Table([[p]], colWidths=[CONTENT_W])
    t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,-1), bg),
        ("TOPPADDING",    (0,0), (-1,-1), 6),
        ("BOTTOMPADDING", (0,0), (-1,-1), 6),
        ("LEFTPADDING",   (0,0), (-1,-1), 8),
        ("RIGHTPADDING",  (0,0), (-1,-1), 8),
    ]))
    return t

# ── Title banner ──────────────────────────────────────────────────────────────
def title_banner():
    rows = [
        [Paragraph("INTRADIALYTIC HYPOTENSION (IDH)", TITLE1)],
        [Paragraph("Quick Reference Guide  |  Hemodialysis Unit Protocol", TITLE2)],
    ]
    t = Table(rows, colWidths=[CONTENT_W])
    t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,-1), NAVY),
        ("TOPPADDING",    (0,0), (0,0), 12),
        ("BOTTOMPADDING", (0,1), (0,1), 12),
        ("TOPPADDING",    (0,1), (0,1), 2),
        ("BOTTOMPADDING", (0,0), (0,0), 2),
        ("LEFTPADDING",   (0,0), (-1,-1), 8),
        ("RIGHTPADDING",  (0,0), (-1,-1), 8),
        ("ALIGN",         (0,0), (-1,-1), "CENTER"),
    ]))
    return t

# ── Definition row ────────────────────────────────────────────────────────────
def definition_box():
    lw, rw = CONTENT_W * 0.5, CONTENT_W * 0.5
    def_left = [
        Paragraph("DEFINITION", S("DL", fontName="Helvetica-Bold", fontSize=8.5,
                                   textColor=TEAL, leading=12)),
        Paragraph("Systolic BP fall <b>&ge;20 mmHg</b> during dialysis,<br/>"
                  "<i>OR</i> SBP drops to <b>&lt;90 mmHg</b> with symptoms", BODY),
    ]
    def_right = [
        Paragraph("INCIDENCE", S("DL2", fontName="Helvetica-Bold", fontSize=8.5,
                                   textColor=TEAL, leading=12)),
        Paragraph("<b>15 - 50%</b> of HD sessions<br/>"
                  "Independent risk factor for <b>mortality</b>", BODY),
    ]
    def_left_tbl  = Table([[item] for item in def_left],  colWidths=[lw - 8])
    def_right_tbl = Table([[item] for item in def_right], colWidths=[rw - 8])
    for tbl in [def_left_tbl, def_right_tbl]:
        tbl.setStyle(TableStyle([
            ("TOPPADDING",    (0,0), (-1,-1), 2),
            ("BOTTOMPADDING", (0,0), (-1,-1), 2),
            ("LEFTPADDING",   (0,0), (-1,-1), 0),
            ("RIGHTPADDING",  (0,0), (-1,-1), 0),
        ]))
    outer = Table([[def_left_tbl, def_right_tbl]], colWidths=[lw, rw])
    outer.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,-1), LIGHT_BLUE),
        ("TOPPADDING",    (0,0), (-1,-1), 7),
        ("BOTTOMPADDING", (0,0), (-1,-1), 7),
        ("LEFTPADDING",   (0,0), (-1,-1), 10),
        ("RIGHTPADDING",  (0,0), (-1,-1), 10),
        ("LINEAFTER",     (0,0), (0,-1), 0.5, MID_GREY),
        ("VALIGN",        (0,0), (-1,-1), "TOP"),
    ]))
    return outer

# ── Risk factors table ────────────────────────────────────────────────────────
def risk_table():
    cw = CONTENT_W / 2
    data = [
        [Paragraph("RISK FACTOR / CAUSE", HDR_W), Paragraph("MECHANISM", HDR_W)],
        [Paragraph("Excessive UF rate  (<b>&gt;13 mL/kg/h</b>)", BODY_S),
         Paragraph("UF rate exceeds intravascular refilling capacity", BODY_S)],
        [Paragraph("High interdialytic weight gain (IDWG)", BODY_S),
         Paragraph("Requires rapid, large-volume fluid removal", BODY_S)],
        [Paragraph("Diabetic / uremic autonomic dysfunction", BODY_S),
         Paragraph("Impaired vasopressor and sympathetic response", BODY_S)],
        [Paragraph("Antihypertensives taken pre-dialysis", BODY_S),
         Paragraph("Reduced arteriolar tone during session", BODY_S)],
        [Paragraph("Food / eating during dialysis", BODY_S),
         Paragraph("Splanchnic vasodilatation shunts blood to gut", BODY_S)],
        [Paragraph("Reduced cardiac reserve (HF, ischaemia)", BODY_S),
         Paragraph("Inability to augment cardiac output", BODY_S)],
        [Paragraph("Pericardial effusion / pericarditis", BODY_S),
         Paragraph("Impaired cardiac filling; tamponade physiology", BODY_S)],
        [Paragraph("Anaemia", BODY_S),
         Paragraph("Reduced O2 delivery; poor haemodynamic compensation", BODY_S)],
        [Paragraph("Rapid osmolar shifts", BODY_S),
         Paragraph("Reduced plasma osmolality; volume redistribution", BODY_S)],
    ]
    t = Table(data, colWidths=[cw, cw])
    t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,0), NAVY),
        ("FONTNAME",      (0,0), (-1,0), "Helvetica-Bold"),
        ("FONTSIZE",      (0,0), (-1,-1), 8),
        ("TOPPADDING",    (0,0), (-1,-1), 4),
        ("BOTTOMPADDING", (0,0), (-1,-1), 4),
        ("LEFTPADDING",   (0,0), (-1,-1), 6),
        ("RIGHTPADDING",  (0,0), (-1,-1), 6),
        ("ROWBACKGROUNDS",(0,1), (-1,-1), [WHITE, LIGHT_GREY]),
        ("GRID",          (0,0), (-1,-1), 0.4, MID_GREY),
        ("VALIGN",        (0,0), (-1,-1), "MIDDLE"),
    ]))
    return t

# ── Immediate management steps ────────────────────────────────────────────────
def immediate_mgmt():
    steps = [
        ("1", RED_ALERT,            "TRENDELENBURG POSITION",
         "Lay patient flat, elevate legs - improves venous return immediately"),
        ("2", HexColor("#A93226"),  "STOP / REDUCE ULTRAFILTRATION",
         "Primary intervention. Reduce UF to zero. Restart only once BP stabilises"),
        ("3", AMBER,                "IV 0.9% ISOTONIC SALINE BOLUS",
         "Give 100-250 mL IV bolus. Repeat as needed. Albumin offers no proven advantage over saline"),
        ("4", HexColor("#1A5276"),  "REASSESS & MONITOR",
         "Re-check BP every 5 min; note symptoms (chest pain, dyspnoea, arrhythmia)"),
        ("5", GREEN_DARK,           "IF CHEST PAIN / DYSPNOEA  ->  ECG + TROPONIN",
         "Rule out ACS. If recurrent unexplained IDH: echocardiogram (pericardial effusion, LV dysfunction)"),
    ]
    nw = 16 * mm
    aw = CONTENT_W - nw
    rows = []
    for num, col, action, detail in steps:
        rows.append([
            Paragraph(num, STEP_N),
            [Paragraph(action, STEP_A), Paragraph(detail, STEP_D)],
        ])
    t = Table(rows, colWidths=[nw, aw])
    ts = TableStyle([
        ("TOPPADDING",    (0,0), (-1,-1), 6),
        ("BOTTOMPADDING", (0,0), (-1,-1), 6),
        ("LEFTPADDING",   (0,0), (-1,-1), 5),
        ("RIGHTPADDING",  (1,0), (1,-1), 8),
        ("VALIGN",        (0,0), (-1,-1), "MIDDLE"),
        ("GRID",          (0,0), (-1,-1), 0.3, MID_GREY),
    ])
    for i, (_, col, _, _) in enumerate(steps):
        ts.add("BACKGROUND", (0, i), (0, i), col)
        ts.add("BACKGROUND", (1, i), (1, i), LIGHT_GREY if i % 2 == 0 else WHITE)
    t.setStyle(ts)
    return t

# ── Do NOT box ────────────────────────────────────────────────────────────────
def do_not_box():
    data = [
        [Paragraph("&#9888;  DO NOT reduce blood flow rate (Qb) to manage IDH", WARN)],
        [Paragraph("Reducing Qb does <b>not</b> improve haemodynamics and will compromise solute clearance (Kt/V). "
                   "Saline bolus is as effective as albumin and far more practical in outpatient settings.", SMALL)],
    ]
    t = Table(data, colWidths=[CONTENT_W])
    t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,-1), LIGHT_RED),
        ("TOPPADDING",    (0,0), (-1,-1), 5),
        ("BOTTOMPADDING", (0,0), (-1,-1), 5),
        ("LEFTPADDING",   (0,0), (-1,-1), 10),
        ("RIGHTPADDING",  (0,0), (-1,-1), 10),
        ("LINEAFTER",     (0,0), (0,-1), 5, RED_ALERT),
    ]))
    return t

# ── Special situations ────────────────────────────────────────────────────────
def special_situations():
    cw = CONTENT_W / 3
    data = [
        [Paragraph("EARLY IDH", HDR_W),
         Paragraph("RECURRENT / UNEXPLAINED IDH", HDR_W),
         Paragraph("IDH IN DIABETES / AUTONOMIC FAILURE", HDR_W)],
        [Paragraph("Often cardiac origin (myocardial stunning), not just volume depletion. "
                   "Associated with worst outcomes. ECG + troponin. Consider echo.", BODY_S),
         Paragraph("Rule out pericardial effusion, LV dysfunction, high-output access failure. "
                   "Echocardiogram warranted. Review dry weight and IDWG. Add midodrine.", BODY_S),
         Paragraph("Autonomic neuropathy impairs vasopressor response. Cool dialysate strongly recommended. "
                   "Midodrine particularly useful. No food during dialysis. Frequent sessions preferred.", BODY_S)],
    ]
    t = Table(data, colWidths=[cw, cw, cw])
    t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,0), DARK_BG),
        ("BACKGROUND",    (0,1), (-1,-1), LIGHT_AMBER),
        ("FONTSIZE",      (0,0), (-1,-1), 8),
        ("TOPPADDING",    (0,0), (-1,-1), 5),
        ("BOTTOMPADDING", (0,0), (-1,-1), 5),
        ("LEFTPADDING",   (0,0), (-1,-1), 6),
        ("RIGHTPADDING",  (0,0), (-1,-1), 6),
        ("VALIGN",        (0,0), (-1,-1), "TOP"),
        ("GRID",          (0,0), (-1,-1), 0.4, MID_GREY),
    ]))
    return t

# ── Prevention table ──────────────────────────────────────────────────────────
def prevention_table():
    cw = [CONTENT_W * r for r in [0.13, 0.30, 0.57]]

    def row(cat, action, detail, cat_bg=None):
        return [
            Paragraph(cat, CAT_S) if cat else Paragraph("", CAT_S),
            Paragraph(action, BODY_SB),
            Paragraph(detail, BODY_S),
        ]

    data = [
        # Header
        [Paragraph("CATEGORY", HDR_W), Paragraph("INTERVENTION", HDR_W), Paragraph("DETAILS / TARGET", HDR_W)],
        # Patient measures
        [Paragraph("PATIENT\nMEASURES", CAT_S),
         Paragraph("Accurate dry weight", BODY_SB),
         Paragraph("Reassess regularly; overestimation drives excess UF", BODY_S)],
        ["", Paragraph("Dietary sodium restriction", BODY_SB),
         Paragraph("Target ~5-6 g salt/day (100 mmol Na/day) to minimise IDWG", BODY_S)],
        ["", Paragraph("Restrict fluid intake", BODY_SB),
         Paragraph("Patient education; target IDWG &lt;2.5 kg between sessions", BODY_S)],
        ["", Paragraph("Hold antihypertensives pre-dialysis", BODY_SB),
         Paragraph("Especially vasodilators; give after dialysis on treatment days", BODY_S)],
        ["", Paragraph("No food during dialysis", BODY_SB),
         Paragraph("Eating causes splanchnic vasodilatation; schedule meals after session", BODY_S)],
        ["", Paragraph("Correct anaemia", BODY_SB),
         Paragraph("Target Hb 10-12 g/dL; improves haemodynamic tolerance", BODY_S)],
        ["", Paragraph("Treat cardiac disease", BODY_SB),
         Paragraph("Optimise HF and arrhythmias; echo if recurrent unexplained IDH", BODY_S)],
        # Dialysis prescription
        [Paragraph("DIALYSIS\nPRESCRIPTION", CAT_S),
         Paragraph("Cool dialysate temperature", BODY_SB),
         Paragraph("Reduce 0.5-1 degC below body temp (stay &gt;35 degC); activates sympathetic nervous system, increases SVR and cardiac contractility", BODY_S)],
        ["", Paragraph("Limit UF rate to &lt;13 mL/kg/h", BODY_SB),
         Paragraph("Higher UF rates independently associated with increased mortality", BODY_S)],
        ["", Paragraph("UF profiling / modelling", BODY_SB),
         Paragraph("Remove more fluid early in session when plasma refilling is maximal", BODY_S)],
        ["", Paragraph("Extend time or increase dialysis frequency", BODY_SB),
         Paragraph("More time = lower required UF rate; 5-6x/week dialysis markedly reduces IDH", BODY_S)],
        ["", Paragraph("Bicarbonate dialysate", BODY_SB),
         Paragraph("Avoid acetate buffer; bicarbonate improves haemodynamic stability", BODY_S)],
        ["", Paragraph("Sequential dialysis (isolated UF then HD)", BODY_SB),
         Paragraph("Separate fluid removal from diffusion; limited but reasonable evidence", BODY_S)],
        ["", Paragraph("Match dialysate Na to serum Na", BODY_SB),
         Paragraph("Avoids positive Na balance; do NOT use routine sodium profiling (linked to increased mortality)", BODY_S)],
        # Pharmacological
        [Paragraph("PHARMA-\nCOLOGICAL", CAT_S),
         Paragraph("Midodrine 2.5-10 mg PO pre-dialysis", BODY_SB),
         Paragraph("Oral alpha-1 agonist; give 15-30 min before session. Peak at 60 min. "
                   "Second smaller dose mid-session may be needed (partial HD removal). "
                   "SE: supine HTN, urinary retention, pilomotor reactions. "
                   "Use when non-pharmacological strategies are insufficient.", BODY_S)],
    ]

    t = Table(data, colWidths=cw, repeatRows=1)
    ts = TableStyle([
        # Header
        ("BACKGROUND",    (0,0), (-1,0), TEAL),
        ("ALIGN",         (0,0), (-1,0), "CENTER"),
        ("FONTSIZE",      (0,0), (-1,-1), 8),
        ("TOPPADDING",    (0,0), (-1,-1), 4),
        ("BOTTOMPADDING", (0,0), (-1,-1), 4),
        ("LEFTPADDING",   (0,0), (-1,-1), 5),
        ("RIGHTPADDING",  (0,0), (-1,-1), 5),
        ("VALIGN",        (0,0), (-1,-1), "TOP"),
        ("GRID",          (0,0), (-1,-1), 0.4, MID_GREY),
        # Patient measures section
        ("BACKGROUND",    (0,1), (0,7), HexColor("#1E8449")),
        ("BACKGROUND",    (1,1), (-1,7), WHITE),
        ("ROWBACKGROUNDS",(1,2), (-1,7), [LIGHT_GREY, WHITE]),
        # Dialysis section
        ("BACKGROUND",    (0,8), (0,14), TEAL),
        ("BACKGROUND",    (1,8), (-1,14), WHITE),
        ("ROWBACKGROUNDS",(1,8), (-1,14), [LIGHT_TEAL, WHITE]),
        # Pharmacological section
        ("BACKGROUND",    (0,15), (0,15), HexColor("#7D3C98")),
        ("BACKGROUND",    (1,15), (-1,15), LIGHT_AMBER),
        # Span category cells
        ("SPAN",          (0,1), (0,7)),
        ("SPAN",          (0,8), (0,14)),
        ("SPAN",          (0,15), (0,15)),
        ("VALIGN",        (0,1), (0,15), "MIDDLE"),
        ("ALIGN",         (0,1), (0,15), "CENTER"),
        ("TEXTCOLOR",     (0,1), (0,15), WHITE),
    ])
    t.setStyle(ts)
    return t

# ── Midodrine highlight box ───────────────────────────────────────────────────
def midodrine_box():
    title_s = S("MB_T", fontName="Helvetica-Bold", fontSize=9, textColor=NAVY, leading=14)
    detail_s = S("MB_D", fontName="Helvetica", fontSize=8, textColor=BLACK, leading=12)
    data = [
        [Paragraph("MIDODRINE - Key Prescribing Points", title_s)],
        [Paragraph(
            "<b>Dose:</b> 2.5-10 mg orally, 15-30 min before dialysis  "
            "&nbsp;|&nbsp;  <b>Class:</b> Selective oral alpha-1 adrenergic agonist  "
            "&nbsp;|&nbsp;  <b>Peak:</b> ~60 min<br/>"
            "<b>Note:</b> Partially removed by HD - second smaller dose may be needed mid-session.<br/>"
            "<b>Side effects:</b> Supine hypertension, urinary retention, pilomotor reactions, GI complaints<br/>"
            "<b>When to use:</b> Resistant IDH despite non-pharmacological measures; autonomic insufficiency",
            detail_s)],
    ]
    t = Table(data, colWidths=[CONTENT_W])
    t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,-1), LIGHT_BLUE),
        ("TOPPADDING",    (0,0), (-1,-1), 5),
        ("BOTTOMPADDING", (0,0), (-1,-1), 5),
        ("LEFTPADDING",   (0,0), (-1,-1), 10),
        ("RIGHTPADDING",  (0,0), (-1,-1), 10),
        ("LINEAFTER",     (0,0), (0,-1), 5, NAVY),
        ("LINEBELOW",     (0,0), (0,0), 0.5, NAVY),
    ]))
    return t

# ── Footer ────────────────────────────────────────────────────────────────────
def footer():
    data = [[Paragraph(
        "Sources: Harrison's Principles of Internal Medicine 22E (2025); "
        "Comprehensive Clinical Nephrology 7th Ed; Brenner &amp; Rector's The Kidney  |  "
        "For clinical decisions, always apply current institutional protocols and individual patient assessment.",
        FOOT)]]
    t = Table(data, colWidths=[CONTENT_W])
    t.setStyle(TableStyle([
        ("TOPPADDING",    (0,0), (-1,-1), 4),
        ("BOTTOMPADDING", (0,0), (-1,-1), 2),
        ("LEFTPADDING",   (0,0), (-1,-1), 0),
        ("RIGHTPADDING",  (0,0), (-1,-1), 0),
        ("LINEABOVE",     (0,0), (0,0), 0.5, MID_GREY),
    ]))
    return t

# ── Assemble story ────────────────────────────────────────────────────────────
doc = SimpleDocTemplate(
    OUTPUT, pagesize=A4,
    leftMargin=MARGIN, rightMargin=MARGIN,
    topMargin=14*mm, bottomMargin=14*mm,
    title="IDH Quick Reference Guide",
    author="Dialysis Unit Protocol",
)

story = []

story.append(title_banner())
story.append(sp(6))
story.append(definition_box())
story.append(sp(8))

story.append(KeepTogether([
    sec_hdr("  CAUSES & RISK FACTORS", bg=NAVY),
    sp(4),
    risk_table(),
    sp(8),
]))

story.append(KeepTogether([
    sec_hdr("  IMMEDIATE MANAGEMENT - STEP BY STEP", bg=RED_ALERT),
    sp(4),
    immediate_mgmt(),
    sp(4),
    do_not_box(),
    sp(8),
]))

story.append(KeepTogether([
    sec_hdr("  SPECIAL CLINICAL SITUATIONS", bg=DARK_BG),
    sp(4),
    special_situations(),
    sp(8),
]))

story.append(sec_hdr("  PREVENTION STRATEGIES", bg=TEAL))
story.append(sp(4))
story.append(prevention_table())
story.append(sp(6))

story.append(midodrine_box())
story.append(sp(6))
story.append(footer())

doc.build(story)
print(f"PDF saved: {OUTPUT}")
Editing File

~/idh-guide/generate_idh_pdf.py

Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/idh-guide/generate_idh_pdf.py'
Running Command

rm /home/daytona/workspace/idh-guide/generate_idh_pdf.py && echo "deleted"

Writing File

~/idh-guide/generate_idh_pdf.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_LEFT, TA_CENTER
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    HRFlowable, KeepTogether
)
from reportlab.lib.colors import HexColor

OUTPUT = "/home/daytona/workspace/idh-guide/IDH_Quick_Reference_Guide.pdf"

NAVY        = HexColor("#1B3A5C")
TEAL        = HexColor("#1A7F8E")
RED_ALERT   = HexColor("#C0392B")
AMBER       = HexColor("#E67E22")
GREEN_DARK  = HexColor("#1E8449")
DARK_BG     = HexColor("#1C2833")
LIGHT_BLUE  = HexColor("#D6EAF8")
LIGHT_TEAL  = HexColor("#D1F2EB")
LIGHT_AMBER = HexColor("#FDEBD0")
LIGHT_RED   = HexColor("#FADBD8")
LIGHT_GREY  = HexColor("#F2F3F4")
MID_GREY    = HexColor("#BDC3C7")
WHITE       = colors.white
BLACK       = colors.black

PAGE_W, PAGE_H = A4
MARGIN    = 18 * mm
CONTENT_W = PAGE_W - 2 * MARGIN

def S(name, **kw):
    return ParagraphStyle(name, **kw)

TITLE1  = S("T1",  fontName="Helvetica-Bold",   fontSize=20, textColor=WHITE,               alignment=TA_CENTER, leading=24)
TITLE2  = S("T2",  fontName="Helvetica",        fontSize=10, textColor=HexColor("#AED6F1"), alignment=TA_CENTER, leading=14)
HDR_W   = S("HW",  fontName="Helvetica-Bold",   fontSize=9,  textColor=WHITE,               alignment=TA_CENTER, leading=13)
BODY_S  = S("BS",  fontName="Helvetica",        fontSize=8,  textColor=BLACK,  leading=12)
BODY_SB = S("SB",  fontName="Helvetica-Bold",   fontSize=8,  textColor=BLACK,  leading=12)
BODY    = S("Bo",  fontName="Helvetica",        fontSize=8.5,textColor=BLACK,  leading=13)
SMALL   = S("Sm",  fontName="Helvetica",        fontSize=7.5,textColor=HexColor("#555555"), leading=11)
FOOT    = S("Ft",  fontName="Helvetica-Oblique",fontSize=6.5,textColor=HexColor("#7F8C8D"), alignment=TA_CENTER, leading=10)
CAT_S   = S("CS",  fontName="Helvetica-Bold",   fontSize=8,  textColor=WHITE,  alignment=TA_CENTER, leading=12)
STEP_N  = S("SN",  fontName="Helvetica-Bold",   fontSize=15, textColor=WHITE,  alignment=TA_CENTER, leading=20)
STEP_A  = S("SA",  fontName="Helvetica-Bold",   fontSize=9,  textColor=NAVY,   leading=13)
STEP_D  = S("SD",  fontName="Helvetica",        fontSize=8,  textColor=HexColor("#333333"), leading=12)
WARN    = S("Wn",  fontName="Helvetica-Bold",   fontSize=9,  textColor=RED_ALERT, leading=13)

def sp(h=5):
    return Spacer(1, h)

def sec_hdr(text, bg=NAVY):
    p = Paragraph(f"<b>{text}</b>",
                  S("SH", fontName="Helvetica-Bold", fontSize=10,
                    textColor=WHITE, leading=14, leftIndent=6))
    t = Table([[p]], colWidths=[CONTENT_W])
    t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,-1), bg),
        ("TOPPADDING",    (0,0), (-1,-1), 6),
        ("BOTTOMPADDING", (0,0), (-1,-1), 6),
        ("LEFTPADDING",   (0,0), (-1,-1), 8),
        ("RIGHTPADDING",  (0,0), (-1,-1), 8),
    ]))
    return t

def cell_stack(*paras):
    """Return a Table wrapping multiple Paragraphs stacked vertically in one cell."""
    t = Table([[p] for p in paras], colWidths=[CONTENT_W])  # width set later by parent
    t.setStyle(TableStyle([
        ("TOPPADDING",    (0,0), (-1,-1), 1),
        ("BOTTOMPADDING", (0,0), (-1,-1), 1),
        ("LEFTPADDING",   (0,0), (-1,-1), 0),
        ("RIGHTPADDING",  (0,0), (-1,-1), 0),
    ]))
    return t

# ── Title banner ──────────────────────────────────────────────────────────────
def title_banner():
    rows = [
        [Paragraph("INTRADIALYTIC HYPOTENSION (IDH)", TITLE1)],
        [Paragraph("Quick Reference Guide  |  Hemodialysis Unit Protocol", TITLE2)],
    ]
    t = Table(rows, colWidths=[CONTENT_W])
    t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,-1), NAVY),
        ("TOPPADDING",    (0,0), (0,0), 12),
        ("BOTTOMPADDING", (0,1), (0,1), 12),
        ("TOPPADDING",    (0,1), (0,1), 2),
        ("BOTTOMPADDING", (0,0), (0,0), 2),
        ("LEFTPADDING",   (0,0), (-1,-1), 8),
        ("RIGHTPADDING",  (0,0), (-1,-1), 8),
        ("ALIGN",         (0,0), (-1,-1), "CENTER"),
    ]))
    return t

# ── Definition box ────────────────────────────────────────────────────────────
def definition_box():
    lw = rw = CONTENT_W / 2
    def_left = Table([
        [Paragraph("DEFINITION", S("DL", fontName="Helvetica-Bold", fontSize=8.5,
                                    textColor=TEAL, leading=12))],
        [Paragraph("Systolic BP fall <b>&ge;20 mmHg</b> during dialysis,<br/>"
                   "<i>OR</i> SBP drops to <b>&lt;90 mmHg</b> with symptoms", BODY)],
    ], colWidths=[lw - 16])
    def_left.setStyle(TableStyle([("TOPPADDING",(0,0),(-1,-1),2),
                                   ("BOTTOMPADDING",(0,0),(-1,-1),2),
                                   ("LEFTPADDING",(0,0),(-1,-1),0),
                                   ("RIGHTPADDING",(0,0),(-1,-1),0)]))
    def_right = Table([
        [Paragraph("INCIDENCE", S("DL2", fontName="Helvetica-Bold", fontSize=8.5,
                                   textColor=TEAL, leading=12))],
        [Paragraph("<b>15 - 50%</b> of HD sessions<br/>"
                   "Independent risk factor for <b>mortality</b>", BODY)],
    ], colWidths=[rw - 16])
    def_right.setStyle(TableStyle([("TOPPADDING",(0,0),(-1,-1),2),
                                    ("BOTTOMPADDING",(0,0),(-1,-1),2),
                                    ("LEFTPADDING",(0,0),(-1,-1),0),
                                    ("RIGHTPADDING",(0,0),(-1,-1),0)]))
    outer = Table([[def_left, def_right]], colWidths=[lw, rw])
    outer.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,-1), LIGHT_BLUE),
        ("TOPPADDING",    (0,0), (-1,-1), 7),
        ("BOTTOMPADDING", (0,0), (-1,-1), 7),
        ("LEFTPADDING",   (0,0), (-1,-1), 10),
        ("RIGHTPADDING",  (0,0), (-1,-1), 10),
        ("LINEAFTER",     (0,0), (0,-1), 0.5, MID_GREY),
        ("VALIGN",        (0,0), (-1,-1), "TOP"),
    ]))
    return outer

# ── Risk factors ──────────────────────────────────────────────────────────────
def risk_table():
    cw = CONTENT_W / 2
    data = [
        [Paragraph("RISK FACTOR / CAUSE", HDR_W), Paragraph("MECHANISM", HDR_W)],
        [Paragraph("Excessive UF rate (<b>&gt;13 mL/kg/h</b>)", BODY_S),
         Paragraph("UF rate exceeds intravascular refilling capacity", BODY_S)],
        [Paragraph("High interdialytic weight gain (IDWG)", BODY_S),
         Paragraph("Requires rapid, large-volume fluid removal", BODY_S)],
        [Paragraph("Diabetic / uremic autonomic dysfunction", BODY_S),
         Paragraph("Impaired vasopressor and sympathetic response", BODY_S)],
        [Paragraph("Antihypertensives taken pre-dialysis", BODY_S),
         Paragraph("Reduced arteriolar tone during session", BODY_S)],
        [Paragraph("Food / eating during dialysis", BODY_S),
         Paragraph("Splanchnic vasodilatation shunts blood to gut", BODY_S)],
        [Paragraph("Reduced cardiac reserve (HF, ischaemia)", BODY_S),
         Paragraph("Inability to augment cardiac output", BODY_S)],
        [Paragraph("Pericardial effusion / pericarditis", BODY_S),
         Paragraph("Impaired cardiac filling; tamponade physiology", BODY_S)],
        [Paragraph("Anaemia", BODY_S),
         Paragraph("Reduced O2 delivery; poor haemodynamic compensation", BODY_S)],
        [Paragraph("Rapid osmolar shifts", BODY_S),
         Paragraph("Reduced plasma osmolality; volume redistribution", BODY_S)],
    ]
    t = Table(data, colWidths=[cw, cw])
    t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,0), NAVY),
        ("FONTSIZE",      (0,0), (-1,-1), 8),
        ("TOPPADDING",    (0,0), (-1,-1), 4),
        ("BOTTOMPADDING", (0,0), (-1,-1), 4),
        ("LEFTPADDING",   (0,0), (-1,-1), 6),
        ("RIGHTPADDING",  (0,0), (-1,-1), 6),
        ("ROWBACKGROUNDS",(0,1), (-1,-1), [WHITE, LIGHT_GREY]),
        ("GRID",          (0,0), (-1,-1), 0.4, MID_GREY),
        ("VALIGN",        (0,0), (-1,-1), "MIDDLE"),
    ]))
    return t

# ── Immediate management ──────────────────────────────────────────────────────
def immediate_mgmt():
    steps = [
        ("1", RED_ALERT,           "TRENDELENBURG POSITION",
         "Lay patient flat, elevate legs - improves venous return immediately"),
        ("2", HexColor("#A93226"), "STOP / REDUCE ULTRAFILTRATION",
         "Primary intervention. Reduce UF to zero. Restart only once BP stabilises"),
        ("3", AMBER,               "IV 0.9% ISOTONIC SALINE BOLUS",
         "100-250 mL IV bolus. Repeat as needed. Albumin offers no proven advantage over saline"),
        ("4", HexColor("#1A5276"), "REASSESS & MONITOR",
         "Re-check BP every 5 min; note chest pain, dyspnoea, arrhythmia"),
        ("5", GREEN_DARK,          "IF CHEST PAIN / DYSPNOEA  ->  ECG + TROPONIN",
         "Rule out ACS. Recurrent unexplained IDH: arrange echocardiogram (pericardial effusion, LV dysfunction)"),
    ]
    nw = 16 * mm
    aw = CONTENT_W - nw
    rows = []
    for num, col, action, detail in steps:
        action_cell = Table(
            [[Paragraph(action, STEP_A)],
             [Paragraph(detail, STEP_D)]],
            colWidths=[aw - 10]
        )
        action_cell.setStyle(TableStyle([
            ("TOPPADDING",    (0,0), (-1,-1), 1),
            ("BOTTOMPADDING", (0,0), (-1,-1), 1),
            ("LEFTPADDING",   (0,0), (-1,-1), 0),
            ("RIGHTPADDING",  (0,0), (-1,-1), 0),
        ]))
        rows.append([Paragraph(num, STEP_N), action_cell])

    t = Table(rows, colWidths=[nw, aw])
    ts = TableStyle([
        ("TOPPADDING",    (0,0), (-1,-1), 6),
        ("BOTTOMPADDING", (0,0), (-1,-1), 6),
        ("LEFTPADDING",   (0,0), (-1,-1), 5),
        ("RIGHTPADDING",  (1,0), (1,-1), 8),
        ("VALIGN",        (0,0), (-1,-1), "MIDDLE"),
        ("GRID",          (0,0), (-1,-1), 0.3, MID_GREY),
    ])
    row_bgs = [LIGHT_GREY, WHITE, LIGHT_GREY, WHITE, LIGHT_GREY]
    for i, ((_, col, _, _), row_bg) in enumerate(zip(steps, row_bgs)):
        ts.add("BACKGROUND", (0, i), (0, i), col)
        ts.add("BACKGROUND", (1, i), (1, i), row_bg)
    t.setStyle(ts)
    return t

def do_not_box():
    data = [
        [Paragraph("DO NOT reduce blood flow rate (Qb) to manage IDH", WARN)],
        [Paragraph("Reducing Qb does <b>not</b> improve haemodynamics and will compromise solute clearance (Kt/V). "
                   "Saline bolus is as effective as albumin and far more practical in outpatient settings.", SMALL)],
    ]
    t = Table(data, colWidths=[CONTENT_W])
    t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,-1), LIGHT_RED),
        ("TOPPADDING",    (0,0), (-1,-1), 5),
        ("BOTTOMPADDING", (0,0), (-1,-1), 5),
        ("LEFTPADDING",   (0,0), (-1,-1), 10),
        ("RIGHTPADDING",  (0,0), (-1,-1), 10),
        ("LINEAFTER",     (0,0), (0,-1), 5, RED_ALERT),
    ]))
    return t

# ── Special situations ────────────────────────────────────────────────────────
def special_situations():
    cw = CONTENT_W / 3
    data = [
        [Paragraph("EARLY IDH", HDR_W),
         Paragraph("RECURRENT / UNEXPLAINED IDH", HDR_W),
         Paragraph("IDH IN DIABETES / AUTONOMIC FAILURE", HDR_W)],
        [Paragraph("Often cardiac origin (myocardial stunning), not just volume depletion. "
                   "Associated with worst outcomes. ECG + troponin. Consider echo.", BODY_S),
         Paragraph("Rule out pericardial effusion, LV dysfunction, high-output access failure. "
                   "Echocardiogram warranted. Review dry weight and IDWG. Add midodrine.", BODY_S),
         Paragraph("Autonomic neuropathy impairs vasopressor response. Cool dialysate strongly recommended. "
                   "Midodrine particularly useful. No food during dialysis. Frequent sessions preferred.", BODY_S)],
    ]
    t = Table(data, colWidths=[cw, cw, cw])
    t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,0), DARK_BG),
        ("BACKGROUND",    (0,1), (-1,-1), LIGHT_AMBER),
        ("FONTSIZE",      (0,0), (-1,-1), 8),
        ("TOPPADDING",    (0,0), (-1,-1), 5),
        ("BOTTOMPADDING", (0,0), (-1,-1), 5),
        ("LEFTPADDING",   (0,0), (-1,-1), 6),
        ("RIGHTPADDING",  (0,0), (-1,-1), 6),
        ("VALIGN",        (0,0), (-1,-1), "TOP"),
        ("GRID",          (0,0), (-1,-1), 0.4, MID_GREY),
    ]))
    return t

# ── Prevention table ──────────────────────────────────────────────────────────
def prevention_table():
    cw = [CONTENT_W * r for r in [0.13, 0.30, 0.57]]

    data = [
        [Paragraph("CATEGORY", HDR_W), Paragraph("INTERVENTION", HDR_W), Paragraph("DETAILS / TARGET", HDR_W)],
        # Patient measures
        [Paragraph("PATIENT\nMEASURES", CAT_S),
         Paragraph("Accurate dry weight", BODY_SB),
         Paragraph("Reassess regularly; overestimation drives excess UF", BODY_S)],
        ["",
         Paragraph("Dietary sodium restriction", BODY_SB),
         Paragraph("Target ~5-6 g salt/day (100 mmol Na/day) to minimise IDWG", BODY_S)],
        ["",
         Paragraph("Restrict fluid intake", BODY_SB),
         Paragraph("Patient education; target IDWG &lt;2.5 kg between sessions", BODY_S)],
        ["",
         Paragraph("Hold antihypertensives pre-dialysis", BODY_SB),
         Paragraph("Especially vasodilators; give after dialysis on treatment days", BODY_S)],
        ["",
         Paragraph("No food during dialysis", BODY_SB),
         Paragraph("Eating causes splanchnic vasodilatation; schedule meals after session", BODY_S)],
        ["",
         Paragraph("Correct anaemia", BODY_SB),
         Paragraph("Target Hb 10-12 g/dL; improves haemodynamic tolerance", BODY_S)],
        ["",
         Paragraph("Treat cardiac disease", BODY_SB),
         Paragraph("Optimise HF and arrhythmias; echo if recurrent unexplained IDH", BODY_S)],
        # Dialysis prescription
        [Paragraph("DIALYSIS\nPRESCRIPTION", CAT_S),
         Paragraph("Cool dialysate temperature", BODY_SB),
         Paragraph("Reduce 0.5-1 degC below body temp (stay &gt;35 degC); activates sympathetic NS, increases SVR and cardiac contractility", BODY_S)],
        ["",
         Paragraph("Limit UF rate to &lt;13 mL/kg/h", BODY_SB),
         Paragraph("Higher UF rates independently associated with increased mortality", BODY_S)],
        ["",
         Paragraph("UF profiling / modelling", BODY_SB),
         Paragraph("Remove more fluid early in session when plasma refilling is maximal", BODY_S)],
        ["",
         Paragraph("Extend time or increase dialysis frequency", BODY_SB),
         Paragraph("More time = lower required UF rate; 5-6x/week dialysis markedly reduces IDH", BODY_S)],
        ["",
         Paragraph("Bicarbonate dialysate", BODY_SB),
         Paragraph("Avoid acetate buffer; bicarbonate improves haemodynamic stability", BODY_S)],
        ["",
         Paragraph("Sequential dialysis (isolated UF then HD)", BODY_SB),
         Paragraph("Separate fluid removal from diffusion; limited but reasonable evidence", BODY_S)],
        ["",
         Paragraph("Match dialysate Na to serum Na", BODY_SB),
         Paragraph("Avoids positive Na balance; do NOT use routine sodium profiling (linked to increased mortality)", BODY_S)],
        # Pharmacological
        [Paragraph("PHARMA-\nCOLOGICAL", CAT_S),
         Paragraph("Midodrine 2.5-10 mg PO pre-dialysis", BODY_SB),
         Paragraph("Oral alpha-1 agonist; give 15-30 min before session. Peak at 60 min. "
                   "Second smaller dose mid-session may be needed (partial HD removal). "
                   "SE: supine HTN, urinary retention, pilomotor reactions. "
                   "Use when non-pharmacological strategies are insufficient.", BODY_S)],
    ]

    t = Table(data, colWidths=cw, repeatRows=1)
    ts = TableStyle([
        ("BACKGROUND",    (0,0), (-1,0), TEAL),
        ("ALIGN",         (0,0), (-1,0), "CENTER"),
        ("FONTSIZE",      (0,0), (-1,-1), 8),
        ("TOPPADDING",    (0,0), (-1,-1), 4),
        ("BOTTOMPADDING", (0,0), (-1,-1), 4),
        ("LEFTPADDING",   (0,0), (-1,-1), 5),
        ("RIGHTPADDING",  (0,0), (-1,-1), 5),
        ("VALIGN",        (0,0), (-1,-1), "TOP"),
        ("GRID",          (0,0), (-1,-1), 0.4, MID_GREY),
        # Category cell backgrounds
        ("BACKGROUND",    (0,1), (0,7),  HexColor("#1E8449")),
        ("BACKGROUND",    (0,8), (0,14), TEAL),
        ("BACKGROUND",    (0,15),(0,15), HexColor("#7D3C98")),
        # Data row backgrounds
        ("ROWBACKGROUNDS",(1,1), (-1,7),  [WHITE, LIGHT_GREY]),
        ("ROWBACKGROUNDS",(1,8), (-1,14), [LIGHT_TEAL, WHITE]),
        ("BACKGROUND",    (1,15),(-1,15), LIGHT_AMBER),
        # Span category cells
        ("SPAN",          (0,1), (0,7)),
        ("SPAN",          (0,8), (0,14)),
        ("SPAN",          (0,15),(0,15)),
        ("VALIGN",        (0,1), (0,15), "MIDDLE"),
        ("ALIGN",         (0,1), (0,15), "CENTER"),
        ("TEXTCOLOR",     (0,1), (0,15), WHITE),
    ])
    t.setStyle(ts)
    return t

# ── Midodrine box ─────────────────────────────────────────────────────────────
def midodrine_box():
    title_s  = S("MBT", fontName="Helvetica-Bold", fontSize=9, textColor=NAVY, leading=14)
    detail_s = S("MBD", fontName="Helvetica",      fontSize=8, textColor=BLACK, leading=12)
    data = [
        [Paragraph("MIDODRINE - Key Prescribing Points", title_s)],
        [Paragraph(
            "<b>Dose:</b> 2.5-10 mg orally, 15-30 min before dialysis  "
            "&nbsp;|&nbsp;  <b>Class:</b> Selective oral alpha-1 adrenergic agonist  "
            "&nbsp;|&nbsp;  <b>Peak:</b> ~60 min<br/>"
            "<b>Note:</b> Partially removed by HD - second smaller dose may be needed mid-session.<br/>"
            "<b>Side effects:</b> Supine hypertension, urinary retention, pilomotor reactions, GI complaints<br/>"
            "<b>When to use:</b> Resistant IDH despite non-pharmacological measures; autonomic insufficiency",
            detail_s)],
    ]
    t = Table(data, colWidths=[CONTENT_W])
    t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,-1), LIGHT_BLUE),
        ("TOPPADDING",    (0,0), (-1,-1), 5),
        ("BOTTOMPADDING", (0,0), (-1,-1), 5),
        ("LEFTPADDING",   (0,0), (-1,-1), 10),
        ("RIGHTPADDING",  (0,0), (-1,-1), 10),
        ("LINEAFTER",     (0,0), (0,-1),  5, NAVY),
        ("LINEBELOW",     (0,0), (0,0),   0.5, NAVY),
    ]))
    return t

# ── Footer ────────────────────────────────────────────────────────────────────
def footer():
    data = [[Paragraph(
        "Sources: Harrison's Principles of Internal Medicine 22E (2025); "
        "Comprehensive Clinical Nephrology 7th Ed; Brenner &amp; Rector's The Kidney  |  "
        "For clinical decisions, always apply current institutional protocols and individual patient assessment.",
        FOOT)]]
    t = Table(data, colWidths=[CONTENT_W])
    t.setStyle(TableStyle([
        ("TOPPADDING",    (0,0), (-1,-1), 4),
        ("BOTTOMPADDING", (0,0), (-1,-1), 2),
        ("LEFTPADDING",   (0,0), (-1,-1), 0),
        ("RIGHTPADDING",  (0,0), (-1,-1), 0),
        ("LINEABOVE",     (0,0), (0,0),   0.5, MID_GREY),
    ]))
    return t

# ── Build ─────────────────────────────────────────────────────────────────────
doc = SimpleDocTemplate(
    OUTPUT, pagesize=A4,
    leftMargin=MARGIN, rightMargin=MARGIN,
    topMargin=14*mm, bottomMargin=14*mm,
    title="IDH Quick Reference Guide",
    author="Dialysis Unit Protocol",
)

story = []

story.append(title_banner())
story.append(sp(6))
story.append(definition_box())
story.append(sp(8))

story.append(KeepTogether([
    sec_hdr("  CAUSES & RISK FACTORS", bg=NAVY),
    sp(4),
    risk_table(),
    sp(8),
]))

story.append(KeepTogether([
    sec_hdr("  IMMEDIATE MANAGEMENT - STEP BY STEP", bg=RED_ALERT),
    sp(4),
    immediate_mgmt(),
    sp(4),
    do_not_box(),
    sp(8),
]))

story.append(KeepTogether([
    sec_hdr("  SPECIAL CLINICAL SITUATIONS", bg=DARK_BG),
    sp(4),
    special_situations(),
    sp(8),
]))

story.append(sec_hdr("  PREVENTION STRATEGIES", bg=TEAL))
story.append(sp(4))
story.append(prevention_table())
story.append(sp(6))

story.append(midodrine_box())
story.append(sp(6))
story.append(footer())

doc.build(story)
print(f"PDF saved: {OUTPUT}")
Running Command

cd /home/daytona/workspace/idh-guide && python3 generate_idh_pdf.py 2>&1

Running Command

python3 -c " from pypdf import PdfReader r = PdfReader('/home/daytona/workspace/…

The PDF is building cleanly - 2 pages with all content rendering correctly. The file size is compact because it's vector text (no embedded images), which actually makes it ideal for printing. The PDF is ready.

Your IDH Quick Reference Guide is ready for download:

IDH Quick Reference Guide

PDF Document · PDF


What's inside (2-page A4 PDF):
SectionContent
Title BannerDark navy header with guide title and unit protocol label
Definition BoxBP criteria + incidence data side by side
Causes & Risk Factors9-row two-column table covering all major IDH triggers
Immediate Management5 colour-coded numbered steps (red -> amber -> blue -> green) with a red "DO NOT" warning box
Special Clinical Situations3-column panel: Early IDH / Recurrent IDH / Diabetic-autonomic IDH
Prevention StrategiesDetailed 3-section table (Patient Measures / Dialysis Prescription / Pharmacological) with colour-coded category bands
Midodrine Highlight BoxDose, timing, mechanism, side effects, indication - all at a glance
FooterSource citations (Harrison's, Comprehensive Clinical Nephrology, Brenner & Rector)
The guide is print-ready on A4 paper and suitable for laminating at the dialysis unit workstation.
This is a shared conversation. Sign in to Orris to start your own chat.