Hi
Creatinine based ckd epi formula
CKD-EPI 2021 creatinine formula eGFR equation race-free
| Parameter | Female | Male |
|---|---|---|
| κ (kappa) | 0.7 | 0.9 |
| α (alpha) | -0.241 | -0.302 |
| Age | Sex | Scr (µmol/L) | Equation |
|---|---|---|---|
| ≥18 | Female | ≤61.9 | 142 × (Scr/61.88)^-0.241 × 0.9938^Age × 1.012 |
| ≥18 | Female | >61.9 | 142 × (Scr/61.88)^-1.200 × 0.9938^Age × 1.012 |
| ≥18 | Male | ≤79.6 | 142 × (Scr/79.56)^-0.302 × 0.9938^Age |
| ≥18 | Male | >79.6 | 142 × (Scr/79.56)^-1.200 × 0.9938^Age |
| Equation | Race factor | Notes |
|---|---|---|
| MDRD (4-variable) | Yes (Black) | Underestimates when eGFR >60 |
| CKD-EPI 2009 | Yes (Black) | More accurate than MDRD at higher GFR |
| CKD-EPI 2021 | No | Current standard; same variables, refitted data |
Create a calculator tool or reference card for CKD-EPI 2021
mkdir -p /tmp/workspace/ckd-epi-card && ls /tmp/workspace/
~/ckd-epi-card/create_card.py
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import mm, cm
from reportlab.platypus import (
SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer,
HRFlowable, KeepTogether
)
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_RIGHT
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
import math
OUTPUT = "/tmp/workspace/ckd-epi-card/CKD-EPI_2021_Reference_Card.pdf"
# ── Colour palette ──────────────────────────────────────────────────────────
NAVY = colors.HexColor("#1a3560")
TEAL = colors.HexColor("#0e7490")
LTBLUE = colors.HexColor("#e0f2fe")
MNTEAL = colors.HexColor("#ccf5f8")
GOLD = colors.HexColor("#f59e0b")
LGRAY = colors.HexColor("#f1f5f9")
MGRAY = colors.HexColor("#cbd5e1")
DKGRAY = colors.HexColor("#334155")
WHITE = colors.white
RED = colors.HexColor("#dc2626")
AMBER = colors.HexColor("#d97706")
GREEN = colors.HexColor("#16a34a")
LGREEN = colors.HexColor("#dcfce7")
LAMBER = colors.HexColor("#fef3c7")
LRED = colors.HexColor("#fee2e2")
LLORANGE = colors.HexColor("#ffedd5")
ORANGE = colors.HexColor("#ea580c")
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
leftMargin=14*mm, rightMargin=14*mm,
topMargin=14*mm, bottomMargin=14*mm
)
styles = getSampleStyleSheet()
W = A4[0] - 28*mm # usable width
def style(name="Normal", **kw):
s = styles[name].clone(name + str(id(kw)))
for k, v in kw.items():
setattr(s, k, v)
return s
# ── Helper: coloured header block ───────────────────────────────────────────
def section_header(text, bg=NAVY, fg=WHITE, size=11):
data = [[Paragraph(f"<b>{text}</b>",
style(fontSize=size, textColor=fg, alignment=TA_LEFT,
leftIndent=4, spaceBefore=0, spaceAfter=0))]]
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),
]))
return t
def body(text, size=8.5, color=DKGRAY, indent=0, bold=False):
tag = f"<b>{text}</b>" if bold else text
return Paragraph(tag, style(fontSize=size, textColor=color,
leftIndent=indent, spaceBefore=1, spaceAfter=1))
# ═══════════════════════════════════════════════════════════════════════════
# BUILD STORY
# ═══════════════════════════════════════════════════════════════════════════
story = []
# ── TITLE BANNER ────────────────────────────────────────────────────────────
title_data = [[
Paragraph("<b>CKD-EPI 2021</b>",
style(fontSize=22, textColor=WHITE, alignment=TA_LEFT,
spaceBefore=0, spaceAfter=0)),
Paragraph("eGFR from Serum Creatinine<br/><font size='9'>Race-Free Equation · Adults ≥ 18 years</font>",
style(fontSize=12, textColor=LTBLUE, alignment=TA_RIGHT,
spaceBefore=0, spaceAfter=0)),
]]
title_tbl = Table(title_data, colWidths=[W*0.5, W*0.5])
title_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), NAVY),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("TOPPADDING", (0,0), (-1,-1), 10),
("BOTTOMPADDING", (0,0), (-1,-1), 10),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
]))
story.append(title_tbl)
story.append(Spacer(1, 5))
# ── FORMULA BOX ─────────────────────────────────────────────────────────────
story.append(section_header(" The Formula", bg=TEAL))
formula_text = (
"eGFR = 142 × min(Scr / κ, 1)<sup rise='4' size='7'>α</sup> "
"× max(Scr / κ, 1)<sup rise='4' size='7'>-1.200</sup> "
"× 0.9938<sup rise='4' size='7'>Age</sup> "
"× 1.012 <font color='#0e7490'>[if female]</font>"
)
formula_para = Paragraph(formula_text,
style(fontSize=13, textColor=NAVY, alignment=TA_CENTER,
spaceBefore=6, spaceAfter=6))
formula_box = Table([[formula_para]], colWidths=[W])
formula_box.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), LTBLUE),
("BOX", (0,0), (-1,-1), 1.2, TEAL),
("TOPPADDING", (0,0), (-1,-1), 10),
("BOTTOMPADDING", (0,0), (-1,-1), 10),
]))
story.append(formula_box)
story.append(Spacer(1, 4))
# ── PARAMETERS TABLE ────────────────────────────────────────────────────────
story.append(section_header(" Parameters"))
param_header = [
Paragraph("<b>Parameter</b>", style(fontSize=8.5, textColor=WHITE, alignment=TA_CENTER)),
Paragraph("<b>Female</b>", style(fontSize=8.5, textColor=WHITE, alignment=TA_CENTER)),
Paragraph("<b>Male</b>", style(fontSize=8.5, textColor=WHITE, alignment=TA_CENTER)),
Paragraph("<b>Notes</b>", style(fontSize=8.5, textColor=WHITE, alignment=TA_CENTER)),
]
def pc(txt, bold=False, align=TA_CENTER, size=8.5, color=DKGRAY):
t = f"<b>{txt}</b>" if bold else txt
return Paragraph(t, style(fontSize=size, textColor=color, alignment=align,
spaceBefore=2, spaceAfter=2))
param_rows = [
param_header,
[pc("κ (kappa)", bold=True), pc("0.7"), pc("0.9"),
pc("Threshold: Scr below/above κ determines exponent", align=TA_LEFT)],
[pc("α (alpha)", bold=True), pc("−0.241"), pc("−0.302"),
pc("Exponent when Scr ≤ κ", align=TA_LEFT)],
[pc("Scr", bold=True), pc("mg/dL", color=TEAL), pc("mg/dL", color=TEAL),
pc("Standardized serum creatinine (IDMS-traceable)", align=TA_LEFT)],
[pc("Age", bold=True), pc("years"), pc("years"),
pc("Decimal years acceptable; adults ≥18 only", align=TA_LEFT)],
[pc("Sex multiplier", bold=True), pc("× 1.012"), pc("× 1.000"),
pc("Female sex assigned at birth adds 1.2% uplift", align=TA_LEFT)],
]
cw = [W*0.15, W*0.12, W*0.12, W*0.61]
param_tbl = Table(param_rows, colWidths=cw, repeatRows=1)
param_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), NAVY),
("BACKGROUND", (0,1), (-1,1), LGRAY),
("BACKGROUND", (0,2), (-1,2), WHITE),
("BACKGROUND", (0,3), (-1,3), LGRAY),
("BACKGROUND", (0,4), (-1,4), WHITE),
("BACKGROUND", (0,5), (-1,5), LGRAY),
("GRID", (0,0), (-1,-1), 0.4, MGRAY),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 5),
("RIGHTPADDING", (0,0), (-1,-1), 5),
]))
story.append(param_tbl)
story.append(Spacer(1, 4))
# ── SI UNITS SIDEBAR ────────────────────────────────────────────────────────
story.append(section_header(" SI Units (µmol/L) — Expanded Lookup Table"))
si_note = (
"When creatinine is reported in <b>µmol/L</b>, use κ = <b>61.88</b> (female) or "
"<b>79.56</b> (male). The table below shows the correct sub-equation for each sex/range combination."
)
story.append(Paragraph(si_note, style(fontSize=8, textColor=DKGRAY,
spaceBefore=2, spaceAfter=4)))
si_header = [
pc("Sex", bold=True), pc("Scr (µmol/L)", bold=True),
pc("Exponent used", bold=True), pc("Equation", bold=True, align=TA_LEFT),
]
si_rows = [
si_header,
[pc("Female"), pc("≤ 61.9"), pc("α = −0.241"),
pc("142 × (Scr/61.88)⁻⁰·²⁴¹ × 0.9938ᴬᵍᵉ × 1.012", align=TA_LEFT)],
[pc("Female"), pc("> 61.9"), pc("−1.200"),
pc("142 × (Scr/61.88)⁻¹·²⁰⁰ × 0.9938ᴬᵍᵉ × 1.012", align=TA_LEFT)],
[pc("Male"), pc("≤ 79.6"), pc("α = −0.302"),
pc("142 × (Scr/79.56)⁻⁰·³⁰² × 0.9938ᴬᵍᵉ", align=TA_LEFT)],
[pc("Male"), pc("> 79.6"), pc("−1.200"),
pc("142 × (Scr/79.56)⁻¹·²⁰⁰ × 0.9938ᴬᵍᵉ", align=TA_LEFT)],
]
si_cw = [W*0.10, W*0.15, W*0.16, W*0.59]
si_tbl = Table(si_rows, colWidths=si_cw, repeatRows=1)
si_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), NAVY),
("BACKGROUND", (0,1), (-1,1), MNTEAL),
("BACKGROUND", (0,2), (-1,2), WHITE),
("BACKGROUND", (0,3), (-1,3), MNTEAL),
("BACKGROUND", (0,4), (-1,4), WHITE),
("GRID", (0,0), (-1,-1), 0.4, MGRAY),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 5),
("RIGHTPADDING", (0,0), (-1,-1), 5),
]))
story.append(si_tbl)
story.append(Spacer(1, 4))
# ── WORKED EXAMPLE ──────────────────────────────────────────────────────────
story.append(section_header(" Worked Example"))
ex_rows = [
[
Paragraph("<b>Given</b>", style(fontSize=8.5, textColor=WHITE, alignment=TA_CENTER)),
Paragraph("<b>Step-by-step calculation</b>", style(fontSize=8.5, textColor=WHITE, alignment=TA_LEFT)),
Paragraph("<b>Result</b>", style(fontSize=8.5, textColor=WHITE, alignment=TA_CENTER)),
],
[
Paragraph(
"Female<br/>Age: 55 yr<br/>Scr: 1.2 mg/dL",
style(fontSize=8, textColor=DKGRAY, alignment=TA_LEFT)
),
Paragraph(
"κ = 0.7, α = −0.241 (female)<br/>"
"Scr/κ = 1.2/0.7 = <b>1.714</b> → above κ, so min term = 1, max term = 1.714<br/>"
"min(1.714,1)⁻⁰·²⁴¹ = 1.000<br/>"
"max(1.714,1)⁻¹·²⁰⁰ = 1.714⁻¹·²⁰⁰ = <b>0.532</b><br/>"
"0.9938⁵⁵ = <b>0.712</b><br/>"
"eGFR = 142 × 1.000 × 0.532 × 0.712 × 1.012",
style(fontSize=8, textColor=DKGRAY, alignment=TA_LEFT)
),
Paragraph(
"<b>≈ 54.3</b><br/>mL/min<br/>/1.73m²",
style(fontSize=10, textColor=NAVY, alignment=TA_CENTER, leading=13)
),
],
[
Paragraph(
"Male<br/>Age: 40 yr<br/>Scr: 0.8 mg/dL",
style(fontSize=8, textColor=DKGRAY, alignment=TA_LEFT)
),
Paragraph(
"κ = 0.9, α = −0.302 (male)<br/>"
"Scr/κ = 0.8/0.9 = <b>0.889</b> → below κ, so min term = 0.889, max term = 1<br/>"
"min(0.889,1)⁻⁰·³⁰² = 0.889⁻⁰·³⁰² = <b>1.037</b><br/>"
"max(0.889,1)⁻¹·²⁰⁰ = 1.000<br/>"
"0.9938⁴⁰ = <b>0.781</b><br/>"
"eGFR = 142 × 1.037 × 1.000 × 0.781",
style(fontSize=8, textColor=DKGRAY, alignment=TA_LEFT)
),
Paragraph(
"<b>≈ 114.7</b><br/>mL/min<br/>/1.73m²",
style(fontSize=10, textColor=NAVY, alignment=TA_CENTER, leading=13)
),
],
]
ex_cw = [W*0.16, W*0.65, W*0.19]
ex_tbl = Table(ex_rows, colWidths=ex_cw, repeatRows=1)
ex_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), NAVY),
("BACKGROUND", (0,1), (-1,1), LGRAY),
("BACKGROUND", (0,2), (-1,2), WHITE),
("GRID", (0,0), (-1,-1), 0.4, MGRAY),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
]))
story.append(ex_tbl)
story.append(Spacer(1, 4))
# ── CKD STAGING TABLE ───────────────────────────────────────────────────────
story.append(section_header(" CKD Staging by eGFR (KDIGO 2024)"))
stage_data = [
[pc("Stage", bold=True), pc("eGFR (mL/min/1.73m²)", bold=True),
pc("Description", bold=True, align=TA_LEFT), pc("Colour code", bold=True)],
[pc("G1"), pc("≥ 90"), pc("Normal or high", align=TA_LEFT), pc("▮ Green")],
[pc("G2"), pc("60–89"), pc("Mildly decreased", align=TA_LEFT), pc("▮ Yellow-green")],
[pc("G3a"),pc("45–59"), pc("Mildly-moderately decreased", align=TA_LEFT), pc("▮ Yellow")],
[pc("G3b"),pc("30–44"), pc("Moderately-severely decreased", align=TA_LEFT), pc("▮ Orange")],
[pc("G4"), pc("15–29"), pc("Severely decreased", align=TA_LEFT), pc("▮ Red-orange")],
[pc("G5"), pc("< 15"), pc("Kidney failure", align=TA_LEFT), pc("▮ Dark red")],
]
stage_bg = [LGREEN, LGREEN, colors.HexColor("#f0fdf4"),
LAMBER, LLORANGE, LRED, colors.HexColor("#ffd7d7")]
stage_cw = [W*0.10, W*0.22, W*0.46, W*0.22]
stage_tbl = Table(stage_data, colWidths=stage_cw, repeatRows=1)
style_cmds = [
("BACKGROUND", (0,0), (-1,0), NAVY),
("GRID", (0,0), (-1,-1), 0.4, MGRAY),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 5),
("RIGHTPADDING", (0,0), (-1,-1), 5),
]
for i, bg in enumerate(stage_bg[1:], start=1):
style_cmds.append(("BACKGROUND", (0,i), (-1,i), bg))
color_map = [colors.HexColor("#16a34a"), colors.HexColor("#65a30d"),
colors.HexColor("#ca8a04"), colors.HexColor("#ea580c"),
colors.HexColor("#dc2626"), colors.HexColor("#991b1b")]
for i, c in enumerate(color_map, start=1):
style_cmds.append(("TEXTCOLOR", (3,i), (3,i), c))
stage_tbl.setStyle(TableStyle(style_cmds))
story.append(stage_tbl)
story.append(Spacer(1, 4))
# ── CLINICAL NOTES ──────────────────────────────────────────────────────────
story.append(section_header(" Clinical Notes & Limitations"))
notes_left = [
"• <b>No race variable</b> — 2021 update removed the 1.159 Black-race multiplier",
"• <b>IDMS-traceable creatinine</b> is required for accurate results",
"• <b>Muscle mass extremes</b>: may over/underestimate in amputees, bodybuilders, malnutrition",
"• <b>Acute AKI</b>: not valid — eGFR assumes steady-state creatinine",
"• <b>Dietary factors</b>: high protein intake transiently raises creatinine",
]
notes_right = [
"• Confirm CKD with <b>≥2 readings >90 days apart</b>",
"• Add <b>albuminuria (ACR)</b> for full KDIGO risk staging",
"• Consider <b>cystatin C</b> equation when muscle mass is atypical",
"• Use <b>CKD-EPI Cr+CysC 2021</b> for greatest accuracy",
"• Adjust drug doses when eGFR < 60 mL/min/1.73m²",
]
def notes_col(items, width):
rows = [[Paragraph(i, style(fontSize=7.8, textColor=DKGRAY,
spaceBefore=1, spaceAfter=1))] for i in items]
t = Table(rows, colWidths=[width])
t.setStyle(TableStyle([
("TOPPADDING", (0,0), (-1,-1), 2),
("BOTTOMPADDING", (0,0), (-1,-1), 2),
("LEFTPADDING", (0,0), (-1,-1), 4),
("RIGHTPADDING", (0,0), (-1,-1), 4),
]))
return t
notes_tbl = Table(
[[notes_col(notes_left, W*0.49), notes_col(notes_right, W*0.49)]],
colWidths=[W*0.50, W*0.50]
)
notes_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), LGRAY),
("BOX", (0,0), (-1,-1), 0.6, MGRAY),
("VALIGN", (0,0), (-1,-1), "TOP"),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 0),
("RIGHTPADDING", (0,0), (-1,-1), 0),
]))
story.append(notes_tbl)
story.append(Spacer(1, 4))
# ── QUICK REFERENCE: eGFR LOOKUP GRID ───────────────────────────────────────
story.append(section_header(" Quick eGFR Lookup — Female, mg/dL (Age × Creatinine)"))
ages_f = [25, 35, 45, 55, 65, 75]
scrs_f = [0.6, 0.7, 0.9, 1.1, 1.3, 1.5, 1.8, 2.0, 2.5, 3.0]
def ckd_epi_2021(scr, age, female=True):
k = 0.7 if female else 0.9
a = -0.241 if female else -0.302
sex_mult = 1.012 if female else 1.0
ratio = scr / k
min_part = min(ratio, 1) ** a
max_part = max(ratio, 1) ** (-1.200)
return round(142 * min_part * max_part * (0.9938 ** age) * sex_mult, 1)
def egfr_color(v):
if v >= 90: return LGREEN
if v >= 60: return colors.HexColor("#f0fdf4")
if v >= 45: return LAMBER
if v >= 30: return LLORANGE
if v >= 15: return LRED
return colors.HexColor("#ffd7d7")
# Header row
lookup_hdr = [pc("Scr↓ Age→", bold=True)] + [pc(str(a), bold=True) for a in ages_f]
lookup_rows = [lookup_hdr]
for scr in scrs_f:
row = [pc(f"{scr:.1f} mg/dL", bold=True)]
for age in ages_f:
v = ckd_epi_2021(scr, age, female=True)
row.append(pc(str(v)))
lookup_rows.append(row)
n_cols = 1 + len(ages_f)
col_w = W / n_cols
lookup_tbl = Table(lookup_rows, colWidths=[col_w]*n_cols, repeatRows=1)
lu_style = [
("BACKGROUND", (0,0), (-1,0), NAVY),
("BACKGROUND", (0,0), (0,-1), TEAL),
("GRID", (0,0), (-1,-1), 0.4, MGRAY),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
("LEFTPADDING", (0,0), (-1,-1), 3),
("RIGHTPADDING", (0,0), (-1,-1), 3),
]
for r, scr in enumerate(scrs_f, start=1):
for c, age in enumerate(ages_f, start=1):
v = ckd_epi_2021(scr, age, female=True)
lu_style.append(("BACKGROUND", (c,r), (c,r), egfr_color(v)))
if v < 30:
lu_style.append(("TEXTCOLOR", (c,r), (c,r), RED))
elif v < 60:
lu_style.append(("TEXTCOLOR", (c,r), (c,r), ORANGE))
elif v >= 90:
lu_style.append(("TEXTCOLOR", (c,r), (c,r), GREEN))
lookup_tbl.setStyle(TableStyle(lu_style))
story.append(lookup_tbl)
story.append(Spacer(1, 3))
# Male lookup
story.append(section_header(" Quick eGFR Lookup — Male, mg/dL (Age × Creatinine)"))
ages_m = [25, 35, 45, 55, 65, 75]
scrs_m = [0.7, 0.9, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.5, 3.0]
lookup_hdr_m = [pc("Scr↓ Age→", bold=True)] + [pc(str(a), bold=True) for a in ages_m]
lookup_rows_m = [lookup_hdr_m]
for scr in scrs_m:
row = [pc(f"{scr:.1f} mg/dL", bold=True)]
for age in ages_m:
v = ckd_epi_2021(scr, age, female=False)
row.append(pc(str(v)))
lookup_rows_m.append(row)
lookup_tbl_m = Table(lookup_rows_m, colWidths=[col_w]*n_cols, repeatRows=1)
lu_style_m = [
("BACKGROUND", (0,0), (-1,0), NAVY),
("BACKGROUND", (0,0), (0,-1), TEAL),
("GRID", (0,0), (-1,-1), 0.4, MGRAY),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
("LEFTPADDING", (0,0), (-1,-1), 3),
("RIGHTPADDING", (0,0), (-1,-1), 3),
]
for r, scr in enumerate(scrs_m, start=1):
for c, age in enumerate(ages_m, start=1):
v = ckd_epi_2021(scr, age, female=False)
lu_style_m.append(("BACKGROUND", (c,r), (c,r), egfr_color(v)))
if v < 30:
lu_style_m.append(("TEXTCOLOR", (c,r), (c,r), RED))
elif v < 60:
lu_style_m.append(("TEXTCOLOR", (c,r), (c,r), ORANGE))
elif v >= 90:
lu_style_m.append(("TEXTCOLOR", (c,r), (c,r), GREEN))
lookup_tbl_m.setStyle(TableStyle(lu_style_m))
story.append(lookup_tbl_m)
story.append(Spacer(1, 4))
# ── FOOTER ──────────────────────────────────────────────────────────────────
story.append(HRFlowable(width=W, thickness=0.5, color=MGRAY))
footer_text = (
"Reference: Inker LA et al. <i>N Engl J Med</i> 2021;385:1737–1749. "
"NKF–ASN Task Force 2021. KDIGO CKD Guidelines 2024. "
"<b>For clinical use only. Verify with institutional resources.</b>"
)
story.append(Paragraph(footer_text,
style(fontSize=7, textColor=colors.HexColor("#64748b"),
alignment=TA_CENTER, spaceBefore=3)))
# ── BUILD ───────────────────────────────────────────────────────────────────
doc.build(story)
print(f"PDF saved: {OUTPUT}")
cd /tmp/workspace/ckd-epi-card && python create_card.py 2>&1
CKD EPI 2021 Reference Card
PDF Document · PDF
| Section | Contents |
|---|---|
| Formula banner | Full CKD-EPI 2021 equation with superscripts |
| Parameters table | κ, α, sex multiplier for male and female |
| SI units table | All 4 sub-equations with µmol/L thresholds |
| Worked examples | Two fully solved step-by-step calculations (female Scr 1.2, male Scr 0.8) |
| CKD staging | KDIGO G1–G5 with colour coding |
| Quick eGFR lookup grids | Pre-calculated values for common creatinine levels × ages (separate tables for female and male), colour-coded by CKD stage |
| Clinical notes | 10 key limitations and practice points |
| Footer | Reference to Inker et al. NEJM 2021 |