Guideline to start statin for opd patients
statin therapy initiation guidelines cardiovascular risk
2024 ACC AHA statin guidelines primary prevention OPD outpatient indications
"Patients with clinical ASCVD... are recommended to begin high-intensity statin therapy with an aim of achieving a 50% or greater reduction in LDL-C levels." - Current Surgical Therapy 14e
| 10-Year PREVENT Risk | Recommendation |
|---|---|
| <3% | Lifestyle modification only; statin generally not indicated |
| 3% to <5% (borderline) | LDL-lowering therapy is reasonable after clinician-patient discussion |
| 5% to <10% (intermediate) | Statin recommended after risk discussion |
| ≥10% (high) | Statin strongly recommended |
Harrison's 22E (2025): "Current guidelines suggest that a 10-year risk >7.5% merits consideration of statin therapy regardless of plasma LDL-C level."
| Intensity | LDL-C Reduction | Examples |
|---|---|---|
| High | ≥50% | Atorvastatin 40-80 mg, Rosuvastatin 20-40 mg |
| Moderate | 30-49% | Atorvastatin 10-20 mg, Rosuvastatin 5-10 mg, Simvastatin 20-40 mg |
| Low | <30% | Simvastatin 10 mg, Pravastatin 10-20 mg |
Create a statin selection flowchart/decision tree for your clinic
ls /home/daytona/workspace/ 2>/dev/null && mkdir -p /home/daytona/workspace/statin-flowchart
~/statin-flowchart/build.js
const pptxgen = require("pptxgenjs");
const pres = new pptxgen();
pres.layout = "LAYOUT_WIDE"; // 13.3 x 7.5 inches
// ── Colour palette ────────────────────────────────────────────────────────────
const C = {
navy: "0B2545", // dominant dark
teal: "1B6CA8", // secondary
sky: "4DA6D9", // light blue accent
green: "2E8B57", // YES / go
amber: "D4870A", // intermediate / caution
red: "C0392B", // high risk / stop
white: "FFFFFF",
offWhite:"F4F8FC",
grey: "7F8C8D",
lightGrey:"E8EFF5",
yellow: "FFF3CD",
darkGrey:"2C3E50",
};
// ── Helper: rounded box ───────────────────────────────────────────────────────
function box(slide, {x, y, w, h, fill, line, lineW, radius, shadow}) {
slide.addShape(pres.ShapeType.roundRect, {
x, y, w, h,
fill: { color: fill || C.white },
line: { color: line || "CCCCCC", width: lineW || 1 },
rectRadius: radius !== undefined ? radius : 0.08,
shadow: shadow || null,
});
}
// ── Helper: centred label inside a box ───────────────────────────────────────
function label(slide, text, {x, y, w, h, color, fontSize, bold, italic, wrap}) {
slide.addText(text, {
x, y, w, h,
fontSize: fontSize || 11,
color: color || C.white,
bold: bold !== undefined ? bold : true,
italic: italic || false,
align: "center",
valign: "middle",
wrap: wrap !== undefined ? wrap : true,
margin: 4,
});
}
// ── Helper: diamond shape ─────────────────────────────────────────────────────
function diamond(slide, {x, y, w, h, fill, line}) {
// Approximate with rotated rectangle using addShape if available, else use lines
slide.addShape(pres.ShapeType.diamond, {
x, y, w, h,
fill: { color: fill || C.amber },
line: { color: line || C.navy, width: 1.5 },
});
}
// ── Helper: arrow connector (horizontal) ─────────────────────────────────────
function arrowH(slide, x1, y, x2, color) {
slide.addShape(pres.ShapeType.line, {
x: x1, y, w: x2 - x1, h: 0,
line: { color: color || C.navy, width: 1.5, endArrowType: "arrow" },
});
}
// ── Helper: arrow connector (vertical) ───────────────────────────────────────
function arrowV(slide, x, y1, y2, color) {
slide.addShape(pres.ShapeType.line, {
x, y: y1, w: 0, h: y2 - y1,
line: { color: color || C.navy, width: 1.5, endArrowType: "arrow" },
});
}
function arrowLabel(slide, text, {x, y, fontSize, color}) {
slide.addText(text, {
x, y, w: 0.5, h: 0.22,
fontSize: fontSize || 8,
color: color || C.green,
bold: true,
align: "center",
margin: 0,
});
}
// ════════════════════════════════════════════════════════════════════════════
// SLIDE 1 – COVER
// ════════════════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
// Full dark background
s.addShape(pres.ShapeType.rect, { x:0, y:0, w:13.3, h:7.5, fill:{ color: C.navy } });
// Decorative teal accent bar (left)
s.addShape(pres.ShapeType.rect, { x:0, y:0, w:0.18, h:7.5, fill:{ color: C.sky } });
// Top accent strip
s.addShape(pres.ShapeType.rect, { x:0.18, y:0, w:13.12, h:0.12, fill:{ color: C.teal } });
// Main title
s.addText("Statin Selection", {
x:0.5, y:1.5, w:12.3, h:1.4,
fontSize:52, color:C.white, bold:true, align:"center",
charSpacing:4,
});
// Subtitle
s.addText("Clinical Decision Flowchart for Outpatient Practice", {
x:0.5, y:3.1, w:12.3, h:0.7,
fontSize:22, color:C.sky, bold:false, align:"center",
});
// Divider line
s.addShape(pres.ShapeType.line, {
x:2, y:4.0, w:9.3, h:0,
line:{ color:C.teal, width:1.5 },
});
// Footer info
s.addText("Based on 2026 ACC/AHA Dyslipidemia Guidelines | Harrison's Principles of Internal Medicine 22E", {
x:0.5, y:4.2, w:12.3, h:0.4,
fontSize:11, color:C.grey, align:"center",
});
// Badge chips
const badges = ["Primary Prevention", "Secondary Prevention", "Statin Intensity", "Special Populations"];
badges.forEach((b, i) => {
const bx = 1.5 + i * 2.7;
s.addShape(pres.ShapeType.roundRect, {
x:bx, y:5.0, w:2.3, h:0.38,
fill:{ color:C.teal }, line:{ color:C.sky, width:1 },
rectRadius:0.12,
});
s.addText(b, {
x:bx, y:5.0, w:2.3, h:0.38,
fontSize:9.5, color:C.white, bold:true, align:"center", valign:"middle",
});
});
// Bottom strip
s.addShape(pres.ShapeType.rect, { x:0, y:7.2, w:13.3, h:0.3, fill:{ color:C.teal } });
s.addText("OPD / Outpatient Clinic | For Educational Use", {
x:0, y:7.2, w:13.3, h:0.3,
fontSize:8, color:C.white, align:"center", valign:"middle",
});
}
// ════════════════════════════════════════════════════════════════════════════
// SLIDE 2 – MAIN FLOWCHART (landscape decision tree)
// ════════════════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
// Background
s.addShape(pres.ShapeType.rect, { x:0, y:0, w:13.3, h:7.5, fill:{ color:C.offWhite } });
// Header bar
s.addShape(pres.ShapeType.rect, { x:0, y:0, w:13.3, h:0.55, fill:{ color:C.navy } });
s.addText("STATIN INITIATION DECISION FLOWCHART — OPD", {
x:0, y:0, w:13.3, h:0.55,
fontSize:13, color:C.white, bold:true, align:"center", valign:"middle", charSpacing:2,
});
// ── START node ──────────────────────────────────────────────────────────
const startX=5.65, startY=0.65, startW=2, startH=0.42;
s.addShape(pres.ShapeType.ellipse, {
x:startX, y:startY, w:startW, h:startH,
fill:{ color:C.navy }, line:{ color:C.sky, width:2 },
});
s.addText("NEW OPD PATIENT", {
x:startX, y:startY, w:startW, h:startH,
fontSize:8.5, color:C.white, bold:true, align:"center", valign:"middle",
});
// Arrow: start → Q1
arrowV(s, 6.65, 1.07, 1.32);
// ── Q1: Established ASCVD? ───────────────────────────────────────────────
const q1x=5.3, q1y=1.32, q1w=2.7, q1h=0.62;
diamond(s, { x:q1x, y:q1y, w:q1w, h:q1h, fill:C.amber, line:C.navy });
s.addText("Established ASCVD?", {
x:q1x, y:q1y, w:q1w, h:q1h,
fontSize:10, color:C.navy, bold:true, align:"center", valign:"middle",
});
// YES → left → Secondary Prevention box
arrowH(s, q1x, q1y + q1h/2, q1x - 0.05, C.green);
// Arrow going left
s.addShape(pres.ShapeType.line, {
x:2.15, y:q1y+q1h/2, w:q1x-2.15, h:0,
line:{ color:C.green, width:2, endArrowType:"arrow" },
});
arrowLabel(s, "YES", { x:2.6, y:q1y+q1h/2-0.22, fontSize:9, color:C.green });
// ── Box A: Secondary Prevention (HIGH intensity) ──────────────────────
const ax=0.2, ay=1.1, aw=1.95, ah=1.05;
s.addShape(pres.ShapeType.roundRect, {
x:ax, y:ay, w:aw, h:ah,
fill:{ color:C.red }, line:{ color:"8B0000", width:2 }, rectRadius:0.1,
shadow:{ type:"outer", color:"00000033", blur:4, offset:2, angle:45 },
});
s.addText([
{ text:"HIGH INTENSITY", options:{ breakLine:true, bold:true, fontSize:11, color:C.yellow } },
{ text:"STATIN", options:{ breakLine:true, bold:true, fontSize:11, color:C.white } },
{ text:"Secondary Prevention", options:{ breakLine:false, bold:false, fontSize:8, color:"FFCCCC" } },
], { x:ax, y:ay, w:aw, h:ah, align:"center", valign:"middle", margin:4 });
// Sub-box: ACS labels
s.addShape(pres.ShapeType.roundRect, {
x:0.18, y:ay+ah+0.08, w:1.98, h:0.85,
fill:{ color:"FDF2F2" }, line:{ color:C.red, width:1 }, rectRadius:0.06,
});
s.addText([
{ text:"• ACS / MI / Angina\n• Stroke / TIA\n• Revascularization\n• PAD / Aortic Aneurysm", options:{ fontSize:7.5, color:C.darkGrey } },
], { x:0.22, y:ay+ah+0.1, w:1.9, h:0.8, valign:"top", margin:3 });
// Target label
s.addShape(pres.ShapeType.roundRect, {
x:0.18, y:ay+ah+0.08+0.85+0.06, w:1.98, h:0.38,
fill:{ color:"2E8B57" }, line:{ color:"1A5C38", width:1 }, rectRadius:0.06,
});
s.addText("Target LDL-C < 70 mg/dL\n(≥50% reduction)", {
x:0.18, y:ay+ah+0.08+0.85+0.06, w:1.98, h:0.38,
fontSize:7.5, color:C.white, bold:true, align:"center", valign:"middle",
});
// NO → down → Q2
arrowV(s, 6.65, q1y+q1h, 2.25, C.navy);
arrowLabel(s, "NO", { x:6.7, y:1.78, fontSize:9, color:C.red });
// ── Q2: LDL ≥ 190? ───────────────────────────────────────────────────────
const q2x=5.3, q2y=2.25, q2w=2.7, q2h=0.62;
diamond(s, { x:q2x, y:q2y, w:q2w, h:q2h, fill:C.amber, line:C.navy });
s.addText("LDL-C ≥ 190 mg/dL?", {
x:q2x, y:q2y, w:q2w, h:q2h,
fontSize:10, color:C.navy, bold:true, align:"center", valign:"middle",
});
// YES → right → FH/severe box
s.addShape(pres.ShapeType.line, {
x:q2x+q2w, y:q2y+q2h/2, w:11.0-(q2x+q2w), h:0,
line:{ color:C.red, width:2, endArrowType:"arrow" },
});
arrowLabel(s, "YES", { x:9.5, y:q2y+q2h/2-0.22, fontSize:9, color:C.green });
// ── Box B: FH / Severe (right side) ──────────────────────────────────────
const bx=11.0, by=1.9, bw=2.1, bh=1.05;
s.addShape(pres.ShapeType.roundRect, {
x:bx, y:by, w:bw, h:bh,
fill:{ color:C.red }, line:{ color:"8B0000", width:2 }, rectRadius:0.1,
shadow:{ type:"outer", color:"00000033", blur:4, offset:2, angle:45 },
});
s.addText([
{ text:"HIGH INTENSITY\n", options:{ bold:true, fontSize:10.5, color:C.yellow, breakLine:true } },
{ text:"STATIN", options:{ bold:true, fontSize:10.5, color:C.white, breakLine:true } },
{ text:"FH / Severe Hyperlipid.", options:{ bold:false, fontSize:7.5, color:"FFCCCC" } },
], { x:bx, y:by, w:bw, h:bh, align:"center", valign:"middle", margin:4 });
s.addShape(pres.ShapeType.roundRect, {
x:bx, y:by+bh+0.08, w:bw, h:0.7,
fill:{ color:"FDF2F2" }, line:{ color:C.red, width:1 }, rectRadius:0.06,
});
s.addText("• FH (familial hypercholest.)\n• Age-independent\n• Add ezetimibe if needed\n• Consider PCSK9i if LDL ↑↑", {
x:bx+0.05, y:by+bh+0.1, w:bw-0.1, h:0.65,
fontSize:7.5, color:C.darkGrey, valign:"top", margin:3,
});
// NO → down → Q3
arrowV(s, 6.65, q2y+q2h, 3.15, C.navy);
arrowLabel(s, "NO", { x:6.7, y:2.7, fontSize:9, color:C.red });
// ── Q3: Diabetes (40-75y)? ────────────────────────────────────────────────
const q3x=5.3, q3y=3.15, q3w=2.7, q3h=0.62;
diamond(s, { x:q3x, y:q3y, w:q3w, h:q3h, fill:C.amber, line:C.navy });
s.addText("Diabetes Mellitus\n(Age 40-75 yrs)?", {
x:q3x, y:q3y, w:q3w, h:q3h,
fontSize:9.5, color:C.navy, bold:true, align:"center", valign:"middle",
});
// YES → left → DM statin box
s.addShape(pres.ShapeType.line, {
x:2.85, y:q3y+q3h/2, w:q3x-2.85, h:0,
line:{ color:C.green, width:2, endArrowType:"arrow" },
});
arrowLabel(s, "YES", { x:3.4, y:q3y+q3h/2-0.22, fontSize:9, color:C.green });
// ── Box C: Diabetes ───────────────────────────────────────────────────────
const cx=0.85, cy=2.9, cw=2.0, ch=1.0;
s.addShape(pres.ShapeType.roundRect, {
x:cx, y:cy, w:cw, h:ch,
fill:{ color:C.teal }, line:{ color:"104E8B", width:2 }, rectRadius:0.1,
shadow:{ type:"outer", color:"00000033", blur:4, offset:2, angle:45 },
});
s.addText([
{ text:"MODERATE\n", options:{ bold:true, fontSize:11, color:C.white, breakLine:true } },
{ text:"INTENSITY STATIN", options:{ bold:true, fontSize:10, color:C.yellow, breakLine:true } },
{ text:"Diabetes", options:{ bold:false, fontSize:8, color:"CCE5FF" } },
], { x:cx, y:cy, w:cw, h:ch, align:"center", valign:"middle", margin:4 });
s.addShape(pres.ShapeType.roundRect, {
x:cx, y:cy+ch+0.08, w:cw, h:0.62,
fill:{ color:"F0F8FF" }, line:{ color:C.teal, width:1 }, rectRadius:0.06,
});
s.addText("Escalate to HIGH if:\n10-yr risk ≥10%\nor other risk factors", {
x:cx+0.05, y:cy+ch+0.1, w:cw-0.1, h:0.58,
fontSize:7.5, color:C.darkGrey, valign:"top", margin:3,
});
// NO → down → Q4 (10-yr risk)
arrowV(s, 6.65, q3y+q3h, 4.05, C.navy);
arrowLabel(s, "NO", { x:6.7, y:3.62, fontSize:9, color:C.red });
// ── Q4: Calculate 10-year ASCVD risk ─────────────────────────────────────
const q4x=4.9, q4y=4.05, q4w=3.5, q4h=0.68;
s.addShape(pres.ShapeType.roundRect, {
x:q4x, y:q4y, w:q4w, h:q4h,
fill:{ color:C.navy }, line:{ color:C.sky, width:2 }, rectRadius:0.1,
});
s.addText("Calculate 10-Year ASCVD Risk\n(PREVENT-ASCVD Equation, Age 30-79)", {
x:q4x, y:q4y, w:q4w, h:q4h,
fontSize:9, color:C.white, bold:true, align:"center", valign:"middle",
});
// Three branches from Q4
// Low (<3%) → left
s.addShape(pres.ShapeType.line, {
x:1.5, y:q4y+q4h/2, w:q4x-1.5, h:0,
line:{ color:C.green, width:1.5, endArrowType:"arrow" },
});
s.addText("< 3%", {
x:2.0, y:q4y+q4h/2-0.22, w:1.0, h:0.22,
fontSize:8, color:C.green, bold:true, align:"center",
});
// Intermediate (3-<10%) → down
arrowV(s, 6.65, q4y+q4h, 5.22, C.amber);
s.addText("3–<10%\n(discuss)", {
x:6.68, y:q4y+q4h+0.04, w:0.9, h:0.3,
fontSize:7.5, color:C.amber, bold:true,
});
// High (≥10%) → right
s.addShape(pres.ShapeType.line, {
x:q4x+q4w, y:q4y+q4h/2, w:10.8-(q4x+q4w), h:0,
line:{ color:C.red, width:1.5, endArrowType:"arrow" },
});
s.addText("≥ 10%", {
x:9.1, y:q4y+q4h/2-0.22, w:1.0, h:0.22,
fontSize:8, color:C.red, bold:true, align:"center",
});
// ── Box D: LOW risk (<3%) ──────────────────────────────────────────────────
const dx=0.18, dy=3.88, dw=1.95, dh=0.78;
s.addShape(pres.ShapeType.roundRect, {
x:dx, y:dy, w:dw, h:dh,
fill:{ color:C.green }, line:{ color:"1A5C38", width:2 }, rectRadius:0.1,
});
s.addText("LIFESTYLE\nMODIFICATION\nOnly", {
x:dx, y:dy, w:dw, h:dh,
fontSize:9.5, color:C.white, bold:true, align:"center", valign:"middle",
});
s.addText("Diet, Exercise,\nNo Statin (typically)\nReassess in 5 yrs", {
x:dx, y:dy+dh+0.05, w:dw, h:0.48,
fontSize:7.5, color:C.darkGrey, align:"center", bold:false,
});
// ── Box E: INTERMEDIATE risk + risk enhancers ─────────────────────────────
const ex=5.15, ey=5.22, ew=3.0, eh=0.88;
s.addShape(pres.ShapeType.roundRect, {
x:ex, y:ey, w:ew, h:eh,
fill:{ color:C.amber }, line:{ color:"8B5E00", width:2 }, rectRadius:0.1,
shadow:{ type:"outer", color:"00000033", blur:4, offset:2, angle:45 },
});
s.addText("CHECK RISK ENHANCERS\n& Shared Decision Making", {
x:ex, y:ey, w:ew, h:eh,
fontSize:9.5, color:C.white, bold:true, align:"center", valign:"middle",
});
// Risk enhancer sub-box
s.addShape(pres.ShapeType.roundRect, {
x:4.4, y:ey+eh+0.06, w:4.5, h:0.92,
fill:{ color:C.yellow }, line:{ color:C.amber, width:1 }, rectRadius:0.08,
});
s.addText([
{ text:"Risk Enhancers (any 1 → favour statin): ", options:{ bold:true, fontSize:8, color:C.navy, breakLine:true } },
{ text:"Fam. hx premature ASCVD • LDL ≥160 • CKD • hs-CRP ≥2 • Lp(a) ≥50 • ApoB ≥130\nMetabolic syndrome • Autoimmune disease (RA/Psoriasis/HIV) • Premature menopause / Pre-eclampsia", options:{ bold:false, fontSize:7.5, color:C.darkGrey } },
], { x:4.45, y:ey+eh+0.08, w:4.4, h:0.86, valign:"top", margin:4 });
// CAC sub-box
s.addShape(pres.ShapeType.roundRect, {
x:4.4, y:ey+eh+0.06+0.92+0.05, w:4.5, h:0.55,
fill:{ color:"EAF4FF" }, line:{ color:C.sky, width:1 }, rectRadius:0.08,
});
s.addText([
{ text:"Coronary Artery Calcium (CAC): ", options:{ bold:true, fontSize:8, color:C.navy, breakLine:false } },
{ text:"CAC = 0 → withhold | CAC 1–99 → consider | CAC ≥100 → INITIATE", options:{ bold:false, fontSize:8, color:C.darkGrey } },
], { x:4.45, y:ey+eh+0.06+0.92+0.07, w:4.4, h:0.5, valign:"middle", margin:4 });
// ── Box F: HIGH risk (≥10%) → moderate/high ───────────────────────────────
const fx=10.8, fy=3.7, fw=2.35, fh=1.1;
s.addShape(pres.ShapeType.roundRect, {
x:fx, y:fy, w:fw, h:fh,
fill:{ color:C.red }, line:{ color:"8B0000", width:2 }, rectRadius:0.1,
shadow:{ type:"outer", color:"00000033", blur:4, offset:2, angle:45 },
});
s.addText([
{ text:"HIGH / MOD-HIGH\n", options:{ bold:true, fontSize:11, color:C.yellow, breakLine:true } },
{ text:"INTENSITY STATIN", options:{ bold:true, fontSize:10, color:C.white, breakLine:true } },
{ text:"Primary Prevention", options:{ bold:false, fontSize:8, color:"FFCCCC" } },
], { x:fx, y:fy, w:fw, h:fh, align:"center", valign:"middle", margin:4 });
s.addShape(pres.ShapeType.roundRect, {
x:fx, y:fy+fh+0.06, w:fw, h:0.55,
fill:{ color:"FDF2F2" }, line:{ color:C.red, width:1 }, rectRadius:0.06,
});
s.addText("Target ≥50% LDL-C reduction\nTarget LDL-C <100 mg/dL (primary)", {
x:fx+0.05, y:fy+fh+0.08, w:fw-0.1, h:0.5,
fontSize:7.5, color:C.darkGrey, valign:"top", margin:3, bold:false,
});
// Footer bar
s.addShape(pres.ShapeType.rect, { x:0, y:7.22, w:13.3, h:0.28, fill:{ color:C.navy } });
s.addText("2026 ACC/AHA Dyslipidemia Guideline | Harrison's 22E | For OPD Use", {
x:0, y:7.22, w:13.3, h:0.28,
fontSize:7.5, color:C.grey, align:"center", valign:"middle",
});
}
// ════════════════════════════════════════════════════════════════════════════
// SLIDE 3 – STATIN INTENSITY TABLE
// ════════════════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
s.addShape(pres.ShapeType.rect, { x:0, y:0, w:13.3, h:7.5, fill:{ color:C.offWhite } });
s.addShape(pres.ShapeType.rect, { x:0, y:0, w:13.3, h:0.55, fill:{ color:C.navy } });
s.addText("STATIN INTENSITY GUIDE", {
x:0, y:0, w:13.3, h:0.55,
fontSize:15, color:C.white, bold:true, align:"center", valign:"middle", charSpacing:3,
});
// Column headers
const colHeaders = ["Intensity", "LDL-C Reduction", "Statin", "Dose (mg/day)", "Use When"];
const colX = [0.2, 1.8, 3.6, 6.8, 8.9];
const colW = [1.5, 1.7, 3.0, 2.0, 4.15];
// Header row background
s.addShape(pres.ShapeType.rect, { x:0.2, y:0.65, w:12.9, h:0.4, fill:{ color:C.navy } });
colHeaders.forEach((h, i) => {
s.addText(h, {
x:colX[i], y:0.65, w:colW[i], h:0.4,
fontSize:10, color:C.white, bold:true, align:"center", valign:"middle",
});
});
// Data rows
const rows = [
{
intensity: "HIGH",
color: C.red,
textColor: C.white,
reduction: "≥ 50%",
drugs: [
{ drug:"Atorvastatin", doses:"40 mg, 80 mg" },
{ drug:"Rosuvastatin", doses:"20 mg, 40 mg" },
],
uses:"• Established ASCVD (2° prevention)\n• LDL-C ≥190 mg/dL (FH)\n• DM with 10-yr risk ≥10%\n• Primary prevention, 10-yr risk ≥10%",
},
{
intensity: "MODERATE",
color: C.amber,
textColor: C.white,
reduction: "30–49%",
drugs: [
{ drug:"Atorvastatin", doses:"10 mg, 20 mg" },
{ drug:"Rosuvastatin", doses:"5 mg, 10 mg" },
{ drug:"Simvastatin", doses:"20 mg, 40 mg" },
{ drug:"Pravastatin", doses:"40 mg, 80 mg" },
{ drug:"Lovastatin", doses:"40 mg" },
{ drug:"Fluvastatin", doses:"40 mg BD / 80 mg XL" },
{ drug:"Pitavastatin", doses:"2 mg, 4 mg" },
],
uses:"• DM age 40-75 (first choice)\n• Primary prevention, 3–<10%\n• Elderly (≥75 y) if treatment indicated\n• Statin-intolerant (try lower dose)",
},
{
intensity: "LOW",
color: C.green,
textColor: C.white,
reduction: "< 30%",
drugs: [
{ drug:"Simvastatin", doses:"10 mg" },
{ drug:"Pravastatin", doses:"10 mg, 20 mg" },
{ drug:"Lovastatin", doses:"20 mg" },
{ drug:"Fluvastatin", doses:"20–40 mg" },
{ drug:"Pitavastatin", doses:"1 mg" },
],
uses:"• Statin intolerance (titrate up)\n• CKD (adjust as needed)\n• Elderly frail patients\n• Drug interaction concern",
},
];
let rowY = 1.1;
rows.forEach((row, ri) => {
const rowH = ri === 1 ? 2.0 : 1.4;
const altFill = ri % 2 === 0 ? "F9FBFD" : "EDF3FA";
// Row background
s.addShape(pres.ShapeType.rect, { x:0.2, y:rowY, w:12.9, h:rowH, fill:{ color:altFill }, line:{ color:"DDDDDD", width:0.5 } });
// Intensity badge
s.addShape(pres.ShapeType.roundRect, {
x:0.25, y:rowY+0.1, w:1.4, h:rowH-0.2,
fill:{ color:row.color }, line:{ color:row.color, width:1 }, rectRadius:0.1,
});
s.addText(row.intensity, {
x:0.25, y:rowY+0.1, w:1.4, h:rowH-0.2,
fontSize:11, color:row.textColor, bold:true, align:"center", valign:"middle",
});
// Reduction
s.addText(row.reduction, {
x:1.85, y:rowY, w:1.65, h:rowH,
fontSize:12, color:row.color, bold:true, align:"center", valign:"middle",
});
// Drugs list
const drugText = row.drugs.map(d => `${d.drug} ${d.doses}`).join("\n");
s.addText(drugText, {
x:3.65, y:rowY+0.08, w:2.9, h:rowH-0.16,
fontSize:8.5, color:C.darkGrey, align:"left", valign:"top", margin:4, wrap:true,
});
// Doses merged into drug text above
// Use When
s.addText(row.uses, {
x:8.95, y:rowY+0.08, w:4.1, h:rowH-0.16,
fontSize:8.5, color:C.darkGrey, align:"left", valign:"top", margin:4, wrap:true,
});
rowY += rowH + 0.06;
});
// Add-on therapy note
s.addShape(pres.ShapeType.roundRect, {
x:0.2, y:6.2, w:12.9, h:0.7,
fill:{ color:"EAF4FF" }, line:{ color:C.sky, width:1.5 }, rectRadius:0.1,
});
s.addText([
{ text:"Add-On Therapy if LDL-C target not met on maximum tolerated statin: ", options:{ bold:true, fontSize:9.5, color:C.navy } },
{ text:"Step 1 → Ezetimibe 10 mg OD | Step 2 → PCSK9 Inhibitor (evolocumab / alirocumab) — especially for very high-risk ASCVD or FH", options:{ bold:false, fontSize:9.5, color:C.darkGrey } },
], { x:0.3, y:6.22, w:12.7, h:0.65, valign:"middle", margin:5 });
// Footer
s.addShape(pres.ShapeType.rect, { x:0, y:7.22, w:13.3, h:0.28, fill:{ color:C.navy } });
s.addText("2026 ACC/AHA Dyslipidemia Guideline | Harrison's 22E | For OPD Use", {
x:0, y:7.22, w:13.3, h:0.28, fontSize:7.5, color:C.grey, align:"center", valign:"middle",
});
}
// ════════════════════════════════════════════════════════════════════════════
// SLIDE 4 – SPECIAL POPULATIONS + MONITORING
// ════════════════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
s.addShape(pres.ShapeType.rect, { x:0, y:0, w:13.3, h:7.5, fill:{ color:C.offWhite } });
s.addShape(pres.ShapeType.rect, { x:0, y:0, w:13.3, h:0.55, fill:{ color:C.navy } });
s.addText("SPECIAL POPULATIONS & MONITORING", {
x:0, y:0, w:13.3, h:0.55,
fontSize:14, color:C.white, bold:true, align:"center", valign:"middle", charSpacing:2,
});
// Left panel: Special Populations
s.addShape(pres.ShapeType.roundRect, {
x:0.2, y:0.65, w:6.1, h:6.5,
fill:{ color:C.white }, line:{ color:C.sky, width:1.5 }, rectRadius:0.1,
});
s.addShape(pres.ShapeType.rect, { x:0.2, y:0.65, w:6.1, h:0.42, fill:{ color:C.teal } });
// Round top corners of header
s.addText("SPECIAL POPULATIONS", {
x:0.2, y:0.65, w:6.1, h:0.42,
fontSize:11, color:C.white, bold:true, align:"center", valign:"middle",
});
const populations = [
{ title:"Pregnancy", color:C.red, text:"ABSOLUTELY CONTRAINDICATED\nStop before conception or as soon as detected\n(teratogenic — Category X)" },
{ title:"CKD (non-dialysis)", color:C.teal, text:"Moderate intensity preferred\nRosuvastatin — caution at high doses (renal excretion)\nPravastatin / Fluvastatin — safer options" },
{ title:"Elderly (> 75 yrs)", color:C.amber, text:"Individualize: clear benefit in 2° prevention\nPrimary prevention — shared decision making\nStart low, titrate slowly; monitor CK / LFT" },
{ title:"HIV Patients", color:C.teal, text:"Moderate-to-high intensity statin recommended\nAvoid simvastatin/lovastatin with PIs (high interaction)\nPravastatin or rosuvastatin preferred" },
{ title:"Liver Disease", color:C.red, text:"Active liver disease = CONTRAINDICATED\nMild-moderate hepatic impairment: use with caution\nMonitor LFTs; avoid high-dose statins" },
{ title:"Drug Interactions", color:C.amber, text:"Macrolides, azole antifungals, cyclosporine, gemfibrozil\n→ Avoid co-administration (↑ myopathy risk)\nPravastatin / rosuvastatin lowest interaction" },
];
let py = 1.18;
populations.forEach(p => {
s.addShape(pres.ShapeType.roundRect, {
x:0.35, y:py, w:5.8, h:0.72,
fill:{ color:C.offWhite }, line:{ color:p.color, width:1.5 }, rectRadius:0.07,
});
s.addShape(pres.ShapeType.roundRect, {
x:0.35, y:py, w:1.35, h:0.72,
fill:{ color:p.color }, line:{ color:p.color, width:0 }, rectRadius:0.07,
});
s.addText(p.title, {
x:0.35, y:py, w:1.35, h:0.72,
fontSize:8, color:C.white, bold:true, align:"center", valign:"middle",
});
s.addText(p.text, {
x:1.75, y:py+0.04, w:4.3, h:0.65,
fontSize:7.5, color:C.darkGrey, align:"left", valign:"top", margin:3, wrap:true,
});
py += 0.82;
});
// Right panel: Monitoring
s.addShape(pres.ShapeType.roundRect, {
x:6.65, y:0.65, w:6.45, h:6.5,
fill:{ color:C.white }, line:{ color:C.teal, width:1.5 }, rectRadius:0.1,
});
s.addShape(pres.ShapeType.rect, { x:6.65, y:0.65, w:6.45, h:0.42, fill:{ color:C.teal } });
s.addText("MONITORING & FOLLOW-UP", {
x:6.65, y:0.65, w:6.45, h:0.42,
fontSize:11, color:C.white, bold:true, align:"center", valign:"middle",
});
const monitorItems = [
{
icon:"🔬", title:"Baseline Workup (BEFORE starting)",
steps:["Fasting lipid panel (LDL-C, HDL, TG)", "ALT / AST (baseline LFT)", "Fasting glucose or HbA1c", "CK (if high myopathy risk: elderly, renal, multiple drugs)", "Calculate PREVENT-ASCVD risk score (primary prevention)"],
color:C.navy,
},
{
icon:"📅", title:"Follow-Up Lipid Panel",
steps:["4–12 weeks after initiating or changing dose", "Goal: ≥50% reduction (high-intensity) / 30–49% (moderate)", "Annual check thereafter if on target"],
color:C.teal,
},
{
icon:"💊", title:"Statin Side Effects — When to Act",
steps:["Myalgia (3-5%): Check CK. Mild → continue; CK >10× ULN → STOP", "Transaminase rise: usually mild/transient; stop if >3× ULN persistent", "New DM: counsel patient — CV benefit outweighs risk", "Routine CK monitoring NOT needed without symptoms"],
color:C.amber,
},
{
icon:"🔄", title:"If Target NOT Met",
steps:["Maximise statin dose first", "Add Ezetimibe 10 mg OD (+ ~18% LDL-C reduction)", "Add PCSK9i (evolocumab / alirocumab) for very high risk / FH"],
color:C.red,
},
];
let my = 1.18;
monitorItems.forEach(m => {
const itemH = m.steps.length * 0.22 + 0.38;
s.addShape(pres.ShapeType.roundRect, {
x:6.8, y:my, w:6.15, h:itemH,
fill:{ color:C.offWhite }, line:{ color:m.color, width:1.5 }, rectRadius:0.07,
});
s.addText(m.title, {
x:6.85, y:my+0.04, w:6.05, h:0.26,
fontSize:9, color:m.color, bold:true, align:"left", valign:"middle", margin:2,
});
m.steps.forEach((step, si) => {
s.addText("• " + step, {
x:6.9, y:my+0.3+si*0.21, w:6.0, h:0.21,
fontSize:7.8, color:C.darkGrey, align:"left", valign:"middle", margin:2, wrap:true,
});
});
my += itemH + 0.1;
});
// Footer
s.addShape(pres.ShapeType.rect, { x:0, y:7.22, w:13.3, h:0.28, fill:{ color:C.navy } });
s.addText("2026 ACC/AHA Dyslipidemia Guideline | Harrison's 22E | For OPD Use", {
x:0, y:7.22, w:13.3, h:0.28, fontSize:7.5, color:C.grey, align:"center", valign:"middle",
});
}
// ════════════════════════════════════════════════════════════════════════════
// WRITE FILE
// ════════════════════════════════════════════════════════════════════════════
pres.writeFile({ fileName: "/home/daytona/workspace/statin-flowchart/Statin_Decision_Flowchart.pptx" })
.then(() => console.log("SUCCESS: File written."))
.catch(e => { console.error("ERROR:", e); process.exit(1); });
cd /home/daytona/workspace/statin-flowchart && node build.js 2>&1
ls -lh /home/daytona/workspace/statin-flowchart/
Statin Decision Flowchart
Presentation · PPTX
Generate a quick-reference pocket card PDF from this flowchart
~/statin-flowchart/pocket_card.py
"""
Statin Selection Quick-Reference Pocket Card
Size: 3.5 x 8.5 inches (tall card, 2 pages = front/back)
"""
from reportlab.lib.pagesizes import inch
from reportlab.lib import colors
from reportlab.lib.colors import HexColor
from reportlab.platypus import SimpleDocTemplate
from reportlab.pdfgen import canvas
from reportlab.lib.utils import simpleSplit
import math
# ── Page size ─────────────────────────────────────────────────────────────────
W, H = 3.5 * inch, 8.5 * inch
# ── Palette ───────────────────────────────────────────────────────────────────
NAVY = HexColor("#0B2545")
TEAL = HexColor("#1B6CA8")
SKY = HexColor("#4DA6D9")
GREEN = HexColor("#2E8B57")
AMBER = HexColor("#D4870A")
RED = HexColor("#C0392B")
YELLOW = HexColor("#FFF3CD")
OFFWHITE= HexColor("#F4F8FC")
WHITE = colors.white
DGREY = HexColor("#2C3E50")
LGREY = HexColor("#ECF0F1")
MGREY = HexColor("#7F8C8D")
OUTPUT = "/home/daytona/workspace/statin-flowchart/Statin_Pocket_Card.pdf"
# ── Canvas helpers ─────────────────────────────────────────────────────────────
def rounded_rect(c, x, y, w, h, r=4, fill=None, stroke=None, stroke_w=0.75):
c.saveState()
if fill:
c.setFillColor(fill)
if stroke:
c.setStrokeColor(stroke)
c.setLineWidth(stroke_w)
else:
c.setLineWidth(0)
c.roundRect(x, y, w, h, r, fill=1 if fill else 0, stroke=1 if stroke else 0)
c.restoreState()
def rect(c, x, y, w, h, fill=None, stroke=None, stroke_w=0.5):
c.saveState()
if fill: c.setFillColor(fill)
if stroke: c.setStrokeColor(stroke); c.setLineWidth(stroke_w)
c.rect(x, y, w, h, fill=1 if fill else 0, stroke=1 if stroke else 0)
c.restoreState()
def text(c, txt, x, y, size=7, color=DGREY, bold=False, align="left", max_w=None):
c.saveState()
c.setFillColor(color)
face = "Helvetica-Bold" if bold else "Helvetica"
c.setFont(face, size)
if align == "center" and max_w:
c.drawCentredString(x + max_w / 2, y, txt)
elif align == "right" and max_w:
c.drawRightString(x + max_w, y, txt)
else:
c.drawString(x, y, txt)
c.restoreState()
def multiline(c, txt, x, y, size=6.5, color=DGREY, bold=False, max_w=None, line_h=9):
"""Wrap and draw multi-line text. Returns final y."""
face = "Helvetica-Bold" if bold else "Helvetica"
mw = max_w or (W - x - 6)
lines = simpleSplit(txt, face, size, mw)
cy = y
c.saveState()
c.setFillColor(color)
c.setFont(face, size)
for line in lines:
c.drawString(x, cy, line)
cy -= line_h
c.restoreState()
return cy
def section_header(c, title, y, bg=NAVY, text_color=WHITE, h=13):
rect(c, 4, y, W-8, h, fill=bg)
text(c, title, 4, y + 3.5, size=7, color=text_color, bold=True, align="center", max_w=W-8)
return y - h - 2
def arrow_down(c, x, y, length=8):
c.saveState()
c.setStrokeColor(NAVY)
c.setLineWidth(0.8)
c.line(x, y, x, y - length)
# arrowhead
c.setFillColor(NAVY)
p = c.beginPath()
p.moveTo(x - 3, y - length + 3)
p.lineTo(x, y - length)
p.lineTo(x + 3, y - length + 3)
p.close()
c.drawPath(p, fill=1, stroke=0)
c.restoreState()
def diamond_node(c, cx, cy, hw, hh, fill=AMBER, stroke=NAVY, label="", label_size=6.5):
"""Draw diamond centred at (cx, cy)."""
c.saveState()
c.setFillColor(fill)
c.setStrokeColor(stroke)
c.setLineWidth(0.8)
p = c.beginPath()
p.moveTo(cx, cy + hh) # top
p.lineTo(cx + hw, cy) # right
p.lineTo(cx, cy - hh) # bottom
p.lineTo(cx - hw, cy) # left
p.close()
c.drawPath(p, fill=1, stroke=1)
c.restoreState()
# label
lines = simpleSplit(label, "Helvetica-Bold", label_size, hw * 1.8)
total = len(lines) * (label_size + 1)
sy = cy + total / 2 - label_size + 1
c.saveState()
c.setFillColor(NAVY)
c.setFont("Helvetica-Bold", label_size)
for ln in lines:
c.drawCentredString(cx, sy, ln)
sy -= label_size + 1
c.restoreState()
def outcome_box(c, x, y, w, h, bg, title, sub, r=5):
rounded_rect(c, x, y, w, h, r=r, fill=bg)
# title
c.saveState()
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 6.5)
c.drawCentredString(x + w/2, y + h - 10, title)
c.setFont("Helvetica", 5.8)
c.setFillColor(HexColor("#EEEEEE"))
lines = simpleSplit(sub, "Helvetica", 5.8, w - 6)
ty = y + h - 20
for ln in lines:
c.drawCentredString(x + w/2, ty, ln)
ty -= 8
c.restoreState()
# ══════════════════════════════════════════════════════════════════════════════
# PAGE 1 — FRONT (Decision Flowchart)
# ══════════════════════════════════════════════════════════════════════════════
def draw_front(c):
margin = 6
cw = W - 2 * margin # content width
# Background
rect(c, 0, 0, W, H, fill=OFFWHITE)
# Header
rect(c, 0, H - 38, W, 38, fill=NAVY)
c.saveState()
c.setFillColor(SKY)
c.setFont("Helvetica-Bold", 9.5)
c.drawCentredString(W/2, H - 15, "STATIN SELECTION")
c.setFont("Helvetica", 7)
c.setFillColor(WHITE)
c.drawCentredString(W/2, H - 26, "Quick-Reference Pocket Card")
c.setFont("Helvetica", 5.5)
c.setFillColor(MGREY)
c.drawCentredString(W/2, H - 35, "2026 ACC/AHA Guideline | Harrison's 22E")
c.restoreState()
# Accent bar
rect(c, 0, H - 40, W, 2, fill=TEAL)
cy = H - 50 # current y (top of content)
# START pill
rounded_rect(c, margin+20, cy-14, cw-40, 14, r=7, fill=NAVY)
text(c, "NEW OPD PATIENT", margin+20, cy-10, size=6.5, color=WHITE, bold=True,
align="center", max_w=cw-40)
cy -= 16
# Arrow down
arrow_down(c, W/2, cy, 8)
cy -= 11
# ── Decision 1: Established ASCVD? ──────────────────────────────────────
diam_cx = W/2
diam_hw = 58
diam_hh = 17
dcy = cy - diam_hh
diamond_node(c, diam_cx, dcy, diam_hw, diam_hh, fill=HexColor("#FEF3C7"), stroke=AMBER,
label="Established ASCVD?", label_size=6.5)
# YES → left
c.saveState()
c.setStrokeColor(GREEN); c.setLineWidth(0.8)
c.line(diam_cx - diam_hw, dcy, margin, dcy)
# arrowhead left
c.setFillColor(GREEN)
p = c.beginPath()
p.moveTo(margin+4, dcy+3); p.lineTo(margin, dcy); p.lineTo(margin+4, dcy-3); p.close()
c.drawPath(p, fill=1, stroke=0)
c.restoreState()
text(c, "YES", margin+1, dcy+2.5, size=5.5, color=GREEN, bold=True)
# NO → down
c.saveState(); c.setStrokeColor(NAVY); c.setLineWidth(0.8)
c.line(diam_cx, dcy-diam_hh, diam_cx, dcy-diam_hh-7)
c.restoreState()
text(c, "NO", W/2+1, dcy-diam_hh-2, size=5.5, color=RED, bold=True)
# LEFT BOX: 2° Prevention
bh = 30
outcome_box(c, margin, dcy-bh+8, 45, bh, bg=RED,
title="HIGH INTENSITY",
sub="2° Prevention\nTarget LDL<70")
cy = dcy - diam_hh - 10
# ── Decision 2: LDL-C ≥190? ─────────────────────────────────────────────
dcy2 = cy - diam_hh
diamond_node(c, diam_cx, dcy2, diam_hw, diam_hh, fill=HexColor("#FEF3C7"), stroke=AMBER,
label="LDL-C ≥190 mg/dL?", label_size=6.5)
# YES → right
c.saveState()
c.setStrokeColor(RED); c.setLineWidth(0.8)
c.line(diam_cx + diam_hw, dcy2, W-margin, dcy2)
c.setFillColor(RED)
p = c.beginPath()
p.moveTo(W-margin-4, dcy2+3); p.lineTo(W-margin, dcy2); p.lineTo(W-margin-4, dcy2-3); p.close()
c.drawPath(p, fill=1, stroke=0)
c.restoreState()
text(c, "YES", W-margin-20, dcy2+2.5, size=5.5, color=GREEN, bold=True)
# NO → down
c.saveState(); c.setStrokeColor(NAVY); c.setLineWidth(0.8)
c.line(diam_cx, dcy2-diam_hh, diam_cx, dcy2-diam_hh-7)
c.restoreState()
text(c, "NO", W/2+1, dcy2-diam_hh-2, size=5.5, color=RED, bold=True)
# RIGHT BOX: FH
outcome_box(c, W-margin-45, dcy2-22+8, 45, 30, bg=RED,
title="HIGH INTENSITY",
sub="FH/Severe\nAdd ezetimibe")
cy = dcy2 - diam_hh - 10
# ── Decision 3: Diabetes 40-75y? ────────────────────────────────────────
dcy3 = cy - diam_hh
diamond_node(c, diam_cx, dcy3, diam_hw, diam_hh, fill=HexColor("#FEF3C7"), stroke=AMBER,
label="DM age 40-75 yrs?", label_size=6.5)
# YES → left
c.saveState()
c.setStrokeColor(GREEN); c.setLineWidth(0.8)
c.line(diam_cx - diam_hw, dcy3, margin, dcy3)
c.setFillColor(GREEN)
p = c.beginPath()
p.moveTo(margin+4, dcy3+3); p.lineTo(margin, dcy3); p.lineTo(margin+4, dcy3-3); p.close()
c.drawPath(p, fill=1, stroke=0)
c.restoreState()
text(c, "YES", margin+1, dcy3+2.5, size=5.5, color=GREEN, bold=True)
# NO → down
c.saveState(); c.setStrokeColor(NAVY); c.setLineWidth(0.8)
c.line(diam_cx, dcy3-diam_hh, diam_cx, dcy3-diam_hh-7)
c.restoreState()
text(c, "NO", W/2+1, dcy3-diam_hh-2, size=5.5, color=RED, bold=True)
# LEFT BOX: DM
outcome_box(c, margin, dcy3-22+8, 45, 30, bg=TEAL,
title="MOD INTENSITY",
sub="Diabetes\n↑ if risk ≥10%")
cy = dcy3 - diam_hh - 10
# ── PREVENT Risk score box ───────────────────────────────────────────────
rounded_rect(c, margin, cy-22, cw, 22, r=5, fill=NAVY)
text(c, "Calculate PREVENT-ASCVD 10-yr Risk", margin, cy-11,
size=6.5, color=WHITE, bold=True, align="center", max_w=cw)
text(c, "(Age 30-79, no known ASCVD, LDL 70-189)", margin, cy-20,
size=5.8, color=SKY, bold=False, align="center", max_w=cw)
arrow_down(c, W/2, cy-23, 8)
cy -= 34
# ── Three risk branches ──────────────────────────────────────────────────
branch_y = cy - 2
bw = (cw - 6) / 3
bh2 = 40
# Draw horizontal line
c.saveState(); c.setStrokeColor(NAVY); c.setLineWidth(0.7)
c.line(margin + bw/2, branch_y, W - margin - bw/2, branch_y)
# Left branch arrow
c.line(margin + bw/2, branch_y, margin + bw/2, branch_y - 8)
# Mid branch arrow (already had vertical)
c.line(W/2, branch_y, W/2, branch_y - 8)
# Right branch arrow
c.line(W - margin - bw/2, branch_y, W - margin - bw/2, branch_y - 8)
c.restoreState()
# LEFT: <3%
rounded_rect(c, margin, branch_y - 8 - bh2, bw, bh2, r=4, fill=GREEN)
c.saveState()
c.setFillColor(WHITE); c.setFont("Helvetica-Bold", 6.5)
c.drawCentredString(margin + bw/2, branch_y - 18, "< 3%")
c.setFont("Helvetica", 5.5); c.setFillColor(HexColor("#CCFFCC"))
c.drawCentredString(margin + bw/2, branch_y - 27, "Lifestyle")
c.drawCentredString(margin + bw/2, branch_y - 35, "Only")
c.drawCentredString(margin + bw/2, branch_y - 43, "No statin")
c.restoreState()
# MID: 3-<10%
rounded_rect(c, margin + bw + 3, branch_y - 8 - bh2, bw, bh2, r=4, fill=AMBER)
c.saveState()
c.setFillColor(WHITE); c.setFont("Helvetica-Bold", 6.5)
c.drawCentredString(margin + bw + 3 + bw/2, branch_y - 18, "3-<10%")
c.setFont("Helvetica", 5.5); c.setFillColor(YELLOW)
c.drawCentredString(margin + bw + 3 + bw/2, branch_y - 27, "Shared")
c.drawCentredString(margin + bw + 3 + bw/2, branch_y - 35, "Decision")
c.drawCentredString(margin + bw + 3 + bw/2, branch_y - 43, "MOD statin")
c.restoreState()
# RIGHT: ≥10%
rounded_rect(c, W - margin - bw, branch_y - 8 - bh2, bw, bh2, r=4, fill=RED)
c.saveState()
c.setFillColor(WHITE); c.setFont("Helvetica-Bold", 6.5)
c.drawCentredString(W - margin - bw/2, branch_y - 18, "≥ 10%")
c.setFont("Helvetica", 5.5); c.setFillColor(HexColor("#FFCCCC"))
c.drawCentredString(W - margin - bw/2, branch_y - 27, "HIGH")
c.drawCentredString(W - margin - bw/2, branch_y - 35, "intensity")
c.drawCentredString(W - margin - bw/2, branch_y - 43, "statin")
c.restoreState()
cy = branch_y - 8 - bh2 - 5
# ── Risk Enhancers strip ────────────────────────────────────────────────
eh = 42
rounded_rect(c, margin, cy-eh, cw, eh, r=4, fill=HexColor("#FFF8E1"), stroke=AMBER, stroke_w=0.8)
text(c, "RISK ENHANCERS (push 3-<10% toward statin):", margin+3, cy-9,
size=6, color=AMBER, bold=True)
enhancers = [
"Fam hx premature ASCVD • LDL ≥160 • hs-CRP ≥2",
"Lp(a) ≥50 • ApoB ≥130 • CKD • Metabolic Synd.",
"RA / Psoriasis / HIV • Pre-eclampsia / Early menopause",
"CAC=0 → withhold | CAC ≥100 → initiate statin",
]
ey2 = cy - 18
for line in enhancers:
text(c, line, margin+3, ey2, size=5.8, color=DGREY)
ey2 -= 8.5
cy -= eh + 4
# ── Footer ──────────────────────────────────────────────────────────────
rect(c, 0, 0, W, 16, fill=NAVY)
text(c, "See reverse for: Statin Doses | Monitoring | Special Populations",
margin, 10, size=5.5, color=SKY, bold=False, align="center", max_w=cw)
text(c, "ACC/AHA 2026 | Harrison's 22E",
margin, 3, size=5, color=MGREY, align="center", max_w=cw)
# ══════════════════════════════════════════════════════════════════════════════
# PAGE 2 — BACK (Statin doses + Monitoring + Special populations)
# ══════════════════════════════════════════════════════════════════════════════
def draw_back(c):
margin = 6
cw = W - 2 * margin
rect(c, 0, 0, W, H, fill=OFFWHITE)
# Header
rect(c, 0, H - 28, W, 28, fill=TEAL)
text(c, "STATIN DOSES & MONITORING", margin, H-11, size=8,
color=WHITE, bold=True, align="center", max_w=cw)
text(c, "Pocket Reference — Back", margin, H-22, size=6,
color=HexColor("#CCE5FF"), align="center", max_w=cw)
rect(c, 0, H-30, W, 2, fill=SKY)
cy = H - 38
# ── Intensity table header ───────────────────────────────────────────────
rect(c, margin, cy-11, cw, 11, fill=NAVY)
cols = [6, 52, 110, 165] # x positions
hdrs = ["LEVEL", "DRUG", "DOSE (mg)", "LDL↓"]
for i, h in enumerate(hdrs):
text(c, h, cols[i]+margin, cy-8, size=5.8, color=WHITE, bold=True)
cy -= 11
# Table data
rows = [
# (level, color, drug, dose, reduction)
("HIGH", RED, "Atorvastatin", "40, 80", "≥50%"),
("HIGH", RED, "Rosuvastatin", "20, 40", "≥50%"),
("MOD", AMBER, "Atorvastatin", "10, 20", "30-49%"),
("MOD", AMBER, "Rosuvastatin", "5, 10", "30-49%"),
("MOD", AMBER, "Simvastatin", "20, 40", "30-49%"),
("MOD", AMBER, "Pravastatin", "40, 80", "30-49%"),
("MOD", AMBER, "Pitavastatin", "2, 4", "30-49%"),
("LOW", GREEN, "Simvastatin", "10", "<30%"),
("LOW", GREEN, "Pravastatin", "10, 20", "<30%"),
("LOW", GREEN, "Fluvastatin", "20-40", "<30%"),
]
for i, (lvl, col, drug, dose, red) in enumerate(rows):
row_bg = WHITE if i % 2 == 0 else HexColor("#EDF3FA")
rect(c, margin, cy-10, cw, 10, fill=row_bg)
# Level badge
rounded_rect(c, margin+1, cy-9, 46, 8.5, r=2, fill=col)
c.saveState()
c.setFillColor(WHITE); c.setFont("Helvetica-Bold", 5.5)
c.drawCentredString(margin+24, cy-3.5, lvl)
c.restoreState()
text(c, drug, cols[1]+margin, cy-7.5, size=6, color=DGREY)
text(c, dose, cols[2]+margin, cy-7.5, size=6, color=DGREY)
text(c, red, cols[3]+margin, cy-7.5, size=6, color=col, bold=True)
cy -= 10
# Divider
rect(c, margin, cy-1, cw, 1, fill=TEAL)
cy -= 4
# ── Add-on therapy ────────────────────────────────────────────────────────
rounded_rect(c, margin, cy-22, cw, 22, r=4, fill=HexColor("#EAF4FF"), stroke=SKY, stroke_w=0.8)
text(c, "ADD-ON THERAPY (if LDL target not met):", margin+3, cy-9,
size=6.5, color=TEAL, bold=True)
text(c, "Step 1: Ezetimibe 10 mg OD (+18% LDL reduction)", margin+3, cy-17.5,
size=6, color=DGREY)
text(c, "Step 2: PCSK9 inhibitor (evolocumab / alirocumab)", margin+3, cy-25,
size=6, color=DGREY)
cy -= 26
# ── Monitoring ────────────────────────────────────────────────────────────
rect(c, margin, cy-11, cw, 11, fill=NAVY)
text(c, "MONITORING", margin, cy-8, size=7, color=WHITE, bold=True,
align="center", max_w=cw)
cy -= 11
monitor_items = [
("Before starting:", "Lipid panel, ALT/AST, Fasting glucose, CK (if high risk)", TEAL),
("4-12 wks after:", "Repeat fasting lipid panel; check % LDL-C reduction", TEAL),
("Myalgia (3-5%):", "Check CK. CK >10× ULN → STOP statin", AMBER),
("Transaminase ↑:", "Usually mild/transient. Stop if >3× ULN persistent", AMBER),
("New DM risk:", "Counsel: CV benefit far outweighs DM risk", GREEN),
("Routine CK:", "NOT needed unless symptoms arise", GREEN),
]
for (label_t, detail, col) in monitor_items:
bg = WHITE
rect(c, margin, cy-16, cw, 16, fill=bg, stroke=LGREY, stroke_w=0.3)
rounded_rect(c, margin+1, cy-15, 52, 13, r=2, fill=col)
c.saveState()
c.setFillColor(WHITE); c.setFont("Helvetica-Bold", 5.5)
lines = simpleSplit(label_t, "Helvetica-Bold", 5.5, 50)
ly = cy - 8
for l in lines:
c.drawCentredString(margin+27, ly, l)
ly -= 7
c.restoreState()
multiline(c, detail, margin+56, cy-8, size=5.8, color=DGREY,
max_w=cw-50, line_h=7.5)
cy -= 16
# ── Special Populations ─────────────────────────────────────────────────
rect(c, margin, cy-11, cw, 11, fill=NAVY)
text(c, "SPECIAL POPULATIONS", margin, cy-8, size=7, color=WHITE, bold=True,
align="center", max_w=cw)
cy -= 11
sp_items = [
("PREGNANCY", "CONTRAINDICATED — stop immediately", RED),
("CKD", "Moderate intensity preferred; pravastatin/fluvastatin safer", TEAL),
("ELDERLY >75y", "2° prevention: continue; 1° prevention: individualize", AMBER),
("HIV", "Avoid simvastatin with PIs; use pravastatin/rosuvastatin", TEAL),
("LIVER DISEASE", "Active disease = CONTRAINDICATED; caution in mild/mod", RED),
("DRUG INTERACTIONS", "Macrolides, azoles, gemfibrozil ↑ myopathy risk", AMBER),
]
for (sp_label, detail, col) in sp_items:
rect(c, margin, cy-14, cw, 14, fill=WHITE, stroke=LGREY, stroke_w=0.3)
rounded_rect(c, margin+1, cy-13, 52, 12, r=2, fill=col)
c.saveState()
c.setFillColor(WHITE); c.setFont("Helvetica-Bold", 5)
lines = simpleSplit(sp_label, "Helvetica-Bold", 5, 50)
ly = cy - 7
for l in lines:
c.drawCentredString(margin+27, ly, l)
ly -= 6
c.restoreState()
multiline(c, detail, margin+56, cy-7, size=5.5, color=DGREY,
max_w=cw-52, line_h=7)
cy -= 14
# ── LDL targets quick ref ────────────────────────────────────────────────
th = 28
rounded_rect(c, margin, cy-th, cw, th, r=4, fill=NAVY, stroke=SKY, stroke_w=0.8)
text(c, "LDL-C TARGETS", margin, cy-9, size=6.5, color=SKY, bold=True,
align="center", max_w=cw)
targets = [
"2° Prevention (very high risk): < 55 mg/dL (≥50% reduction)",
"2° Prevention: < 70 mg/dL (≥50% reduction)",
"1° Prevention (high risk): < 100 mg/dL",
"1° Prevention (mod risk): LDL reduction 30-49%",
]
ty2 = cy - 18
for t in targets:
text(c, t, margin+3, ty2, size=5.5, color=WHITE)
ty2 -= 7.5
cy -= th + 3
# Footer
rect(c, 0, 0, W, 14, fill=TEAL)
text(c, "2026 ACC/AHA Dyslipidemia Guideline | Harrison's 22E (2025)",
margin, 9, size=5.5, color=WHITE, align="center", max_w=cw)
text(c, "For educational use in outpatient clinical practice",
margin, 2, size=5, color=HexColor("#CCE5FF"), align="center", max_w=cw)
# ══════════════════════════════════════════════════════════════════════════════
# RENDER
# ══════════════════════════════════════════════════════════════════════════════
c = canvas.Canvas(OUTPUT, pagesize=(W, H))
c.setTitle("Statin Selection — Quick Reference Pocket Card")
c.setAuthor("Orris Clinical Tools")
c.setSubject("Statin initiation guidelines 2026")
draw_front(c)
c.showPage()
draw_back(c)
c.showPage()
c.save()
print("SUCCESS:", OUTPUT)
cd /home/daytona/workspace/statin-flowchart && python pocket_card.py 2>&1
ls -lh /home/daytona/workspace/statin-flowchart/Statin_Pocket_Card.pdf
Statin Pocket Card
PDF Document · PDF