Work up before starting Semaglutide

I'll look up the pre-treatment workup for Semaglutide from the medical library right away.
Reading File
Now I'll run parallel searches across the library and PubMed simultaneously.
Finding Sources
Searching PubMed

semaglutide pre-treatment workup screening

Searching the Web

semaglutide pre-treatment workup labs baseline evaluation before starting 2025

Now let me pull deeper content from the library and the top PMC article simultaneously.
Finding Sources
Finding Sources
Reading Web Page

https://pmc.ncbi.nlm.nih.gov/articles/PMC12346479

Let me get textbook content on GLP-1 drugs and their clinical use.
Finding Sources
Searching PubMed

semaglutide GLP-1 agonist safety monitoring

Now let me read the relevant textbook sections on GLP-1 RA pharmacotherapy and contraindications.
Reading File
Reading File
I now have sufficient information from the textbooks, the PMC peer-reviewed study, and the web sources to compile a thorough answer. Let me put it together.

Pre-Treatment Workup Before Starting Semaglutide (Ozempic / Wegovy / Rybelsus)

A thorough workup before initiating semaglutide serves three purposes: (1) confirm the indication is appropriate, (2) identify absolute and relative contraindications, and (3) establish a metabolic baseline for ongoing monitoring. A 2025 PMC study on pre-treatment semaglutide assessment found that only 1.8% of patients received a complete pre-treatment evaluation - highlighting how often this is underperformed in practice.

1. Confirm the Indication

IndicationCriteria
Type 2 Diabetes (Ozempic)Diagnosed T2DM, inadequate glycemic control
Obesity / Overweight (Wegovy)BMI ≥ 30, OR BMI ≥ 27 with at least one weight-related comorbidity (HTN, dyslipidemia, OSA, CVD)
Cardiovascular risk reductionEstablished CVD in T2DM patients

2. History and Physical Examination

Personal & Family History - Screen Specifically For:

  • Personal or family history of medullary thyroid carcinoma (MTC) - absolute contraindication
  • Multiple Endocrine Neoplasia type 2 (MEN 2) - absolute contraindication
  • History of pancreatitis (acute or chronic) - use with caution / relative contraindication
  • Gallbladder disease (cholelithiasis, cholecystitis) - increased risk on therapy
  • History of diabetic retinopathy - rapid glycemic improvement can transiently worsen DR in the first 1-2 years
  • Gastroparesis or severe GI motility disorders - semaglutide slows gastric emptying, can exacerbate
  • Eating disorders (anorexia, bulimia)
  • Depression / suicidal ideation - monitor mood on treatment
  • Prior bariatric surgery - dose adjustments may be needed

Vitals & Anthropometrics:

  • Weight, Height, BMI
  • Blood pressure
  • Waist circumference
  • Heart rate

3. Laboratory Workup

Essential (Required)

TestRationale
HbA1cGlycemic baseline; identifies undiagnosed diabetes in Wegovy patients; monitors hypoglycemia risk if on other anti-diabetics
Fasting plasma glucoseAcute glycemic status
Comprehensive Metabolic Panel (CMP)Includes electrolytes, BUN, creatinine, liver enzymes (ALT, AST, ALP, bilirubin), albumin
eGFR / Serum CreatinineSemaglutide is used with caution in severe renal impairment (eGFR < 15); GI side effects may cause dehydration worsening renal function
Lipid Panel (TC, LDL, HDL, TG)Cardiovascular risk stratification; baseline for monitoring improvement
TSHScreen for pre-existing thyroid disease; monitor for thyroid abnormalities; baseline before drug with theoretical thyroid C-cell risk

Highly Recommended

TestRationale
T3 / Free T4Deeper thyroid evaluation if TSH is abnormal
Fasting insulin + HOMA-IRAssesses insulin resistance; not captured by HbA1c alone
Amylase & LipaseBaseline pancreatic enzymes; semaglutide carries a risk of acute pancreatitis
CBC (Complete Blood Count)Rule out anemia, infection, or hematologic issues
Vitamin B12GLP-1 RAs reduce appetite significantly; nutritional monitoring baseline
Vitamin DCommon deficiency in obesity; baseline recommended
Serum CalcitoninSome protocols include this to screen for medullary thyroid pathology before GLP-1 RA initiation (STEP trials excluded patients with calcitonin ≥ 100 ng/L)
Urine albumin-to-creatinine ratio (uACR)Renal baseline, especially in diabetic patients
hs-CRPInflammation baseline

4. Cardiovascular Assessment

  • 12-lead ECG (if age > 40, or existing CVD risk)
  • Blood pressure both arms
  • Review current cardiovascular medications (especially other antihyperglycemics - high hypoglycemia risk when combined with insulin or sulfonylureas)

5. Eye Exam

  • Dilated fundoscopic / retinal exam - mandatory if any history of diabetic retinopathy, as intensive glucose lowering with semaglutide can transiently worsen existing DR within the first 1-2 years of therapy (per PMC 12346479 and SUSTAIN-6 trial data).

6. Absolute Contraindications

  • Personal or family history of Medullary Thyroid Carcinoma (MTC)
  • Personal or family history of MEN 2 syndrome
  • Pregnancy (or planned pregnancy - discontinue at least 2 months before conception)
  • Known hypersensitivity to semaglutide or any component of the formulation

7. Relative Contraindications / Use With Caution

  • History of pancreatitis
  • Severe renal impairment (eGFR < 15 mL/min) - not recommended
  • Severe hepatic impairment - limited data
  • Gastroparesis or delayed gastric emptying
  • High-risk diabetic retinopathy - requires close ophthalmologic follow-up
  • Concomitant use of insulin or insulin secretagogues (sulfonylureas) - increase hypoglycemia risk; consider dose reduction of the secretagogue

8. Medication Review

  • Check for insulin or sulfonylureas - reduce doses to prevent hypoglycemia
  • Check for oral medications with narrow therapeutic windows (warfarin, immunosuppressants) - slowed gastric emptying alters absorption
  • Review for concurrent QT-prolonging drugs if relevant

9. Patient Counseling Before Starting

  • Realistic expectations for weight loss timeline
  • GI side effects (nausea, vomiting, diarrhea) are common early - start low, titrate slow
  • Importance of adherence to injection technique (SC weekly)
  • Drug must be continued long-term - weight typically returns after cessation
  • Anesthesia considerations: hold semaglutide at least 1 week before elective surgery due to risk of aspiration from delayed gastric emptying (per Miller's Anesthesia, 10e)

Summary Checklist

CategoryDone?
BMI, BP, vitals
Personal/family history of MTC, MEN2, pancreatitis
HbA1c, fasting glucose
CMP (renal + hepatic panel)
Lipid panel
TSH (± T3/T4)
Amylase / lipase
CBC
B12, Vitamin D
Fasting insulin
Retinal exam (if diabetic or at-risk)
Medication reconciliation
Contraindications confirmed absent
Patient counseling completed

Key References:

Create a pre-treatment checklist PDF I can print for clinic

Reading File
Running Command

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

Running Command

mkdir -p /home/daytona/workspace/semaglutide-checklist && python3 -c "import reportlab; print('reportlab ok')" && python3 -c "from reportlab.lib.pagesizes import letter; print('letter ok')"

Writing File

~/semaglutide-checklist/build_pdf.py

from reportlab.lib.pagesizes import letter
from reportlab.lib import colors
from reportlab.lib.units import inch
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, HRFlowable
)
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT
from reportlab.platypus import KeepTogether

OUTPUT = "/home/daytona/workspace/semaglutide-checklist/Semaglutide_Pretreatment_Checklist.pdf"

# ── Colors ──────────────────────────────────────────────────────────────────
DARK_BLUE   = colors.HexColor("#1A3A5C")
MED_BLUE    = colors.HexColor("#2C6FAC")
LIGHT_BLUE  = colors.HexColor("#D6E8F7")
ACCENT      = colors.HexColor("#E8F4FD")
RED_WARN    = colors.HexColor("#C0392B")
LIGHT_RED   = colors.HexColor("#FADBD8")
GREEN_OK    = colors.HexColor("#1D6A3A")
LIGHT_GREEN = colors.HexColor("#D5F5E3")
GREY_LINE   = colors.HexColor("#BDC3C7")
WHITE       = colors.white
DARK_TEXT   = colors.HexColor("#1C2833")
MID_GREY    = colors.HexColor("#5D6D7E")

# ── Styles ───────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()

title_style = ParagraphStyle(
    "Title", parent=styles["Normal"],
    fontSize=20, leading=26, textColor=WHITE,
    fontName="Helvetica-Bold", alignment=TA_CENTER
)
subtitle_style = ParagraphStyle(
    "Subtitle", parent=styles["Normal"],
    fontSize=10, leading=14, textColor=colors.HexColor("#AED6F1"),
    fontName="Helvetica", alignment=TA_CENTER
)
section_style = ParagraphStyle(
    "Section", parent=styles["Normal"],
    fontSize=10, leading=13, textColor=WHITE,
    fontName="Helvetica-Bold", alignment=TA_LEFT
)
item_style = ParagraphStyle(
    "Item", parent=styles["Normal"],
    fontSize=9, leading=13, textColor=DARK_TEXT,
    fontName="Helvetica", leftIndent=4
)
item_bold_style = ParagraphStyle(
    "ItemBold", parent=styles["Normal"],
    fontSize=9, leading=13, textColor=DARK_TEXT,
    fontName="Helvetica-Bold", leftIndent=4
)
note_style = ParagraphStyle(
    "Note", parent=styles["Normal"],
    fontSize=8, leading=11, textColor=MID_GREY,
    fontName="Helvetica-Oblique", leftIndent=4
)
warn_style = ParagraphStyle(
    "Warn", parent=styles["Normal"],
    fontSize=8.5, leading=12, textColor=RED_WARN,
    fontName="Helvetica-Bold", leftIndent=4
)
footer_style = ParagraphStyle(
    "Footer", parent=styles["Normal"],
    fontSize=7.5, leading=10, textColor=MID_GREY,
    fontName="Helvetica", alignment=TA_CENTER
)

# ── Helpers ──────────────────────────────────────────────────────────────────
def section_header(text, color=MED_BLUE):
    """Returns a colored header row table."""
    data = [[Paragraph(text, section_style)]]
    t = Table(data, colWidths=[7.2 * inch])
    t.setStyle(TableStyle([
        ("BACKGROUND", (0, 0), (-1, -1), color),
        ("ROWPADDING", (0, 0), (-1, -1), 5),
        ("LEFTPADDING", (0, 0), (-1, -1), 8),
        ("TOPPADDING", (0, 0), (-1, -1), 4),
        ("BOTTOMPADDING", (0, 0), (-1, -1), 4),
        ("ROUNDEDCORNERS", [4, 4, 4, 4]),
    ]))
    return t

def checklist_table(rows, bg=WHITE):
    """
    rows: list of (label, note) tuples.
    Returns a table with a checkbox column and label/note columns.
    """
    data = []
    for label, note in rows:
        label_cell = Paragraph(label, item_bold_style if note else item_style)
        if note:
            combined = [label_cell, Spacer(1, 1), Paragraph(note, note_style)]
        else:
            combined = label_cell
        data.append(["", combined])

    col_widths = [0.35 * inch, 6.85 * inch]
    t = Table(data, colWidths=col_widths, repeatRows=0)

    ts = [
        ("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
        ("LEFTPADDING", (0, 0), (0, -1), 6),
        ("RIGHTPADDING", (0, 0), (0, -1), 0),
        ("LEFTPADDING", (1, 0), (1, -1), 4),
        ("TOPPADDING", (0, 0), (-1, -1), 4),
        ("BOTTOMPADDING", (0, 0), (-1, -1), 4),
        ("LINEBELOW", (0, 0), (-1, -2), 0.4, GREY_LINE),
    ]
    # Alternate row tints
    for i in range(len(rows)):
        if i % 2 == 1:
            ts.append(("BACKGROUND", (0, i), (-1, i), ACCENT))
    t.setStyle(TableStyle(ts))
    return t

def checkbox_symbol():
    """Returns a Unicode square (print-friendly checkbox)."""
    return "□"

def draw_checkbox_col(canvas, doc):
    """Draw actual checkbox squares after the table is placed — not used;
    instead we embed the checkbox character in the first column."""
    pass

# Override checklist_table to put checkbox char in col 0
def checklist_table(rows, section_bg=ACCENT):
    data = []
    for label, note in rows:
        if note:
            cell_content = [
                Paragraph(label, item_bold_style),
                Paragraph(note, note_style),
            ]
        else:
            cell_content = Paragraph(label, item_style)

        data.append([
            Paragraph("<font size=14>□</font>", ParagraphStyle(
                "cb", parent=styles["Normal"], fontSize=14,
                leading=16, textColor=MED_BLUE,
                fontName="Helvetica", alignment=TA_CENTER
            )),
            cell_content,
        ])

    col_widths = [0.3 * inch, 6.9 * inch]
    t = Table(data, colWidths=col_widths)
    ts = [
        ("VALIGN", (0, 0), (-1, -1), "TOP"),
        ("TOPPADDING", (0, 0), (-1, -1), 4),
        ("BOTTOMPADDING", (0, 0), (-1, -1), 4),
        ("LEFTPADDING", (0, 0), (0, -1), 4),
        ("LEFTPADDING", (1, 0), (1, -1), 6),
        ("LINEBELOW", (0, 0), (-1, -2), 0.3, GREY_LINE),
    ]
    for i in range(len(rows)):
        if i % 2 == 1:
            ts.append(("BACKGROUND", (0, i), (-1, i), section_bg))
    t.setStyle(TableStyle(ts))
    return t

# ── Build PDF ─────────────────────────────────────────────────────────────────
doc = SimpleDocTemplate(
    OUTPUT,
    pagesize=letter,
    rightMargin=0.65 * inch,
    leftMargin=0.65 * inch,
    topMargin=0.6 * inch,
    bottomMargin=0.7 * inch,
    title="Semaglutide Pre-Treatment Checklist",
    author="Orris Clinical Tools",
)

story = []

# ── HEADER BANNER ────────────────────────────────────────────────────────────
header_data = [[
    Paragraph("SEMAGLUTIDE PRE-TREATMENT CHECKLIST", title_style),
    Paragraph("Ozempic® / Wegovy® / Rybelsus®", subtitle_style),
    Paragraph("Patient Name: ______________________________    DOB: ______________    Date: ______________", ParagraphStyle(
        "hdr_sub2", parent=styles["Normal"], fontSize=9, leading=13,
        textColor=colors.HexColor("#AED6F1"), fontName="Helvetica", alignment=TA_CENTER
    )),
]]
# Flatten to one column
header_data2 = [
    [Paragraph("SEMAGLUTIDE  PRE-TREATMENT  CHECKLIST", title_style)],
    [Paragraph("Ozempic\u00ae (T2DM)  |  Wegovy\u00ae (Obesity)  |  Rybelsus\u00ae (Oral T2DM)", subtitle_style)],
    [Paragraph(
        "Patient: ________________________________  DOB: ________________  Date: _______________  Provider: ________________",
        ParagraphStyle("hf", parent=styles["Normal"], fontSize=8.5, leading=12,
                       textColor=colors.HexColor("#AED6F1"), fontName="Helvetica", alignment=TA_CENTER)
    )],
]
header_tbl = Table(header_data2, colWidths=[7.2 * inch])
header_tbl.setStyle(TableStyle([
    ("BACKGROUND", (0, 0), (-1, -1), DARK_BLUE),
    ("LEFTPADDING", (0, 0), (-1, -1), 12),
    ("RIGHTPADDING", (0, 0), (-1, -1), 12),
    ("TOPPADDING", (0, 0), (0, 0), 12),
    ("TOPPADDING", (0, 1), (0, 1), 2),
    ("TOPPADDING", (0, 2), (0, 2), 6),
    ("BOTTOMPADDING", (0, 2), (0, 2), 12),
    ("ROUNDEDCORNERS", [6, 6, 6, 6]),
]))
story.append(header_tbl)
story.append(Spacer(1, 10))

# ── INDICATION BOX ───────────────────────────────────────────────────────────
ind_items = [
    ("BMI \u2265 30 kg/m\u00b2  (Obesity - Wegovy)", ""),
    ("BMI \u2265 27 kg/m\u00b2 with \u22651 weight-related comorbidity (HTN, dyslipidemia, OSA, CVD)", "e.g., for Wegovy"),
    ("Type 2 Diabetes Mellitus - inadequate glycemic control  (Ozempic / Rybelsus)", ""),
    ("Established CVD in T2DM for cardiovascular risk reduction  (Ozempic)", ""),
]
story.append(KeepTogether([
    section_header("1.  CONFIRM INDICATION", DARK_BLUE),
    Spacer(1, 3),
    checklist_table(ind_items, ACCENT),
    Spacer(1, 8),
]))

# ── CONTRAINDICATIONS ────────────────────────────────────────────────────────
abs_ci = [
    ("Personal or family history of Medullary Thyroid Carcinoma (MTC)", "ABSOLUTE contraindication - do NOT prescribe"),
    ("Personal or family history of Multiple Endocrine Neoplasia type 2 (MEN 2)", "ABSOLUTE contraindication - do NOT prescribe"),
    ("Known hypersensitivity / allergy to semaglutide", ""),
    ("Pregnancy (or planning pregnancy within 2 months)", "Discontinue \u22652 months before conception"),
]
abs_tbl = Table([[Paragraph(label, ParagraphStyle(
    "warn_item", parent=styles["Normal"], fontSize=9, leading=13,
    textColor=RED_WARN, fontName="Helvetica-Bold", leftIndent=4)),
    Paragraph(note, note_style)] for label, note in abs_ci],
    colWidths=[0.3*inch, 3.5*inch, 3.4*inch])

# Rebuild with checkboxes
abs_rows = []
for label, note in abs_ci:
    cb_style = ParagraphStyle("cb2", parent=styles["Normal"], fontSize=14,
                               leading=16, textColor=RED_WARN, fontName="Helvetica",
                               alignment=TA_CENTER)
    if note:
        cell = [Paragraph(label, ParagraphStyle("wi", parent=styles["Normal"], fontSize=9,
                           leading=13, textColor=RED_WARN, fontName="Helvetica-Bold", leftIndent=4)),
                Paragraph(note, note_style)]
    else:
        cell = Paragraph(label, ParagraphStyle("wi", parent=styles["Normal"], fontSize=9,
                          leading=13, textColor=RED_WARN, fontName="Helvetica-Bold", leftIndent=4))
    abs_rows.append([Paragraph("<font size=14>\u25a1</font>", cb_style), cell])

abs_tbl2 = Table(abs_rows, colWidths=[0.3*inch, 6.9*inch])
abs_tbl2.setStyle(TableStyle([
    ("BACKGROUND", (0, 0), (-1, -1), LIGHT_RED),
    ("VALIGN", (0, 0), (-1, -1), "TOP"),
    ("TOPPADDING", (0, 0), (-1, -1), 4),
    ("BOTTOMPADDING", (0, 0), (-1, -1), 4),
    ("LEFTPADDING", (0, 0), (0, -1), 4),
    ("LEFTPADDING", (1, 0), (1, -1), 6),
    ("LINEBELOW", (0, 0), (-1, -2), 0.3, colors.HexColor("#E74C3C")),
]))

story.append(KeepTogether([
    section_header("2.  ABSOLUTE CONTRAINDICATIONS  (if ANY checked, do NOT prescribe)", colors.HexColor("#922B21")),
    Spacer(1, 3),
    abs_tbl2,
    Spacer(1, 8),
]))

# ── HISTORY & PHYSICAL ───────────────────────────────────────────────────────
hx_items = [
    ("Weight _______ kg   Height _______ cm   BMI _______ kg/m\u00b2", ""),
    ("Blood pressure _______ / _______   HR _______   Waist circumference _______ cm", ""),
    ("Personal hx of pancreatitis (acute or chronic)", "Use with caution / may be relative CI"),
    ("Personal hx of gallbladder disease (stones, cholecystitis)", "Semaglutide increases gallbladder disease risk"),
    ("Personal hx of diabetic retinopathy", "Rapid glucose lowering can transiently worsen DR - mandatory eye exam"),
    ("Personal hx of gastroparesis or severe GI motility disorder", "Semaglutide slows gastric emptying - may worsen"),
    ("Personal hx of depression, suicidal ideation, or eating disorder", "Monitor mood; use with caution"),
    ("Prior bariatric surgery", "May alter dosing and response"),
    ("History of renal impairment (eGFR < 15)", "Not recommended in severe CKD"),
    ("History of severe hepatic impairment", "Limited safety data"),
    ("Family hx of thyroid cancer (non-MTC)", "Monitor thyroid closely"),
]
story.append(KeepTogether([
    section_header("3.  HISTORY & PHYSICAL EXAM", MED_BLUE),
    Spacer(1, 3),
    checklist_table(hx_items, ACCENT),
    Spacer(1, 8),
]))

# ── LABS - ESSENTIAL ─────────────────────────────────────────────────────────
labs_ess = [
    ("HbA1c  _______ %", "Glycemic baseline; identifies undiagnosed DM in obesity patients; monitors hypoglycemia risk"),
    ("Fasting Plasma Glucose  _______ mg/dL", "Acute glycemic status"),
    ("Comprehensive Metabolic Panel (CMP)", "Electrolytes, BUN, creatinine, glucose, LFTs (ALT, AST, ALP, bilirubin, albumin)"),
    ("eGFR / Serum Creatinine  _______ mL/min", "Not recommended if eGFR < 15; GI side effects risk dehydration"),
    ("Lipid Panel  (TC / LDL / HDL / TG)", "Cardiovascular risk baseline; expect improvement on therapy"),
    ("TSH  _______ mIU/L", "Screen for thyroid disease; baseline before drug with C-cell risk signal"),
]
story.append(KeepTogether([
    section_header("4a.  LABORATORY WORKUP - ESSENTIAL", MED_BLUE),
    Spacer(1, 3),
    checklist_table(labs_ess, ACCENT),
    Spacer(1, 8),
]))

# ── LABS - RECOMMENDED ───────────────────────────────────────────────────────
labs_rec = [
    ("Free T4 / T3  _______", "If TSH is abnormal - deeper thyroid evaluation"),
    ("Serum Amylase  _______   Lipase  _______", "Baseline pancreatic enzymes; semaglutide carries acute pancreatitis risk"),
    ("CBC (Complete Blood Count)", "Rule out anemia, infection, hematologic issues"),
    ("Fasting Insulin  _______   HOMA-IR  _______", "Insulin resistance assessment not captured by HbA1c alone"),
    ("Vitamin B12  _______", "Baseline nutrition marker - appetite suppression risks deficiency over time"),
    ("Vitamin D (25-OH)  _______", "Common deficiency in obesity"),
    ("hs-CRP  _______", "Inflammation baseline; semaglutide has anti-inflammatory effects"),
    ("Urine Albumin-to-Creatinine Ratio (uACR)  _______", "Renal baseline in diabetic patients"),
    ("Serum Calcitonin  _______ ng/L", "Optional; STEP trials excluded if calcitonin \u2265 100 ng/L"),
]
story.append(KeepTogether([
    section_header("4b.  LABORATORY WORKUP - HIGHLY RECOMMENDED", MED_BLUE),
    Spacer(1, 3),
    checklist_table(labs_rec, ACCENT),
    Spacer(1, 8),
]))

# ── CARDIOVASCULAR & EYE ─────────────────────────────────────────────────────
cv_items = [
    ("12-lead ECG", "If age > 40 or existing CVD risk factors"),
    ("Blood pressure documented bilaterally", ""),
    ("Dilated fundoscopic / retinal exam", "MANDATORY if any history of diabetic retinopathy - intensive glucose lowering can transiently worsen DR in yr 1-2 (SUSTAIN-6 data)"),
    ("Cardiovascular risk score calculated (e.g., ASCVD 10-yr risk)", ""),
]
story.append(KeepTogether([
    section_header("5.  CARDIOVASCULAR & OPHTHALMOLOGIC ASSESSMENT", MED_BLUE),
    Spacer(1, 3),
    checklist_table(cv_items, ACCENT),
    Spacer(1, 8),
]))

# ── MEDICATION REVIEW ────────────────────────────────────────────────────────
med_items = [
    ("Insulin  (current dose: _________________)", "Reduce dose to prevent hypoglycemia when adding semaglutide"),
    ("Sulfonylureas / Meglitinides  (current: ___________)", "Reduce dose; significantly increased hypoglycemia risk"),
    ("Oral medications with narrow therapeutic index", "Semaglutide slows gastric emptying - alters absorption of warfarin, immunosuppressants, etc."),
    ("Other antihyperglycemics documented", "Review for additive GI effects (e.g., metformin + semaglutide GI overlap)"),
    ("Current medications reviewed for interactions", ""),
]
story.append(KeepTogether([
    section_header("6.  MEDICATION RECONCILIATION", MED_BLUE),
    Spacer(1, 3),
    checklist_table(med_items, ACCENT),
    Spacer(1, 8),
]))

# ── PATIENT COUNSELING ───────────────────────────────────────────────────────
counsel_items = [
    ("Indication, expected benefits, and realistic timeline for weight loss / glycemic control discussed", ""),
    ("GI side effects (nausea, vomiting, diarrhea) explained - common early; start low, titrate slow", ""),
    ("Injection technique (SC, weekly) reviewed with patient / caregiver", "For injectable formulations (Ozempic, Wegovy)"),
    ("Oral administration (Rybelsus) instructions given if applicable", "30 min before first food/drink of day, with \u226490 mL water"),
    ("Weight regain expected if drug is discontinued - long-term therapy discussed", ""),
    ("Anesthesia hold: discontinue \u22651 week before any elective surgery", "Delayed gastric emptying raises aspiration risk (Miller\u2019s Anesthesia, 10e)"),
    ("Signs to report: severe abdominal pain, vomiting, visual changes, neck lump, rapid HR", ""),
    ("Pregnancy / contraception counseled; discontinue \u22652 months before conception", ""),
    ("Follow-up schedule established (3-month labs, annual ophthalmology, annual thyroid palpation)", ""),
]
story.append(KeepTogether([
    section_header("7.  PATIENT COUNSELING & CONSENT", colors.HexColor("#1A5276")),
    Spacer(1, 3),
    checklist_table(counsel_items, LIGHT_GREEN),
    Spacer(1, 8),
]))

# ── PRESCRIBER SIGN-OFF ──────────────────────────────────────────────────────
signoff_data = [
    [
        Paragraph("Prescriber Signature: ______________________________", item_style),
        Paragraph("Date: ___________________", item_style),
    ],
    [
        Paragraph("Indication confirmed:  \u25a1 T2DM (Ozempic / Rybelsus)   \u25a1 Obesity (Wegovy)   \u25a1 CV risk reduction", item_style),
        Paragraph("Starting dose: _______________", item_style),
    ],
    [
        Paragraph("All absolute CIs ruled out:  \u25a1 Yes   \u25a1 No (document reason if prescribing despite relative CI)", item_style),
        Paragraph("", item_style),
    ],
]
signoff_tbl = Table(signoff_data, colWidths=[4.5*inch, 2.7*inch])
signoff_tbl.setStyle(TableStyle([
    ("BACKGROUND", (0, 0), (-1, -1), LIGHT_BLUE),
    ("GRID", (0, 0), (-1, -1), 0.4, GREY_LINE),
    ("TOPPADDING", (0, 0), (-1, -1), 7),
    ("BOTTOMPADDING", (0, 0), (-1, -1), 7),
    ("LEFTPADDING", (0, 0), (-1, -1), 8),
    ("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
]))
story.append(KeepTogether([
    section_header("8.  PRESCRIBER SIGN-OFF", DARK_BLUE),
    Spacer(1, 3),
    signoff_tbl,
    Spacer(1, 10),
]))

# ── FOOTER ───────────────────────────────────────────────────────────────────
story.append(HRFlowable(width="100%", thickness=0.5, color=GREY_LINE))
story.append(Spacer(1, 4))
story.append(Paragraph(
    "References: Sabiston Textbook of Surgery 21e | Miller\u2019s Anesthesia 10e | Harrison\u2019s Principles of Internal Medicine 22e | "
    "PMC12346479 (2025) | STEP & SUSTAIN-6 Trial Data   \u2022   For clinical use only \u2014 verify against current prescribing information",
    footer_style
))

# ── RENDER ───────────────────────────────────────────────────────────────────
doc.build(story)
print("PDF created:", OUTPUT)
Running Command

cd /home/daytona/workspace/semaglutide-checklist && python3 build_pdf.py

Running Command

ls -lh /home/daytona/workspace/semaglutide-checklist/

Your print-ready checklist is ready:

Semaglutide Pretreatment Checklist

PDF Document · PDF


What's included in the PDF:
The checklist is formatted as a single letter-size page with color-coded sections and print-friendly checkbox squares (□) throughout:
SectionContents
HeaderPatient name, DOB, date, provider fill-in fields
1. Confirm IndicationBMI/comorbidity thresholds for Wegovy; T2DM criteria for Ozempic/Rybelsus
2. Absolute ContraindicationsRed-highlighted - MTC hx, MEN2, allergy, pregnancy
3. History & PhysicalVitals fill-ins, pancreatitis, retinopathy, gastroparesis, renal/hepatic hx
4a. Essential LabsHbA1c, FPG, CMP, eGFR, lipid panel, TSH (with fill-in values)
4b. Recommended LabsFree T4/T3, amylase/lipase, CBC, fasting insulin, B12, Vit D, calcitonin
5. CV & EyeECG, BP bilateral, fundoscopic exam, ASCVD risk
6. Medication ReconciliationInsulin/sulfonylurea dose reduction reminders
7. Patient CounselingGI SE, injection technique, anesthesia hold, signs to report
8. Prescriber Sign-offSignature, indication confirmation, starting dose, CI clearance

Create a follow-up monitoring schedule template (3-month, 6-month, 12-month labs)

Running Command

ls -la /home/daytona/workspace/semaglutide-checklist/

Writing File

~/semaglutide-checklist/build_followup_pdf.py

from reportlab.lib.pagesizes import letter
from reportlab.lib import colors
from reportlab.lib.units import inch
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    HRFlowable, KeepTogether
)
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_RIGHT

OUTPUT = "/home/daytona/workspace/semaglutide-checklist/Semaglutide_Followup_Monitoring.pdf"

# ── Colors ───────────────────────────────────────────────────────────────────
DARK_BLUE   = colors.HexColor("#1A3A5C")
MED_BLUE    = colors.HexColor("#2C6FAC")
LIGHT_BLUE  = colors.HexColor("#D6E8F7")
ACCENT      = colors.HexColor("#EBF5FB")
TEAL        = colors.HexColor("#148F77")
LIGHT_TEAL  = colors.HexColor("#D1F2EB")
PURPLE      = colors.HexColor("#6C3483")
LIGHT_PUR   = colors.HexColor("#E8DAEF")
ORANGE      = colors.HexColor("#BA4A00")
LIGHT_ORG   = colors.HexColor("#FDEBD0")
RED_WARN    = colors.HexColor("#C0392B")
LIGHT_RED   = colors.HexColor("#FADBD8")
GREY_LINE   = colors.HexColor("#BDC3C7")
GREY_BG     = colors.HexColor("#F2F3F4")
WHITE       = colors.white
DARK_TEXT   = colors.HexColor("#1C2833")
MID_GREY    = colors.HexColor("#5D6D7E")

# Column colors per visit
V_COLORS = {
    "base":  (DARK_BLUE,   LIGHT_BLUE),   # baseline (reference)
    "3mo":   (TEAL,        LIGHT_TEAL),   # 3-month
    "6mo":   (PURPLE,      LIGHT_PUR),    # 6-month
    "12mo":  (ORANGE,      LIGHT_ORG),    # 12-month
}

# ── Styles ───────────────────────────────────────────────────────────────────
base_styles = getSampleStyleSheet()

def ps(name, base="Normal", **kwargs):
    return ParagraphStyle(name, parent=base_styles[base], **kwargs)

title_s    = ps("T", fontSize=20, leading=26, textColor=WHITE,
                fontName="Helvetica-Bold", alignment=TA_CENTER)
sub_s      = ps("S", fontSize=9.5, leading=13, textColor=colors.HexColor("#AED6F1"),
                fontName="Helvetica", alignment=TA_CENTER)
hdr_fill_s = ps("HF", fontSize=8.5, leading=12, textColor=colors.HexColor("#AED6F1"),
                fontName="Helvetica", alignment=TA_CENTER)
sec_s      = ps("SEC", fontSize=9.5, leading=13, textColor=WHITE,
                fontName="Helvetica-Bold", alignment=TA_LEFT)
visit_s    = ps("VIS", fontSize=10, leading=14, textColor=WHITE,
                fontName="Helvetica-Bold", alignment=TA_CENTER)
visit_sub  = ps("VSUB", fontSize=8, leading=11, textColor=WHITE,
                fontName="Helvetica", alignment=TA_CENTER)
col_hdr_s  = ps("CH", fontSize=8.5, leading=12, textColor=WHITE,
                fontName="Helvetica-Bold", alignment=TA_CENTER)
item_s     = ps("IT", fontSize=8.5, leading=12, textColor=DARK_TEXT,
                fontName="Helvetica", leftIndent=4)
item_b_s   = ps("ITB", fontSize=8.5, leading=12, textColor=DARK_TEXT,
                fontName="Helvetica-Bold", leftIndent=4)
note_s     = ps("NT", fontSize=7.5, leading=10, textColor=MID_GREY,
                fontName="Helvetica-Oblique", leftIndent=4)
cb_s       = ps("CB", fontSize=13, leading=15, textColor=MED_BLUE,
                fontName="Helvetica", alignment=TA_CENTER)
val_s      = ps("VL", fontSize=8.5, leading=12, textColor=DARK_TEXT,
                fontName="Helvetica", alignment=TA_CENTER)
warn_s     = ps("WN", fontSize=8, leading=11, textColor=RED_WARN,
                fontName="Helvetica-Bold", leftIndent=4)
footer_s   = ps("FT", fontSize=7.5, leading=10, textColor=MID_GREY,
                fontName="Helvetica", alignment=TA_CENTER)
action_s   = ps("AC", fontSize=8, leading=11, textColor=DARK_TEXT,
                fontName="Helvetica-Oblique", alignment=TA_CENTER)

# ── Helper: Section Header ────────────────────────────────────────────────────
def section_hdr(text, bg=MED_BLUE, width=7.2*inch):
    t = Table([[Paragraph(text, sec_s)]], colWidths=[width])
    t.setStyle(TableStyle([
        ("BACKGROUND", (0,0),(-1,-1), bg),
        ("LEFTPADDING",(0,0),(-1,-1), 8),
        ("TOPPADDING",(0,0),(-1,-1), 4),
        ("BOTTOMPADDING",(0,0),(-1,-1), 4),
    ]))
    return t

# ── Helper: Visit column header ───────────────────────────────────────────────
def visit_col_hdr(label, sublabel, bg):
    data = [
        [Paragraph(label, visit_s)],
        [Paragraph(sublabel, visit_sub)],
    ]
    t = Table(data, colWidths=[1.55*inch])
    t.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1), bg),
        ("ALIGN",(0,0),(-1,-1),"CENTER"),
        ("TOPPADDING",(0,0),(-1,-1), 4),
        ("BOTTOMPADDING",(0,0),(-1,-1), 4),
    ]))
    return t

# ── Helper: Monitoring grid ───────────────────────────────────────────────────
# rows: list of dicts:
#   label, note, base, m3, m6, m12, action
#   base/m3/m6/m12 = "●" (required), "◑" (if indicated), "○" (optional), "" (not needed)
def monitor_grid(rows, show_action=True):
    """
    Columns: Lab/Assessment | Baseline | 3-Month | 6-Month | 12-Month | Action / Target
    """
    SYMBOL_COLOR = {
        "●": colors.HexColor("#1D6A3A"),   # required - dark green
        "◑": colors.HexColor("#B7770D"),   # if indicated - amber
        "○": MID_GREY,                     # optional - grey
        "":  MID_GREY,
    }

    # Header row
    header = [
        Paragraph("Lab / Assessment", col_hdr_s),
        visit_col_hdr("Baseline", "(Pre-Tx)", DARK_BLUE),
        visit_col_hdr("3 Months", "(±2 wks)", TEAL),
        visit_col_hdr("6 Months", "(±4 wks)", PURPLE),
        visit_col_hdr("12 Months", "(±4 wks)", ORANGE),
    ]
    if show_action:
        header.append(Paragraph("Action / Target", col_hdr_s))

    data = [header]

    for r in rows:
        sym_cells = []
        for sym_key in ["base", "m3", "m6", "m12"]:
            sym = r.get(sym_key, "")
            sym_color = SYMBOL_COLOR.get(sym, MID_GREY)
            sym_cells.append(
                Paragraph(
                    f'<font color="#{sym_color.hexval()[1:]}" size=14>{sym if sym else "-"}</font>',
                    ps(f"sc_{sym}", fontSize=14, leading=16, fontName="Helvetica",
                       alignment=TA_CENTER)
                )
            )

        label_cell = [Paragraph(r["label"], item_b_s)]
        if r.get("note"):
            label_cell.append(Paragraph(r["note"], note_s))

        row = [label_cell] + sym_cells
        if show_action:
            row.append(Paragraph(r.get("action",""), action_s))
        data.append(row)

    col_w = [2.35*inch, 0.75*inch, 0.75*inch, 0.75*inch, 0.75*inch]
    if show_action:
        col_w.append(1.85*inch)

    t = Table(data, colWidths=col_w, repeatRows=1)
    ts = [
        # header background
        ("BACKGROUND",(0,0),(0,0), MED_BLUE),
        ("BACKGROUND",(1,0),(1,0), DARK_BLUE),
        ("BACKGROUND",(2,0),(2,0), TEAL),
        ("BACKGROUND",(3,0),(3,0), PURPLE),
        ("BACKGROUND",(4,0),(4,0), ORANGE),
        ("ALIGN",(1,0),(-1,-1),"CENTER"),
        ("VALIGN",(0,0),(-1,-1),"MIDDLE"),
        ("TOPPADDING",(0,0),(-1,-1), 4),
        ("BOTTOMPADDING",(0,0),(-1,-1), 4),
        ("LEFTPADDING",(0,0),(0,-1), 6),
        ("LINEBELOW",(0,0),(-1,-2), 0.3, GREY_LINE),
        ("ROWBACKGROUNDS",(0,1),(-1,-1),[WHITE, ACCENT]),
    ]
    if show_action:
        ts.append(("BACKGROUND",(5,0),(5,0), MED_BLUE))
    t.setStyle(TableStyle(ts))
    return t

# ── Helper: Visit summary card ────────────────────────────────────────────────
def visit_card(title, date_label, bg_hdr, bg_body, items):
    """
    items: list of (category, text) or (category, [text1, text2, ...])
    """
    card_data = []
    # Title row
    card_data.append([
        Paragraph(f"{title}  |  {date_label}", ps(
            f"vt_{title}", fontSize=10, leading=14, textColor=WHITE,
            fontName="Helvetica-Bold", alignment=TA_LEFT)),
    ])
    for cat, content in items:
        if isinstance(content, list):
            lines = [Paragraph(f"<b>{cat}:</b>", item_b_s)]
            for c in content:
                lines.append(Paragraph(f"\u2003\u2022 {c}", item_s))
            card_data.append([lines])
        else:
            card_data.append([[
                Paragraph(f"<b>{cat}:</b>  {content}", item_s)
            ]])

    t = Table(card_data, colWidths=[7.2*inch])
    ts = [
        ("BACKGROUND",(0,0),(0,0), bg_hdr),
        ("BACKGROUND",(0,1),(-1,-1), bg_body),
        ("LEFTPADDING",(0,0),(-1,-1), 8),
        ("RIGHTPADDING",(0,0),(-1,-1), 8),
        ("TOPPADDING",(0,0),(0,0), 6),
        ("BOTTOMPADDING",(0,0),(0,0), 6),
        ("TOPPADDING",(0,1),(-1,-1), 4),
        ("BOTTOMPADDING",(0,1),(-1,-1), 4),
        ("LINEBELOW",(0,1),(-1,-2), 0.3, GREY_LINE),
        ("BOX",(0,0),(-1,-1), 0.5, GREY_LINE),
    ]
    t.setStyle(TableStyle(ts))
    return t

# ── Legend ────────────────────────────────────────────────────────────────────
def legend():
    leg_data = [[
        Paragraph('<font color="#1D6A3A" size=13>●</font>  <b>Required</b>', item_s),
        Paragraph('<font color="#B7770D" size=13>◑</font>  <b>If clinically indicated</b>', item_s),
        Paragraph('<font color="#5D6D7E" size=13>○</font>  <b>Optional / individualize</b>', item_s),
        Paragraph('<font color="#5D6D7E">—</font>  <b>Not routinely needed</b>', item_s),
    ]]
    t = Table(leg_data, colWidths=[1.8*inch, 2.2*inch, 2.2*inch, 1.0*inch])
    t.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1), GREY_BG),
        ("BOX",(0,0),(-1,-1), 0.5, GREY_LINE),
        ("TOPPADDING",(0,0),(-1,-1), 5),
        ("BOTTOMPADDING",(0,0),(-1,-1), 5),
        ("LEFTPADDING",(0,0),(-1,-1), 8),
        ("VALIGN",(0,0),(-1,-1),"MIDDLE"),
    ]))
    return t

# ═══════════════════════════════════════════════════════════════════════════════
# BUILD
# ═══════════════════════════════════════════════════════════════════════════════
doc = SimpleDocTemplate(
    OUTPUT,
    pagesize=letter,
    rightMargin=0.65*inch, leftMargin=0.65*inch,
    topMargin=0.6*inch, bottomMargin=0.7*inch,
    title="Semaglutide Follow-Up Monitoring Schedule",
    author="Orris Clinical Tools",
)
story = []

# ── PAGE HEADER ───────────────────────────────────────────────────────────────
hdr_data = [
    [Paragraph("SEMAGLUTIDE  FOLLOW-UP MONITORING SCHEDULE", title_s)],
    [Paragraph("Ozempic\u00ae  |  Wegovy\u00ae  |  Rybelsus\u00ae    \u2014    3-Month \u2022 6-Month \u2022 12-Month Labs & Assessment", sub_s)],
    [Paragraph(
        "Patient: ________________________________  DOB: _______________  "
        "Start Date: _______________  Indication: \u25a1 T2DM  \u25a1 Obesity  \u25a1 CV Risk",
        hdr_fill_s)],
]
hdr_tbl = Table(hdr_data, colWidths=[7.2*inch])
hdr_tbl.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1), DARK_BLUE),
    ("LEFTPADDING",(0,0),(-1,-1), 12),
    ("RIGHTPADDING",(0,0),(-1,-1), 12),
    ("TOPPADDING",(0,0),(0,0), 12),
    ("TOPPADDING",(0,1),(0,1), 2),
    ("TOPPADDING",(0,2),(0,2), 6),
    ("BOTTOMPADDING",(0,2),(0,2), 12),
]))
story.append(hdr_tbl)
story.append(Spacer(1, 8))

# ── LEGEND ────────────────────────────────────────────────────────────────────
story.append(legend())
story.append(Spacer(1, 10))

# ── SECTION A: GLYCEMIC MONITORING ───────────────────────────────────────────
glycemic_rows = [
    dict(label="HbA1c",
         note="Primary glycemic endpoint",
         base="●", m3="●", m6="●", m12="●",
         action="Target: <7% (T2DM) or monitor trend (obesity)"),
    dict(label="Fasting Plasma Glucose",
         note="",
         base="●", m3="●", m6="●", m12="●",
         action="Fasting 80-130 mg/dL (ADA target for T2DM)"),
    dict(label="Fasting Insulin / HOMA-IR",
         note="Insulin resistance marker",
         base="◑", m3="", m6="◑", m12="◑",
         action="Track trend; not a dose-adjustment driver alone"),
    dict(label="Hypoglycemia Episodes",
         note="Especially if on insulin or sulfonylurea",
         base="●", m3="●", m6="●", m12="●",
         action="If occurring: reduce insulin / SU dose"),
]
story.append(KeepTogether([
    section_hdr("A.  GLYCEMIC MONITORING", DARK_BLUE),
    Spacer(1,4),
    monitor_grid(glycemic_rows),
    Spacer(1,8),
]))

# ── SECTION B: RENAL FUNCTION ────────────────────────────────────────────────
renal_rows = [
    dict(label="Serum Creatinine + eGFR",
         note="GI side effects risk dehydration \u2192 AKI",
         base="●", m3="●", m6="●", m12="●",
         action="Hold if eGFR <15 or acute AKI; ensure hydration"),
    dict(label="BUN (Blood Urea Nitrogen)",
         note="Dehydration marker",
         base="●", m3="◑", m6="◑", m12="●",
         action="Elevated BUN:Cr ratio suggests prerenal state"),
    dict(label="Urine Albumin-to-Creatinine Ratio (uACR)",
         note="Diabetic nephropathy screening",
         base="◑", m3="", m6="◑", m12="●",
         action="Target: <30 mg/g (normoalbuminuria)"),
    dict(label="Electrolytes (Na, K, Cl, CO2)",
         note="Part of CMP",
         base="●", m3="●", m6="●", m12="●",
         action="Correct hyponatremia if vomiting prominent"),
]
story.append(KeepTogether([
    section_hdr("B.  RENAL FUNCTION", MED_BLUE),
    Spacer(1,4),
    monitor_grid(renal_rows),
    Spacer(1,8),
]))

# ── SECTION C: HEPATIC FUNCTION ──────────────────────────────────────────────
liver_rows = [
    dict(label="ALT / AST",
         note="Liver enzymes",
         base="●", m3="◑", m6="●", m12="●",
         action=">3x ULN: consider dose hold; evaluate for NAFLD improvement"),
    dict(label="ALP / GGT",
         note="Biliary marker; gallbladder disease risk with semaglutide",
         base="●", m3="◑", m6="◑", m12="●",
         action="Elevated: evaluate for cholelithiasis / cholecystitis"),
    dict(label="Total Bilirubin",
         note="",
         base="●", m3="", m6="◑", m12="●",
         action=""),
    dict(label="Albumin",
         note="Nutritional status",
         base="●", m3="", m6="◑", m12="●",
         action="Low albumin: assess for protein-calorie malnutrition"),
]
story.append(KeepTogether([
    section_hdr("C.  HEPATIC FUNCTION & GALLBLADDER", MED_BLUE),
    Spacer(1,4),
    monitor_grid(liver_rows),
    Spacer(1,8),
]))

# ── SECTION D: LIPIDS & CARDIOVASCULAR ───────────────────────────────────────
cv_rows = [
    dict(label="Lipid Panel (TC, LDL, HDL, TG)",
         note="Semaglutide typically improves lipid profile",
         base="●", m3="", m6="●", m12="●",
         action="LDL target depends on CV risk tier; expect TG \u2193"),
    dict(label="Blood Pressure",
         note="Each visit",
         base="●", m3="●", m6="●", m12="●",
         action="Target: <130/80 mmHg (AHA/ACC)"),
    dict(label="Heart Rate",
         note="GLP-1 RAs mildly raise HR (+2-4 bpm)",
         base="●", m3="●", m6="●", m12="●",
         action="Persistent tachycardia: evaluate; consider ECG"),
    dict(label="ECG",
         note="If symptomatic palpitations or new arrhythmia concern",
         base="◑", m3="◑", m6="◑", m12="◑",
         action="If resting HR >100 or symptoms: 12-lead ECG"),
]
story.append(KeepTogether([
    section_hdr("D.  LIPIDS & CARDIOVASCULAR", MED_BLUE),
    Spacer(1,4),
    monitor_grid(cv_rows),
    Spacer(1,8),
]))

# ── SECTION E: THYROID ────────────────────────────────────────────────────────
thyroid_rows = [
    dict(label="TSH",
         note="Theoretical C-cell risk signal from animal data",
         base="●", m3="", m6="◑", m12="●",
         action="If TSH abnormal: check Free T4; refer if thyroid mass felt"),
    dict(label="Free T4",
         note="If TSH out of range",
         base="◑", m3="◑", m6="◑", m12="◑",
         action="Hypothyroidism may worsen obesity outcomes if untreated"),
    dict(label="Serum Calcitonin",
         note="Optional; if neck mass or family hx non-MTC thyroid cancer",
         base="○", m3="", m6="○", m12="○",
         action="If \u2265100 ng/L: refer to endocrinology / discontinue"),
    dict(label="Thyroid palpation (clinical)",
         note="Check for new nodules at each visit",
         base="●", m3="●", m6="●", m12="●",
         action="New neck mass: urgent thyroid US + calcitonin"),
]
story.append(KeepTogether([
    section_hdr("E.  THYROID MONITORING", TEAL),
    Spacer(1,4),
    monitor_grid(thyroid_rows),
    Spacer(1,8),
]))

# ── SECTION F: PANCREATIC ────────────────────────────────────────────────────
panc_rows = [
    dict(label="Serum Amylase",
         note="Baseline and if abdominal pain develops",
         base="●", m3="◑", m6="◑", m12="◑",
         action="If \u22653x ULN with sx: hold drug, evaluate for pancreatitis"),
    dict(label="Serum Lipase",
         note="More sensitive than amylase for pancreatitis",
         base="●", m3="◑", m6="◑", m12="◑",
         action="Acute pancreatitis confirmed: DISCONTINUE permanently"),
    dict(label="Abdominal pain / N&V symptom screen",
         note="At every visit",
         base="●", m3="●", m6="●", m12="●",
         action="Severe persistent pain: hold drug, urgent imaging"),
]
story.append(KeepTogether([
    section_hdr("F.  PANCREATIC MONITORING", TEAL),
    Spacer(1,4),
    monitor_grid(panc_rows),
    Spacer(1,8),
]))

# ── SECTION G: WEIGHT & ANTHROPOMETRICS ──────────────────────────────────────
wt_rows = [
    dict(label="Body Weight (kg)",
         note="",
         base="●", m3="●", m6="●", m12="●",
         action="<5% loss at 16 wks: reassess indication / adherence"),
    dict(label="BMI (kg/m\u00b2)",
         note="",
         base="●", m3="●", m6="●", m12="●",
         action="Goal: \u22655% weight loss at 6 months to continue"),
    dict(label="Waist Circumference (cm)",
         note="Visceral adiposity proxy",
         base="●", m3="", m6="●", m12="●",
         action="Target: <88 cm (F), <102 cm (M)"),
    dict(label="Dose documentation & titration note",
         note="",
         base="●", m3="●", m6="●", m12="●",
         action="Titrate per tolerability; max 2.4 mg/wk (Wegovy)"),
]
story.append(KeepTogether([
    section_hdr("G.  WEIGHT, BMI & DOSE TITRATION", PURPLE),
    Spacer(1,4),
    monitor_grid(wt_rows),
    Spacer(1,8),
]))

# ── SECTION H: NUTRITIONAL & OPHTHALMOLOGIC ──────────────────────────────────
nutr_rows = [
    dict(label="Vitamin B12",
         note="Appetite suppression risks deficiency over time",
         base="●", m3="", m6="●", m12="●",
         action="If low: oral B12 supplementation"),
    dict(label="Vitamin D (25-OH)",
         note="Common deficiency in obesity",
         base="●", m3="", m6="○", m12="●",
         action="Target: >30 ng/mL; supplement if deficient"),
    dict(label="CBC",
         note="Nutritional anemia screen",
         base="●", m3="", m6="◑", m12="●",
         action="MCV changes may indicate B12/folate deficiency"),
    dict(label="Dilated retinal exam",
         note="Rapid glucose lowering can transiently worsen diabetic retinopathy",
         base="●", m3="", m6="", m12="●",
         action="Annually in DM pts; more frequent if pre-existing DR"),
    dict(label="Peripheral neuropathy screen",
         note="Monofilament + vibration",
         base="◑", m3="", m6="", m12="●",
         action="Annual foot exam in diabetic patients"),
]
story.append(KeepTogether([
    section_hdr("H.  NUTRITIONAL STATUS & OPHTHALMOLOGIC", PURPLE),
    Spacer(1,4),
    monitor_grid(nutr_rows),
    Spacer(1,8),
]))

# ── SECTION I: MENTAL HEALTH & SIDE EFFECTS ──────────────────────────────────
mh_rows = [
    dict(label="GI side effects (nausea, vomiting, diarrhea)",
         note="Most common early adverse effects",
         base="●", m3="●", m6="●", m12="●",
         action="Antiemetics PRN; consider dose hold / slower titration"),
    dict(label="Depression / Mood screening (PHQ-2/PHQ-9)",
         note="Especially if hx of mood disorder",
         base="◑", m3="◑", m6="◑", m12="●",
         action="Worsening mood: reassess; do not abruptly stop if T2DM"),
    dict(label="Injection site reactions",
         note="Rotate sites each week",
         base="", m3="●", m6="●", m12="●",
         action="Persistent nodules: review technique; consider rotation sites"),
    dict(label="Gallbladder symptom screen",
         note="Right upper quadrant pain, fatty food intolerance",
         base="●", m3="●", m6="●", m12="●",
         action="Symptomatic: RUQ US; refer surgery if cholelithiasis"),
    dict(label="Heart rate / palpitation screen",
         note="GLP-1 RAs cause small HR increase",
         base="●", m3="●", m6="●", m12="●",
         action="HR >100 at rest: ECG; consider cardiology referral"),
]
story.append(KeepTogether([
    section_hdr("I.  SIDE EFFECTS & MENTAL HEALTH SCREEN", ORANGE),
    Spacer(1,4),
    monitor_grid(mh_rows),
    Spacer(1,8),
]))

# ── SECTION J: VISIT SUMMARY CARDS ──────────────────────────────────────────
story.append(section_hdr("J.  VISIT SUMMARY CARDS  (detach or use separately)", DARK_BLUE))
story.append(Spacer(1, 6))

# 3-month card
card3 = visit_card(
    "3-MONTH VISIT", "Date: ___________________ / Provider: ___________________",
    TEAL, LIGHT_TEAL,
    [
        ("Weight", "_______ kg  |  BMI _______ kg/m\u00b2  |  BP _______ / _______  |  HR _______"),
        ("HbA1c", "_______ %  |  FPG _______ mg/dL"),
        ("CMP", "Cr _______ | eGFR _______ | ALT _______ | AST _______  | Na _______ | K _______"),
        ("Dose", "Current dose: _______________  \u25a1 Tolerated well  \u25a1 GI side effects present"),
        ("Hypoglycemia", "\u25a1 None   \u25a1 Episodes (document): _____________________________________"),
        ("GI Symptoms", "\u25a1 None  \u25a1 Nausea  \u25a1 Vomiting  \u25a1 Diarrhea  \u25a1 Constipation"),
        ("Thyroid Palpation", "\u25a1 Normal  \u25a1 Mass / nodule detected \u2192 order thyroid US + calcitonin"),
        ("Abdominal Pain", "\u25a1 None  \u25a1 Present \u2014 amylase / lipase ordered"),
        ("Plan", "_____________________________________________________________________________"),
        ("Next Visit", "6-month labs due: ___________________ "),
    ]
)
story.append(card3)
story.append(Spacer(1, 8))

# 6-month card
card6 = visit_card(
    "6-MONTH VISIT", "Date: ___________________ / Provider: ___________________",
    PURPLE, LIGHT_PUR,
    [
        ("Weight", "_______ kg  |  BMI _______ kg/m\u00b2  |  BP _______ / _______  |  HR _______  |  Waist _______ cm"),
        ("HbA1c", "_______ %  |  FPG _______ mg/dL  |  Fasting Insulin _______  |  HOMA-IR _______"),
        ("CMP (full)", "Cr _______ | eGFR _______ | ALT _______ | AST _______ | ALP _______ | Bili _______"),
        ("Lipids", "TC _______ | LDL _______ | HDL _______ | TG _______"),
        ("Thyroid", "TSH _______  |  Thyroid palpation: \u25a1 Normal  \u25a1 Abnormal"),
        ("Nutrition", "B12 _______  |  Vit D _______  |  CBC: Hgb _______"),
        ("Dose", "Current dose: _______________  \u25a1 At max dose  \u25a1 Titrating"),
        ("Response", "\u25a1 \u22655% weight loss achieved  \u25a1 <5% loss \u2014 reassess adherence, diet, consider adjunct therapy"),
        ("GI / Gallbladder", "\u25a1 No symptoms  \u25a1 RUQ pain \u2192 order RUQ US"),
        ("Plan", "_____________________________________________________________________________"),
        ("Next Visit", "12-month labs due: ___________________"),
    ]
)
story.append(card6)
story.append(Spacer(1, 8))

# 12-month card
card12 = visit_card(
    "12-MONTH VISIT", "Date: ___________________ / Provider: ___________________",
    ORANGE, LIGHT_ORG,
    [
        ("Weight", "_______ kg  |  BMI _______ kg/m\u00b2  |  BP _______ / _______  |  HR _______  |  Waist _______ cm"),
        ("HbA1c", "_______ %  |  FPG _______ mg/dL  |  Fasting Insulin _______  |  HOMA-IR _______"),
        ("Full CMP + CBC", "Cr _______ | eGFR _______ | LFTs _______ | Electrolytes _______ | Hgb _______"),
        ("Lipids", "TC _______ | LDL _______ | HDL _______ | TG _______"),
        ("Thyroid", "TSH _______  |  Free T4 _______  |  Thyroid palpation: \u25a1 Normal  \u25a1 Abnormal"),
        ("Nutrition", "B12 _______  |  Vit D _______  |  Albumin _______"),
        ("Renal", "uACR _______  mg/g  |  eGFR trend: improving / stable / declining"),
        ("Eyes", "\u25a1 Dilated retinal exam completed (date: ___________)  \u25a1 Referred to ophthalmology"),
        ("Neuropathy", "\u25a1 Monofilament normal  \u25a1 Abnormal \u2192 refer"),
        ("Mental Health", "PHQ-2 score: _______  \u25a1 No concerns  \u25a1 PHQ-9 ordered"),
        ("Dose", "Current dose: _______________  \u25a1 Maintenance achieved  \u25a1 Continue titrating"),
        ("Annual Assessment", "\u25a1 Benefit continues \u2192 continue  \u25a1 Inadequate response \u2192 reassess / switch"),
        ("Plan", "_____________________________________________________________________________"),
        ("Next Visit", "Annual follow-up due: ___________________"),
    ]
)
story.append(card12)
story.append(Spacer(1, 10))

# ── DISCONTINUATION TRIGGERS ──────────────────────────────────────────────────
disc_items = [
    ("Confirmed acute pancreatitis", "DISCONTINUE permanently - do not rechallenge"),
    ("Medullary thyroid carcinoma confirmed or newly diagnosed", "DISCONTINUE immediately - refer endocrinology / oncology"),
    ("Pregnancy confirmed", "DISCONTINUE immediately; switch to safe alternative"),
    ("Severe hypersensitivity reaction (anaphylaxis, angioedema)", "DISCONTINUE permanently"),
    ("eGFR < 15 mL/min or dialysis initiation", "DISCONTINUE; not recommended in severe CKD"),
    ("<5% weight loss at 16 weeks (obesity indication)", "Consider discontinuing - clinical guidelines suggest reassessment"),
    ("Severe or persistent GI symptoms affecting quality of life", "Consider dose reduction, hold, or switch agent"),
]
disc_rows = []
for label, note in disc_items:
    cb_p = Paragraph('<font color="#C0392B" size=13>\u25a1</font>',
                     ps("dc", fontSize=13, leading=15, textColor=RED_WARN,
                        fontName="Helvetica", alignment=TA_CENTER))
    if note:
        cell = [Paragraph(label, ps("dl", fontSize=9, leading=13, textColor=RED_WARN,
                                    fontName="Helvetica-Bold", leftIndent=4)),
                Paragraph(note, note_s)]
    else:
        cell = Paragraph(label, ps("dl2", fontSize=9, leading=13, textColor=RED_WARN,
                                   fontName="Helvetica-Bold", leftIndent=4))
    disc_rows.append([cb_p, cell])

disc_tbl = Table(disc_rows, colWidths=[0.3*inch, 6.9*inch])
disc_tbl.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1), LIGHT_RED),
    ("VALIGN",(0,0),(-1,-1),"TOP"),
    ("TOPPADDING",(0,0),(-1,-1), 4),
    ("BOTTOMPADDING",(0,0),(-1,-1), 4),
    ("LEFTPADDING",(0,0),(0,-1), 4),
    ("LEFTPADDING",(1,0),(1,-1), 6),
    ("LINEBELOW",(0,0),(-1,-2), 0.3, colors.HexColor("#E74C3C")),
]))

story.append(KeepTogether([
    section_hdr("K.  DISCONTINUATION TRIGGERS", colors.HexColor("#922B21")),
    Spacer(1,3),
    disc_tbl,
    Spacer(1,10),
]))

# ── ANNUAL RENEWAL CHECKLIST ───────────────────────────────────────────────────
renew_items = [
    ("\u25a1  Indication remains valid (BMI / DM / CV risk still meets criteria)", ""),
    ("\u25a1  Patient reports adherence and tolerability", ""),
    ("\u25a1  Documented clinical benefit (weight loss \u22655% OR HbA1c improvement)", ""),
    ("\u25a1  No new contraindications identified", ""),
    ("\u25a1  All annual labs reviewed and documented", ""),
    ("\u25a1  Prior authorization / refill submitted", ""),
]
renew_tbl_data = [[Paragraph(label, item_s)] for label, _ in renew_items]
renew_tbl = Table(renew_tbl_data, colWidths=[7.2*inch])
renew_tbl.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1), LIGHT_BLUE),
    ("LEFTPADDING",(0,0),(-1,-1), 10),
    ("TOPPADDING",(0,0),(-1,-1), 4),
    ("BOTTOMPADDING",(0,0),(-1,-1), 4),
    ("LINEBELOW",(0,0),(-1,-2), 0.3, GREY_LINE),
]))
story.append(KeepTogether([
    section_hdr("L.  ANNUAL RENEWAL CRITERIA", DARK_BLUE),
    Spacer(1,3),
    renew_tbl,
    Spacer(1,10),
]))

# ── FOOTER ────────────────────────────────────────────────────────────────────
story.append(HRFlowable(width="100%", thickness=0.5, color=GREY_LINE))
story.append(Spacer(1,4))
story.append(Paragraph(
    "References: Sabiston Textbook of Surgery 21e | Miller\u2019s Anesthesia 10e | Harrison\u2019s Principles of Internal Medicine 22e | "
    "PMC12346479 (2025) | ADA Standards of Care 2024 | STEP & SUSTAIN-6 Trial Data   \u2022   For clinical use only \u2014 verify against current prescribing information",
    footer_s
))

doc.build(story)
print("PDF created:", OUTPUT)
Running Command

cd /home/daytona/workspace/semaglutide-checklist && python3 build_followup_pdf.py

The hexval() method returns a string with a leading #x prefix. I need to use the hex value directly from the color definitions instead.
Editing File

~/semaglutide-checklist/build_followup_pdf.py

Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/semaglutide-checklist/build_followup_pdf.py'
Editing File

~/semaglutide-checklist/build_followup_pdf.py

Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/semaglutide-checklist/build_followup_pdf.py'
Also fix the legend section which has the same issue:
Editing File

~/semaglutide-checklist/build_followup_pdf.py

Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/semaglutide-checklist/build_followup_pdf.py'
Running Command

cd /home/daytona/workspace/semaglutide-checklist && python3 build_followup_pdf.py

Running Command

ls -lh /home/daytona/workspace/semaglutide-checklist/

Your monitoring schedule PDF is ready:

Semaglutide Followup Monitoring

PDF Document · PDF


What's inside (12 sections):
Monitoring Grid (Sections A-I) - Color-coded matrix showing each lab/assessment across all 4 timepoints with a symbol key:
  • 🟢 ● Required at that visit
  • 🟡 ◑ Order if clinically indicated
  • ⚪ ○ Optional / individualize
  • — Not routinely needed
SectionLabs & Assessments
A. GlycemicHbA1c, FPG, fasting insulin, hypoglycemia screen
B. RenalCreatinine, eGFR, BUN, electrolytes, uACR
C. Hepatic / GallbladderALT, AST, ALP, bilirubin, albumin
D. Lipids & CVFull lipid panel, BP, HR, ECG
E. ThyroidTSH, Free T4, calcitonin, palpation
F. PancreaticAmylase, lipase, abdominal symptom screen
G. Weight & DoseWeight, BMI, waist circumference, titration log
H. Nutrition & EyesB12, Vit D, CBC, retinal exam, neuropathy screen
I. Side Effects & Mental HealthGI symptoms, PHQ screen, gallbladder, injection sites
Visit Summary Cards (Section J) - Separate fill-in cards for 3-, 6-, and 12-month visits with all value fill-in fields, perfect for scanning or filing in the chart.
Section K - Discontinuation trigger checklist (red-flagged)
Section L - Annual renewal criteria checklist
This is a shared conversation. Sign in to Orris to start your own chat.