~/antidiabetic-pdf/generate_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 mm, cm
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, KeepTogether
)
from reportlab.lib.enums import TA_CENTER, TA_LEFT
from reportlab.platypus import PageBreak
OUTPUT = "/home/daytona/workspace/antidiabetic-pdf/Antidiabetic_Drug_Reference.pdf"
# ── Colour palette ──────────────────────────────────────────────────────────
NAVY = colors.HexColor("#1a3a5c")
TEAL = colors.HexColor("#0d7377")
AMBER = colors.HexColor("#f5a623")
RED_LIGHT = colors.HexColor("#fff0f0")
RED_DARK = colors.HexColor("#c0392b")
GREEN_LIGHT= colors.HexColor("#f0fff4")
GREEN_DARK = colors.HexColor("#1e8449")
YELLOW_LT = colors.HexColor("#fffde7")
BLUE_LIGHT = colors.HexColor("#e8f4fd")
GREY_LT = colors.HexColor("#f5f6fa")
GREY_HD = colors.HexColor("#2c3e50")
WHITE = colors.white
# ── Styles ────────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()
def S(name, **kw):
return ParagraphStyle(name, **kw)
cover_title = S("CoverTitle", fontName="Helvetica-Bold", fontSize=26,
textColor=WHITE, alignment=TA_CENTER, leading=32)
cover_sub = S("CoverSub", fontName="Helvetica", fontSize=13,
textColor=colors.HexColor("#d6eaf8"), alignment=TA_CENTER, leading=18)
cover_date = S("CoverDate", fontName="Helvetica", fontSize=10,
textColor=colors.HexColor("#aed6f1"), alignment=TA_CENTER)
sec_head = S("SecHead", fontName="Helvetica-Bold", fontSize=13,
textColor=WHITE, alignment=TA_LEFT, leading=18,
backColor=NAVY, leftIndent=6, rightIndent=6,
spaceBefore=10, spaceAfter=4)
drug_head = S("DrugHead", fontName="Helvetica-Bold", fontSize=11,
textColor=TEAL, spaceBefore=8, spaceAfter=2)
body = S("Body", fontName="Helvetica", fontSize=9,
leading=13, spaceBefore=2, spaceAfter=2)
small = S("Small", fontName="Helvetica", fontSize=8,
leading=11, textColor=colors.HexColor("#555555"))
warn = S("Warn", fontName="Helvetica-Bold", fontSize=8,
textColor=RED_DARK)
caption = S("Caption", fontName="Helvetica-Oblique", fontSize=7.5,
textColor=colors.HexColor("#666666"), alignment=TA_CENTER)
tbl_hdr = S("TblHdr", fontName="Helvetica-Bold", fontSize=8.5,
textColor=WHITE, alignment=TA_CENTER)
tbl_cell = S("TblCell", fontName="Helvetica", fontSize=8,
leading=11)
tbl_cell_c = S("TblCellC", fontName="Helvetica", fontSize=8,
leading=11, alignment=TA_CENTER)
tbl_bold = S("TblBold", fontName="Helvetica-Bold", fontSize=8,
leading=11)
note_style = S("Note", fontName="Helvetica-Oblique", fontSize=8,
textColor=colors.HexColor("#555555"), leftIndent=8)
footer_style= S("Footer", fontName="Helvetica", fontSize=7,
textColor=colors.HexColor("#999999"), alignment=TA_CENTER)
# ── Helpers ────────────────────────────────────────────────────────────────────
def section_header(text):
data = [[Paragraph(text, tbl_hdr)]]
t = Table(data, colWidths=[175*mm])
t.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), NAVY),
('BOTTOMPADDING', (0,0), (-1,-1), 6),
('TOPPADDING', (0,0), (-1,-1), 6),
('LEFTPADDING', (0,0), (-1,-1), 8),
]))
return t
def sub_header(text, color=TEAL):
data = [[Paragraph(f"<b>{text}</b>", S("sh", fontName="Helvetica-Bold",
fontSize=10, textColor=WHITE))]]
t = Table(data, colWidths=[175*mm])
t.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), color),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('TOPPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 8),
]))
return t
def make_table(header_row, data_rows, col_widths, row_colors=None):
"""col_widths in mm, row_colors: list of (row_idx, color)"""
cw = [c*mm for c in col_widths]
all_rows = []
all_rows.append([Paragraph(h, tbl_hdr) for h in header_row])
for row in data_rows:
all_rows.append([Paragraph(str(cell), tbl_cell) if cell else Paragraph("", tbl_cell) for cell in row])
t = Table(all_rows, colWidths=cw, repeatRows=1)
style_cmds = [
('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),
('ROWBACKGROUNDS',(0,1),(-1,-1), [WHITE, GREY_LT]),
('GRID', (0,0), (-1,-1), 0.4, colors.HexColor("#cccccc")),
('VALIGN', (0,0), (-1,-1), 'TOP'),
('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 (ri, rc) in row_colors:
style_cmds.append(('BACKGROUND', (0, ri), (-1, ri), rc))
t.setStyle(TableStyle(style_cmds))
return t
def legend_box(items):
"""items: list of (color, label)"""
cells = []
for bg, label in items:
cells.append(Table([[Paragraph(f"<b>{label}</b>",
S("leg", fontName="Helvetica-Bold", fontSize=7.5,
textColor=colors.HexColor("#333333")))
]],
colWidths=[38*mm],
style=TableStyle([
('BACKGROUND', (0,0), (-1,-1), bg),
('BOX', (0,0), (-1,-1), 0.5, colors.HexColor("#aaaaaa")),
('TOPPADDING', (0,0), (-1,-1), 3),
('BOTTOMPADDING', (0,0), (-1,-1), 3),
('LEFTPADDING', (0,0), (-1,-1), 5),
])))
row = [cells]
t = Table(row, colWidths=[40*mm]*len(items))
t.setStyle(TableStyle([('VALIGN',(0,0),(-1,-1),'MIDDLE')]))
return t
# ── Document setup ────────────────────────────────────────────────────────────
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
leftMargin=15*mm, rightMargin=15*mm,
topMargin=15*mm, bottomMargin=18*mm,
title="Antidiabetic Drugs – Contraindications & Renal Adjustments",
author="Orris Medical Reference",
)
story = []
# ════════════════════════════════════════════════════════════════════════════
# COVER PAGE
# ════════════════════════════════════════════════════════════════════════════
cover_bg = Table(
[[Paragraph("ANTIDIABETIC DRUGS", cover_title)],
[Spacer(1, 8)],
[Paragraph("Quick Reference: Contraindications & Renal Dose Adjustments", cover_sub)],
[Spacer(1, 16)],
[Paragraph("Based on Lippincott Illustrated Reviews: Pharmacology<br/>"
"Comprehensive Clinical Nephrology 7th Ed. | Katzung 16th Ed.", cover_sub)],
[Spacer(1, 10)],
[Paragraph("July 2026 | Orris Medical Reference", cover_date)],
],
colWidths=[175*mm],
)
cover_bg.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), NAVY),
('TOPPADDING', (0,0), (-1,-1), 18),
('BOTTOMPADDING', (0,0), (-1,-1), 18),
('LEFTPADDING', (0,0), (-1,-1), 12),
('RIGHTPADDING', (0,0), (-1,-1), 12),
('ROUNDEDCORNERS', [8]),
]))
story.append(Spacer(1, 50))
story.append(cover_bg)
story.append(Spacer(1, 18))
# Disclaimer box
disc = Table([[Paragraph(
"<b>CLINICAL DISCLAIMER:</b> This quick-reference is for educational purposes. "
"Always verify doses against current local guidelines and SmPC. "
"eGFR thresholds given as CKD-EPI in mL/min/1.73 m<super>2</super>.",
S("disc", fontName="Helvetica", fontSize=8, textColor=GREY_HD,
leftIndent=4))]],
colWidths=[175*mm])
disc.setStyle(TableStyle([
('BACKGROUND', (0,0),(-1,-1), YELLOW_LT),
('BOX', (0,0),(-1,-1), 1, AMBER),
('TOPPADDING',(0,0),(-1,-1),6),
('BOTTOMPADDING',(0,0),(-1,-1),6),
('LEFTPADDING',(0,0),(-1,-1),8),
]))
story.append(disc)
story.append(PageBreak())
# ════════════════════════════════════════════════════════════════════════════
# LEGEND
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("HOW TO USE THIS REFERENCE"))
story.append(Spacer(1, 6))
story.append(Paragraph(
"Each drug class is presented with: (1) absolute contraindications, (2) precautions/relative contraindications, "
"and (3) renal dosing guidance. Row shading in tables follows the key below.",
body))
story.append(Spacer(1, 4))
story.append(legend_box([
(RED_LIGHT, " AVOID / Contraindicated"),
(YELLOW_LT, " Use with CAUTION / Reduce dose"),
(GREEN_LIGHT," Generally safe in CKD"),
(BLUE_LIGHT, " Special note"),
]))
story.append(Spacer(1, 10))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 1 — METFORMIN (BIGUANIDE)
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("1. BIGUANIDE — METFORMIN"))
story.append(Spacer(1, 4))
# Contraindications table
story.append(sub_header("Absolute Contraindications", RED_DARK))
ci_hdr = ["Condition", "Reason"]
ci_data = [
["eGFR < 30 mL/min/1.73 m²", "Accumulates → lactic acidosis risk"],
["Acute/decompensated heart failure", "Tissue hypoperfusion → lactic acidosis"],
["Sepsis / haemodynamic instability", "Reduced renal perfusion"],
["Acute myocardial infarction", "Circulatory failure → lactic acidosis"],
["IV iodinated contrast (peri-procedure)", "Contrast nephropathy → metformin accumulation"],
["Severe hepatic impairment", "Impaired lactate clearance"],
["Alcohol abuse (heavy)", "Increased lactic acidosis risk"],
]
story.append(make_table(ci_hdr, ci_data, [85, 90],
row_colors=[(i+1, RED_LIGHT) for i in range(len(ci_data))]))
story.append(Spacer(1, 6))
story.append(sub_header("Renal Dose Adjustments", TEAL))
ren_data = [
["eGFR ≥ 60", "Normal dose (500–2000 mg/day)", "No restriction"],
["eGFR 45–59", "Reduce dose; monitor renal function 3–6 monthly", "Caution"],
["eGFR 30–44", "Reduce to 50% of usual dose; monitor closely", "Use with caution"],
["eGFR < 30", "CONTRAINDICATED", "Avoid"],
["eGFR < 30 (dialysis)", "CONTRAINDICATED — not removed by dialysis", "Avoid"],
]
story.append(make_table(
["eGFR (mL/min/1.73 m²)", "Recommendation", "Status"],
ren_data, [50, 95, 30],
row_colors=[
(1, GREEN_LIGHT),(2, GREEN_LIGHT),
(3, YELLOW_LT),(4, RED_LIGHT),(5, RED_LIGHT)
]))
story.append(Spacer(1, 4))
story.append(Paragraph(
"Note: Hold metformin 48 h before and 48 h after IV contrast if eGFR 30–60; "
"restart only after renal function confirmed stable. "
"Monitor vitamin B12 annually with long-term use.",
note_style))
story.append(Spacer(1, 10))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 2 — SULFONYLUREAS
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("2. SULFONYLUREAS"))
story.append(Spacer(1, 4))
story.append(sub_header("Contraindications (Class-wide)", RED_DARK))
su_ci = [
["Type 1 diabetes mellitus", "No functioning beta-cells"],
["Diabetic ketoacidosis", "Require insulin, not secretagogues"],
["Severe hepatic impairment", "Impaired metabolism → hypoglycaemia"],
["Sulfonamide / sulfonylurea allergy", "Cross-reactivity possible"],
["Pregnancy", "Teratogenicity; neonatal hypoglycaemia"],
]
story.append(make_table(["Contraindication","Reason"], su_ci, [85,90],
row_colors=[(i+1, RED_LIGHT) for i in range(len(su_ci))]))
story.append(Spacer(1,6))
story.append(sub_header("Renal Dose Adjustments by Agent", TEAL))
su_ren = [
["Glipizide", "Hepatic (safe)", "Normal dose throughout CKD", "Preferred in CKD"],
["Gliclazide", "Hepatic (safe)", "Normal dose; use with caution eGFR 30–60", "Preferred in CKD"],
["Glimepiride", "Hepatic → renal", "Start low (1 mg); avoid if eGFR < 30", "Caution"],
["Glyburide\n(Glibenclamide)", "Renal active metabolites",
"AVOID in any degree of renal impairment", "Contraindicated in CKD"],
["Tolbutamide", "Hepatic", "Relatively safe but avoid in severe renal disease", "Older agent"],
]
story.append(make_table(
["Drug","Elimination","Recommendation","Notes"],
su_ren, [30, 32, 72, 41],
row_colors=[
(1,GREEN_LIGHT),(2,GREEN_LIGHT),(3,YELLOW_LT),
(4,RED_LIGHT),(5,YELLOW_LT)
]))
story.append(Paragraph(
"Key risk: Hypoglycaemia is prolonged in renal impairment — renally cleared active metabolites accumulate. "
"Glipizide and gliclazide are the preferred sulfonylureas in CKD.",
note_style))
story.append(Spacer(1, 10))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 3 — MEGLITINIDES
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("3. MEGLITINIDES (Glinides)"))
story.append(Spacer(1, 4))
meg_data = [
["Repaglinide", "Mainly hepatic biliary", "Normal dose in mild–mod CKD\nStart 0.5 mg in severe CKD; titrate cautiously", "Preferred glinide in CKD"],
["Nateglinide", "Hepatic + renal (16%)", "Normal dose; caution in severe CKD\nActive metabolite accumulates in ESRD", "Caution in eGFR < 30"],
]
story.append(make_table(
["Drug","Elimination","Renal Guidance","Notes"],
meg_data, [32, 36, 68, 39],
row_colors=[(1,GREEN_LIGHT),(2,YELLOW_LT)]))
story.append(Paragraph(
"Contraindications: Type 1 DM, DKA, severe hepatic impairment. "
"Both require functioning beta-cells. Hypoglycaemia risk is lower than sulfonylureas.",
note_style))
story.append(Spacer(1, 10))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 4 — THIAZOLIDINEDIONES
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("4. THIAZOLIDINEDIONES (TZDs / Glitazones)"))
story.append(Spacer(1, 4))
story.append(sub_header("Absolute Contraindications", RED_DARK))
tzd_ci = [
["NYHA Class III–IV heart failure", "Cause fluid retention/oedema → worsen HF"],
["Active or history of bladder cancer (Pioglitazone)", "Association with bladder cancer risk"],
["Severe hepatic impairment (ALT > 2.5× ULN)", "Hepatotoxicity"],
["Pregnancy", "Teratogenicity risk"],
["Type 1 DM", "Require insulin; insulin sensitisers ineffective alone"],
]
story.append(make_table(["Contraindication","Reason"], tzd_ci, [85,90],
row_colors=[(i+1, RED_LIGHT) for i in range(len(tzd_ci))]))
story.append(Spacer(1,6))
story.append(sub_header("Renal Dose Adjustments", TEAL))
tzd_ren = [
["Pioglitazone", "Hepatic (CYP2C8)", "No dose adjustment required in CKD",
"Fluid retention worsens as CKD progresses — monitor"],
["Rosiglitazone", "Hepatic (CYP2C8)", "No dose adjustment required",
"Largely withdrawn — cardiovascular concerns"],
]
story.append(make_table(
["Drug","Elimination","Renal Guidance","Caution"],
tzd_ren, [30, 32, 60, 53],
row_colors=[(1,GREEN_LIGHT),(2,YELLOW_LT)]))
story.append(Paragraph(
"Although TZDs do not require dose reduction in renal impairment, the resulting fluid retention is particularly "
"problematic in CKD and can precipitate or worsen heart failure. Monitor weight and oedema closely. "
"Fracture risk is increased (especially distal limbs in women).",
note_style))
story.append(Spacer(1, 10))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 5 — GLP-1 RECEPTOR AGONISTS
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("5. GLP-1 RECEPTOR AGONISTS"))
story.append(Spacer(1, 4))
story.append(sub_header("Contraindications (Class)", RED_DARK))
glp_ci = [
["Personal/family history of medullary thyroid carcinoma", "Thyroid C-cell tumour risk (rodent data)"],
["Multiple Endocrine Neoplasia type 2 (MEN2)", "Same thyroid risk"],
["History of pancreatitis", "Relative CI — may worsen pancreatitis"],
["Severe gastroparesis", "Slow gastric emptying contraindicated"],
["Type 1 DM (as monotherapy)", "Risk of DKA; not approved for T1DM"],
]
story.append(make_table(["Contraindication","Reason"], glp_ci, [90,85],
row_colors=[(i+1, RED_LIGHT) for i in range(len(glp_ci))]))
story.append(Spacer(1,6))
story.append(sub_header("Renal Dose Adjustments", TEAL))
glp_ren = [
["Semaglutide (SC/oral)","Proteolytic degradation","No dose adjustment in CKD\nUse with caution in severe CKD (limited data)","Monitor nausea/dehydration"],
["Dulaglutide","Proteolytic degradation","No dose adjustment required","Data limited in ESRD"],
["Liraglutide","Proteolytic degradation","No dose adjustment; use caution eGFR < 15","Monitor renal function"],
["Exenatide (twice daily)","Renal excretion","Avoid if eGFR < 30\nNo dose adj if eGFR 30–50 but monitor","Renally excreted"],
["Exenatide (once weekly)","Renal excretion","Avoid if eGFR < 45","Slower release — risk accumulates"],
["Lixisenatide","Renal excretion","Avoid if eGFR < 30","Limited data in severe CKD"],
]
story.append(make_table(
["Drug","Elimination","Renal Guidance","Notes"],
glp_ren, [32, 32, 75, 36],
row_colors=[
(1,GREEN_LIGHT),(2,GREEN_LIGHT),(3,GREEN_LIGHT),
(4,RED_LIGHT),(5,YELLOW_LT),(6,RED_LIGHT)
]))
story.append(Paragraph(
"GLP-1 RAs can cause dehydration (nausea/vomiting) which worsens renal function — "
"monitor eGFR in CKD. Liraglutide and semaglutide have proven cardiovascular benefit and "
"are preferred in T2DM with ASCVD.",
note_style))
story.append(Spacer(1, 10))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 6 — DPP-4 INHIBITORS
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("6. DPP-4 INHIBITORS (Gliptins)"))
story.append(Spacer(1, 4))
story.append(sub_header("Contraindications", RED_DARK))
dpp_ci = [
["History of DPP-4 inhibitor-induced pancreatitis", "Class-wide risk of pancreatitis"],
["Severe heart failure — Saxagliptin specifically", "Saxagliptin increases HF hospitalizations"],
["Hypersensitivity to drug or excipients", "Anaphylaxis / angioedema reported"],
["Do NOT combine DPP-4i with GLP-1 RA", "No additive benefit; increased GI ADR"],
]
story.append(make_table(["Contraindication","Reason"], dpp_ci, [90,85],
row_colors=[(i+1, RED_LIGHT) for i in range(len(dpp_ci))]))
story.append(Spacer(1,6))
story.append(sub_header("Renal Dose Adjustments", TEAL))
dpp_ren = [
["Sitagliptin", "Renal (87% unchanged)",
"eGFR ≥ 50: 100 mg once daily (full dose)\n"
"eGFR 30–49: Reduce to 50 mg once daily\n"
"eGFR < 30 / dialysis: Reduce to 25 mg once daily",
"Dose-reduce; safe in ESRD with adjustment"],
["Saxagliptin", "Renal + CYP3A4 (active metabolite)",
"eGFR ≥ 50: 5 mg once daily\n"
"eGFR < 50 / dialysis: Reduce to 2.5 mg once daily",
"Caution in HF"],
["Alogliptin", "Renal (76% unchanged)",
"eGFR ≥ 60: 25 mg once daily\n"
"eGFR 30–59: 12.5 mg once daily\n"
"eGFR < 30 / dialysis: 6.25 mg once daily",
"Dialysis: give after session"],
["Linagliptin", "Enterohepatic (biliary/faecal)",
"NO dose adjustment in any stage of CKD\n"
"Safe in dialysis — not renally eliminated",
"PREFERRED in CKD"],
]
story.append(make_table(
["Drug","Elimination","Dose Adjustment Detail","Notes"],
dpp_ren, [28, 30, 80, 37],
row_colors=[(1,YELLOW_LT),(2,YELLOW_LT),(3,YELLOW_LT),(4,GREEN_LIGHT)]))
story.append(Paragraph(
"Linagliptin is the ONLY gliptin that requires NO dose reduction in CKD — the preferred DPP-4i when renal function is impaired.",
note_style))
story.append(Spacer(1, 10))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 7 — SGLT2 INHIBITORS
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("7. SGLT2 INHIBITORS (Gliflozins)"))
story.append(Spacer(1, 4))
story.append(sub_header("Contraindications", RED_DARK))
sglt_ci = [
["eGFR < 30 (glycaemic indication)", "Mechanism requires adequate GFR — ineffective + risky"],
["Type 1 Diabetes Mellitus", "High risk of euglycaemic DKA"],
["Recurrent urinary tract or genital infections", "SGLT2i significantly increase mycotic infections"],
["Prior DKA on SGLT2 inhibitor", "Absolute contraindication"],
["Severe volume depletion / dehydration", "Osmotic diuresis worsens hypovolaemia"],
["Canagliflozin + peripheral artery disease / prior amputation", "Increased amputation risk with canagliflozin"],
]
story.append(make_table(["Contraindication","Reason"], sglt_ci, [85,90],
row_colors=[(i+1, RED_LIGHT) for i in range(len(sglt_ci))]))
story.append(Spacer(1,6))
story.append(sub_header("Renal Dose Adjustments & eGFR Thresholds", TEAL))
sglt_ren = [
["Empagliflozin",
"eGFR ≥ 45: 10–25 mg once daily (full glycaemic dose)\n"
"eGFR 20–44: 10 mg daily for HF/CKD indication (not glycaemic)\n"
"eGFR < 20: Avoid for glycaemic control; HF indication use 10 mg",
"Approved HFrEF + HFpEF"],
["Dapagliflozin",
"eGFR ≥ 45: 10 mg once daily (glycaemic)\n"
"eGFR 25–44: 10 mg for HF or CKD indication (not glycaemia)\n"
"eGFR < 25: Avoid for glycaemic control; continue if CKD/HF indication",
"Approved HFrEF + CKD"],
["Canagliflozin",
"eGFR ≥ 60: 100–300 mg once daily\n"
"eGFR 45–59: 100 mg once daily (glycaemic)\n"
"eGFR 30–44: 100 mg (CKD/CVD indication); avoid for glycaemia alone\n"
"eGFR < 30: CONTRAINDICATED for glycaemia; CKD benefit data available",
"Amputation & fracture risk"],
["Ertugliflozin",
"eGFR ≥ 60: Full dose (5–15 mg)\n"
"eGFR 30–59: Do not initiate (reduced efficacy)\n"
"eGFR < 30: Contraindicated",
"No special organ benefit proven"],
]
story.append(make_table(
["Drug","Renal Dose Guidance","Notes"],
sglt_ren, [28, 112, 35],
row_colors=[(1,GREEN_LIGHT),(2,GREEN_LIGHT),(3,YELLOW_LT),(4,YELLOW_LT)]))
story.append(Paragraph(
"Key concept: SGLT2 inhibitors have distinct thresholds for GLYCAEMIC vs CARDIORENAL indications. "
"Even below the glycaemic eGFR threshold, empagliflozin/dapagliflozin/canagliflozin may still be "
"continued for cardiovascular or CKD benefits — always confirm the indication.",
note_style))
story.append(Spacer(1, 10))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 8 — INSULIN
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("8. INSULIN"))
story.append(Spacer(1, 4))
story.append(Paragraph("Insulin has NO absolute renal contraindication, but requires dose monitoring due to reduced renal clearance.", body))
story.append(Spacer(1,4))
ins_ren = [
["eGFR 60–89 (CKD G2)", "Normal doses; monitor for hypoglycaemia"],
["eGFR 30–59 (CKD G3)", "Reduce total daily dose by 25%; increase monitoring"],
["eGFR 15–29 (CKD G4)", "Reduce total daily dose by 50%; increased hypoglycaemia risk"],
["eGFR < 15 / Dialysis",
"Reduce dose by 50–75%; insulin clearance reduced\n"
"Patients on dialysis may need significantly less insulin\n"
"Hold if patient is fasting or not eating"],
]
story.append(make_table(
["Stage of CKD","Guidance"],
ins_ren, [55, 120],
row_colors=[
(1,GREEN_LIGHT),(2,GREEN_LIGHT),(3,YELLOW_LT),(4,RED_LIGHT)
]))
story.append(Paragraph(
"Rationale: Kidney is responsible for ~20–30% of insulin degradation. As GFR declines, "
"insulin half-life increases → hypoglycaemia risk rises. Gluconeogenesis is also impaired in CKD.",
note_style))
story.append(Spacer(1, 10))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 9 — ALPHA-GLUCOSIDASE INHIBITORS
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("9. ALPHA-GLUCOSIDASE INHIBITORS"))
story.append(Spacer(1, 4))
agi_data = [
["Acarbose", "GI (minimal systemic)",
"Avoid if serum creatinine > 2 mg/dL (eGFR < 30 approx)\nNot recommended in ESRD",
"GI side-effects limit use; if hypoglycaemia occurs — must treat with GLUCOSE, not sucrose"],
["Miglitol", "Renal (absorbed systemically)",
"Contraindicated if eGFR < 30\nAccumulates significantly in renal impairment",
"Absorbed unlike acarbose — renal accumulation is clinically significant"],
]
story.append(make_table(
["Drug","Elimination","Renal Guidance","Notes"],
agi_data, [28, 28, 66, 53],
row_colors=[(1,YELLOW_LT),(2,RED_LIGHT)]))
story.append(Spacer(1,4))
story.append(Paragraph(
"Contraindications (class): IBD, colonic ulceration, intestinal obstruction, malabsorption syndromes, liver cirrhosis.",
note_style))
story.append(Spacer(1, 10))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 10 — MASTER SUMMARY TABLE
# ════════════════════════════════════════════════════════════════════════════
story.append(PageBreak())
story.append(section_header("MASTER SUMMARY: RENAL THRESHOLDS AT A GLANCE"))
story.append(Spacer(1, 6))
master_data = [
["Metformin", "eGFR < 30", "eGFR 30–44", "eGFR ≥ 45", "Risk: Lactic acidosis"],
["Glyburide", "Any CKD", "Any CKD", "Use alt. agent","Active metabolites"],
["Glipizide/Gliclazide","eGFR < 30", "Caution", "Full dose", "Preferred SU in CKD"],
["Repaglinide", "Caution", "Low start dose","Full dose", "Low CKD risk"],
["Pioglitazone/TZD", "N/A", "Monitor oedema","Full dose", "No renal adjustment"],
["Sitagliptin", "25 mg/day", "50 mg/day", "100 mg/day", "Dose reduce"],
["Linagliptin", "Full dose", "Full dose", "Full dose", "No adjustment needed"],
["Saxagliptin", "2.5 mg/day", "2.5 mg/day", "5 mg/day", "Avoid in HF"],
["Liraglutide/Semaglutide","Caution","Caution", "Full dose", "Dehydration risk"],
["Exenatide (BID)", "Avoid", "Monitor", "Full dose", "Renally cleared"],
["Empagliflozin", "10 mg (HF)", "10 mg (HF/CKD)","10–25 mg", "Dual glycaemic/HF use"],
["Dapagliflozin", "Avoid gluc.","10 mg (HF/CKD)","10 mg", "CKD + HF benefit"],
["Canagliflozin", "Avoid gluc.","100 mg (CKD)", "100–300 mg", "Amputation risk"],
["Insulin (all)", "Reduce 50–75%","Reduce 50%", "Reduce 25%", "Monitor carefully"],
["Acarbose", "Avoid", "Caution", "Full dose", "Avoid eGFR < 30"],
]
def colored_cell(text, bg):
return Paragraph(text, S("mc", fontName="Helvetica", fontSize=7.5,
leading=10, backColor=bg))
master_header = ["Drug", "eGFR < 30\n(Severe CKD/ESRD)", "eGFR 30–44\n(Mod-Severe CKD)",
"eGFR ≥ 45\n(Mild-Mod CKD)", "Key Note"]
# colour code the eGFR columns
def status_color(text):
t = text.strip().lower()
if "avoid" in t or "contraindicated" in t:
return RED_LIGHT
elif "caution" in t or "reduce" in t or "monitor" in t or "mg (hf" in t or "mg (ckd" in t:
return YELLOW_LT
else:
return GREEN_LIGHT
from reportlab.platypus import Table as RLTable, TableStyle as RLTableStyle
all_master_rows = [[Paragraph(h, tbl_hdr) for h in master_header]]
for row in master_data:
styled_row = [
Paragraph(row[0], tbl_bold),
Paragraph(row[1], S("mc1", fontName="Helvetica", fontSize=7.5, leading=10,
backColor=status_color(row[1]))),
Paragraph(row[2], S("mc2", fontName="Helvetica", fontSize=7.5, leading=10,
backColor=status_color(row[2]))),
Paragraph(row[3], S("mc3", fontName="Helvetica", fontSize=7.5, leading=10,
backColor=status_color(row[3]))),
Paragraph(row[4], S("mc4", fontName="Helvetica-Oblique", fontSize=7.5, leading=10)),
]
all_master_rows.append(styled_row)
master_t = RLTable(all_master_rows, colWidths=[35*mm, 37*mm, 37*mm, 37*mm, 29*mm], repeatRows=1)
master_t.setStyle(RLTableStyle([
('BACKGROUND', (0,0), (-1,0), NAVY),
('TEXTCOLOR', (0,0), (-1,0), WHITE),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('GRID', (0,0), (-1,-1), 0.3, colors.HexColor("#cccccc")),
('VALIGN', (0,0), (-1,-1), 'TOP'),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 4),
('RIGHTPADDING', (0,0), (-1,-1), 4),
('ROWBACKGROUNDS', (0,1), (0,-1), [WHITE, GREY_LT]),
]))
story.append(master_t)
story.append(Spacer(1, 10))
story.append(legend_box([
(RED_LIGHT, " Avoid / Contraindicated"),
(YELLOW_LT, " Caution / Dose Reduce"),
(GREEN_LIGHT, " Generally safe (usual or adjusted dose)"),
]))
story.append(Spacer(1, 10))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 11 — SPECIAL POPULATIONS
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("SPECIAL POPULATIONS & ADDITIONAL NOTES"))
story.append(Spacer(1,4))
sp_data = [
["Elderly",
"Increased hypoglycaemia risk with sulfonylureas/insulin\n"
"Prefer metformin (if eGFR permits), DPP-4i, or low-dose SGLT2i\n"
"Avoid glyburide and chlorpropamide\n"
"Use short-acting insulins with close monitoring"],
["Hepatic Impairment",
"Avoid: Metformin (lactic acidosis), TZDs (hepatotoxicity), glyburide\n"
"Safe options: DPP-4i (most), GLP-1 RA (proteolytic degradation)\n"
"Reduce sulfonylurea doses in severe impairment\n"
"Insulin titration may be required; unpredictable gluconeogenesis"],
["Pregnancy",
"Insulin is the preferred treatment for all types of diabetes in pregnancy\n"
"Metformin used in GDM but crosses placenta; long-term infant data limited\n"
"Avoid: Sulfonylureas (neonatal hypoglycaemia), SGLT2i, GLP-1 RA, TZDs\n"
"DPP-4 inhibitors: insufficient data — avoid"],
["Contrast Nephropathy\nPrevention",
"Hold metformin 48 h before AND 48 h after IV contrast if eGFR 30–60\n"
"Do not restart until renal function is confirmed normal\n"
"SGLT2 inhibitors: hold 3 days before major surgery/procedures\n"
"Euglycaemic DKA risk with SGLT2i perioperatively"],
["Dialysis (ESRD)",
"Metformin: CONTRAINDICATED\n"
"Linagliptin: Safe (no renal elimination) — preferred DPP-4i\n"
"Sitagliptin/Alogliptin: Reduce to lowest dose; give post-dialysis\n"
"SGLT2i: Generally ineffective (mechanism depends on GFR) except for cardiac indications\n"
"Insulin: Continue with up to 75% dose reduction; monitor closely"],
]
story.append(make_table(
["Population","Key Prescribing Points"],
sp_data, [40, 135]))
story.append(Spacer(1, 12))
# Footer / reference
story.append(HRFlowable(width="100%", thickness=0.5, color=colors.HexColor("#cccccc")))
story.append(Spacer(1, 4))
story.append(Paragraph(
"Sources: Lippincott Illustrated Reviews: Pharmacology; Comprehensive Clinical Nephrology 7th Ed. (Elsevier); "
"Katzung's Basic and Clinical Pharmacology 16th Ed. | Generated by Orris Medical Reference · July 2026",
footer_style))
# ── Build ─────────────────────────────────────────────────────────────────────
doc.build(story)
print(f"PDF saved → {OUTPUT}")