Varicella zoster without encephalitis symptoms prescription

Reading File
Finding Sources
Searching PubMed

varicella zoster treatment antiviral

Reading File
Reading File
Reading File
Reading File
Reading File
Reading File
Reading File
Reading File
I have comprehensive information. Now I'll compile the full answer:

Varicella Zoster Virus (VZV) - Prescription Without Encephalitis

Disclaimer: This is for educational/clinical reference only. Always confirm dosing against current formulary and adjust for renal function, age, and immune status.

Overview

VZV causes two distinct presentations:
  • Varicella (chickenpox) - primary infection
  • Herpes Zoster (shingles) - reactivation from dorsal root ganglia
The treatment goal in uncomplicated (non-encephalitis) disease is oral antiviral therapy started as early as possible - ideally within 24-72 hours of rash onset.

1. VARICELLA (Primary Chickenpox) - Without Encephalitis

Immunocompetent Patients

Patient GroupRegimenDuration
NeonateAcyclovir 10 mg/kg or 500 mg/m² IV every 8 h10 days
Child 2-18 years (immunocompetent)Symptomatic treatment alone (routine antiviral NOT recommended) -OR- Valacyclovir 20 mg/kg orally every 8 h (max 3 g/day) -OR- Acyclovir 20 mg/kg orally 4x/day (max 3200 mg/day)5 days
Adolescent (≥40 kg) / AdultValacyclovir 1 g orally every 8 h (preferred) -OR- Famciclovir 500 mg orally every 8 h -OR- Acyclovir 800 mg orally 5x/day7 days
Pregnancy (uncomplicated)Routine antiviral use NOT recommended; treat complications (e.g., pneumonia) with IV acyclovir
Key note on timing: In adults, acyclovir started within 24 hours of rash onset decreases lesion count and disease severity. Oral valacyclovir may be more effective than oral acyclovir.

Immunocompromised Patients (No Encephalitis)

SeverityRegimenDuration
Mild varicella OR mild immunocompromiseValacyclovir 1 g orally every 8 h -OR- Famciclovir 500 mg orally every 8 h -OR- Acyclovir 800 mg orally 5x/day7-10 days
Severe varicella OR severe immunocompromiseAcyclovir 10 mg/kg IV every 8 h7-10 days
Acyclovir-resistant VZV (advanced AIDS/transplant)Foscarnet 40 mg/kg IV every 8 hUntil all lesions crusted (~2 weeks)

2. HERPES ZOSTER (Shingles) - Without Encephalitis

When to Treat

  • Start within 72 hours of rash onset for maximum benefit
  • Still initiate even after 72 h if: new vesicles are forming, ophthalmic/cranial nerve involvement, age >50, or immunocompromise

Immunocompetent Adults - Oral Therapy (Preferred)

DrugDoseDuration
Valacyclovir (1st line, preferred)1000 mg (1 g) orally every 8 h7 days
Famciclovir (1st line, preferred)500 mg orally every 8 h7 days
Acyclovir (acceptable but less preferred)800 mg orally 5 times/day7 days
Valacyclovir and famciclovir are preferred over acyclovir for zoster because VZV is ~10x less sensitive to acyclovir than HSV, they have better oral bioavailability, produce higher blood levels, and allow three-times-daily (vs. 5x/day) dosing. - Fitzpatrick's Dermatology, p. 3082

Immunocompromised Patients

SeverityRegimen
Mild immunocompromise, localized zosterValacyclovir 1 g orally every 8 h -OR- Famciclovir 500 mg orally every 8 h x 7-10 days
Severe immunocompromiseAcyclovir 10 mg/kg IV every 8 h x 7-10 days (or until all lesions crusted); can switch to oral once stable
Acyclovir-resistantFoscarnet 40 mg/kg IV every 8 h

3. Special Scenarios (No Encephalitis)

SituationTreatment
Ophthalmic zoster (Zoster ophthalmicus)Oral valacyclovir or famciclovir (preferred) + ophthalmology referral; treat even if >72 h post-onset
Ramsay Hunt syndrome (cranial nerve VII)Oral valacyclovir or famciclovir, same doses as zoster
Post-herpetic neuralgia (PHN) - pain managementGabapentin 300 mg/night titrated to 1200 mg TID, OR pregabalin 75 mg/night titrated to 300 mg BID, OR nortriptyline 25 mg/night titrated to 150 mg/day, OR lidocaine patches, OR topical capsaicin

4. Adjunct Therapy

  • Corticosteroids (e.g., prednisone 60 mg/day tapered over 21 days) added to acyclovir may reduce acute pain and improve quality of life in patients >50 years, but do NOT reduce PHN incidence - use selectively for moderate-severe pain without contraindications.
  • Pain control: NSAIDs, paracetamol, or opioids as needed for acute zoster pain. Target pain <3/10 on VAS.
  • Topical: Cool compresses, calamine lotion, cornstarch during the acute phase of zoster. Topical antivirals have no role.

5. Renal Dose Adjustments (Summary)

DrugCrCl 25-50 mL/minCrCl 10-25 mL/minCrCl <10 mL/min
Valacyclovir (zoster)1 g every 12 h1 g every 24 h500 mg every 24 h
Famciclovir (zoster)500 mg every 12 h500 mg every 24 h250 mg every 24 h
Acyclovir 800 mg (zoster)800 mg every 8 h800 mg every 12 h800 mg every 24 h

Summary: Drug of Choice

  • Uncomplicated adult varicella or zoster (immunocompetent): Valacyclovir 1 g TID x 7 days (oral) - preferred due to ease of dosing and pharmacokinetics
  • Severe/complicated or immunocompromised: IV Acyclovir 10 mg/kg every 8 h
  • Acyclovir-resistant VZV: Foscarnet 40 mg/kg IV every 8 h
Sources:
  • Fitzpatrick's Dermatology, Tables 165-3 & 165-4 (pp. 3081-3082)
  • Goldman-Cecil Medicine, Chapter 346 (pp. 3612-3614)
  • Harrison's Principles of Internal Medicine 22E, Chapter 328 (Antiviral therapy tables)

Generate a quick reference prescription PDF for VZV treatment

Reading File
Running Command

ls -la /home/daytona/workspace/ 2>/dev/null && mkdir -p /home/daytona/workspace/vzv-pdf

Writing File

~/vzv-pdf/generate_vzv_pdf.py

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

OUTPUT = "/home/daytona/workspace/vzv-pdf/VZV_Treatment_Quick_Reference.pdf"

# ── Colour palette ────────────────────────────────────────────────
NAVY      = HexColor("#1B3A6B")
TEAL      = HexColor("#0F7B8C")
LIGHT_TEAL= HexColor("#E6F4F7")
AMBER     = HexColor("#F5A623")
LIGHT_AMB = HexColor("#FFF8EC")
RED_WARN  = HexColor("#C0392B")
LIGHT_RED = HexColor("#FDECEA")
GREEN     = HexColor("#27AE60")
LIGHT_GRN = HexColor("#EAF7EF")
LIGHT_GREY= HexColor("#F4F6F8")
MID_GREY  = HexColor("#BDC3C7")
DARK_GREY = HexColor("#555555")
WHITE     = colors.white
BLACK     = colors.black

W, H = A4  # 210 x 297 mm

doc = SimpleDocTemplate(
    OUTPUT,
    pagesize=A4,
    leftMargin=14*mm, rightMargin=14*mm,
    topMargin=14*mm, bottomMargin=14*mm,
)

styles = getSampleStyleSheet()

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

# ── Custom styles ─────────────────────────────────────────────────
title_style = S("DocTitle",
    fontSize=20, leading=24, textColor=WHITE,
    fontName="Helvetica-Bold", alignment=TA_CENTER)

subtitle_style = S("DocSub",
    fontSize=10, leading=13, textColor=HexColor("#D0E8F0"),
    fontName="Helvetica", alignment=TA_CENTER)

section_header = S("SecHeader",
    fontSize=11, leading=14, textColor=WHITE,
    fontName="Helvetica-Bold", alignment=TA_LEFT,
    leftIndent=4, spaceBefore=2)

body = S("Body",
    fontSize=8.5, leading=12, textColor=DARK_GREY,
    fontName="Helvetica")

bold_body = S("BoldBody",
    fontSize=8.5, leading=12, textColor=BLACK,
    fontName="Helvetica-Bold")

small = S("Small",
    fontSize=7.5, leading=10, textColor=DARK_GREY,
    fontName="Helvetica", leftIndent=4)

footnote = S("Footnote",
    fontSize=7, leading=9, textColor=DARK_GREY,
    fontName="Helvetica-Oblique")

warn_style = S("Warn",
    fontSize=8, leading=11, textColor=RED_WARN,
    fontName="Helvetica-Bold")

green_style = S("GreenNote",
    fontSize=8, leading=11, textColor=GREEN,
    fontName="Helvetica-Bold")

# ── Helper: section banner ────────────────────────────────────────
def banner(text, color=NAVY):
    t = Table([[Paragraph(text, section_header)]],
              colWidths=[W - 28*mm])
    t.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), color),
        ("ROUNDEDCORNERS", [4]),
        ("TOPPADDING",  (0,0), (-1,-1), 5),
        ("BOTTOMPADDING",(0,0), (-1,-1), 5),
        ("LEFTPADDING", (0,0), (-1,-1), 8),
    ]))
    return t

# ── Helper: info box ──────────────────────────────────────────────
def info_box(rows, header_color=NAVY, alt_color=LIGHT_GREY,
             col_widths=None, header_text_color=WHITE):
    if col_widths is None:
        col_widths = [55*mm, W - 28*mm - 55*mm]
    styled_rows = []
    for i, row in enumerate(rows):
        styled_row = [Paragraph(str(cell), bold_body if i == 0 else body)
                      for cell in row]
        styled_rows.append(styled_row)

    ts = TableStyle([
        # header row
        ("BACKGROUND",    (0,0), (-1,0), header_color),
        ("TEXTCOLOR",     (0,0), (-1,0), header_text_color),
        ("FONTNAME",      (0,0), (-1,0), "Helvetica-Bold"),
        ("FONTSIZE",      (0,0), (-1,0), 9),
        ("TOPPADDING",    (0,0), (-1,0), 6),
        ("BOTTOMPADDING", (0,0), (-1,0), 6),
        # data rows alternating
        ("ROWBACKGROUNDS",(0,1), (-1,-1), [WHITE, alt_color]),
        ("FONTSIZE",      (0,1), (-1,-1), 8.5),
        ("TOPPADDING",    (0,1), (-1,-1), 5),
        ("BOTTOMPADDING", (0,1), (-1,-1), 5),
        # all
        ("LEFTPADDING",   (0,0), (-1,-1), 7),
        ("RIGHTPADDING",  (0,0), (-1,-1), 7),
        ("VALIGN",        (0,0), (-1,-1), "TOP"),
        ("GRID",          (0,0), (-1,-1), 0.3, MID_GREY),
        ("LINEBELOW",     (0,0), (-1,0),  1,   header_color),
    ])
    t = Table(styled_rows, colWidths=col_widths, repeatRows=1)
    t.setStyle(ts)
    return t

# ── Coloured note box ─────────────────────────────────────────────
def note_box(text, bg=LIGHT_TEAL, border=TEAL, style=None):
    s = style or body
    t = Table([[Paragraph(text, s)]],
              colWidths=[W - 28*mm])
    t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,-1), bg),
        ("LINEBELOW",     (0,0), (-1,-1), 2, border),
        ("LINEBEFORE",    (0,0), (-1,-1), 3, border),
        ("TOPPADDING",    (0,0), (-1,-1), 5),
        ("BOTTOMPADDING", (0,0), (-1,-1), 5),
        ("LEFTPADDING",   (0,0), (-1,-1), 8),
        ("RIGHTPADDING",  (0,0), (-1,-1), 8),
    ]))
    return t

# ─────────────────────────────────────────────────────────────────
# BUILD CONTENT
# ─────────────────────────────────────────────────────────────────
story = []

# ═══════════════════════════════════════════════════════════════
# TITLE BLOCK
# ═══════════════════════════════════════════════════════════════
title_data = [[
    Paragraph("Varicella-Zoster Virus (VZV)", title_style),
    Paragraph("Treatment Quick Reference", title_style),
]]
title_sub = [[
    Paragraph("Excludes CNS/Encephalitis presentations  •  For uncomplicated & dermatological disease", subtitle_style),
]]
header_table = Table([
    [Paragraph("Varicella-Zoster Virus (VZV) — Treatment Quick Reference", title_style)],
    [Paragraph("Excludes CNS/Encephalitis presentations  •  Uncomplicated & Dermatological Disease Only", subtitle_style)],
    [Paragraph(f"Reference: Fitzpatrick's Dermatology | Goldman-Cecil Medicine | Harrison's 22E  •  Generated {datetime.date.today().strftime('%B %d, %Y')}", subtitle_style)],
], colWidths=[W - 28*mm])
header_table.setStyle(TableStyle([
    ("BACKGROUND",    (0,0), (-1,-1), NAVY),
    ("TOPPADDING",    (0,0), (-1,-1), 7),
    ("BOTTOMPADDING", (0,0), (-1,-1), 7),
    ("LEFTPADDING",   (0,0), (-1,-1), 10),
    ("RIGHTPADDING",  (0,0), (-1,-1), 10),
    ("ROUNDEDCORNERS",[5]),
]))
story.append(header_table)
story.append(Spacer(1, 5*mm))

# ═══════════════════════════════════════════════════════════════
# DRUG OF CHOICE SUMMARY ROW
# ═══════════════════════════════════════════════════════════════
doc_cols = [44*mm, 44*mm, 44*mm, 44*mm]
doc_summary = Table([
    [
        Paragraph("<b>1st Line (Oral)</b>", bold_body),
        Paragraph("<b>Alternative (Oral)</b>", bold_body),
        Paragraph("<b>Severe / Immunocomp.</b>", bold_body),
        Paragraph("<b>ACV-Resistant</b>", bold_body),
    ],
    [
        Paragraph("Valacyclovir\n<font color='#0F7B8C'>1 g every 8 h</font>", body),
        Paragraph("Famciclovir\n<font color='#0F7B8C'>500 mg every 8 h</font>", body),
        Paragraph("Acyclovir IV\n<font color='#C0392B'>10 mg/kg every 8 h</font>", body),
        Paragraph("Foscarnet IV\n<font color='#C0392B'>40 mg/kg every 8 h</font>", body),
    ],
], colWidths=doc_cols)
doc_summary.setStyle(TableStyle([
    ("BACKGROUND",    (0,0), (-1,0), TEAL),
    ("TEXTCOLOR",     (0,0), (-1,0), WHITE),
    ("FONTNAME",      (0,0), (-1,0), "Helvetica-Bold"),
    ("BACKGROUND",    (0,1), (1,1), LIGHT_TEAL),
    ("BACKGROUND",    (2,1), (3,1), LIGHT_RED),
    ("GRID",          (0,0), (-1,-1), 0.4, MID_GREY),
    ("ALIGN",         (0,0), (-1,-1), "CENTER"),
    ("VALIGN",        (0,0), (-1,-1), "MIDDLE"),
    ("TOPPADDING",    (0,0), (-1,-1), 6),
    ("BOTTOMPADDING", (0,0), (-1,-1), 6),
    ("FONTSIZE",      (0,1), (-1,1), 8.5),
]))
story.append(doc_summary)
story.append(Spacer(1, 4*mm))

# ═══════════════════════════════════════════════════════════════
# SECTION 1: VARICELLA (Chickenpox)
# ═══════════════════════════════════════════════════════════════
story.append(KeepTogether([
    banner("① VARICELLA (Primary Chickenpox)  —  No Encephalitis", NAVY),
    Spacer(1, 2*mm),
]))

# Varicella table
vzv_col = [42*mm, 62*mm, 30*mm, 42*mm]
vzv_data = [
    ["Patient Group", "Preferred Regimen", "Duration", "Notes"],
    ["Neonate",
     "Acyclovir 10 mg/kg IV\nor 500 mg/m² IV every 8 h",
     "10 days",
     "IV only"],
    ["Child 2-18 y\n(Immunocompetent)",
     "Symptomatic Rx alone (preferred)\nOR Acyclovir 20 mg/kg orally 4×/day\n  (max 3,200 mg/day)\nOR Valacyclovir 20 mg/kg orally every 8 h\n  (max 3 g/day)",
     "5 days\nif treating",
     "Routine antiviral NOT recommended in healthy children;\nstart within 24 h of rash only"],
    ["Adolescent ≥40 kg\n& Adult (Immunocompetent)",
     "Valacyclovir 1 g orally every 8 h  ★\nOR Famciclovir 500 mg orally every 8 h\nOR Acyclovir 800 mg orally 5×/day",
     "7 days",
     "Start within 24 h of rash. Valacyclovir preferred."],
    ["Pregnancy\n(Uncomplicated)",
     "Symptomatic treatment — routine antiviral\nNOT recommended\nComplications (e.g. pneumonia): IV Acyclovir",
     "—",
     "Specialist input advised"],
    ["Mild varicella or\nMild immunocompromise",
     "Valacyclovir 1 g orally every 8 h\nOR Famciclovir 500 mg orally every 8 h\nOR Acyclovir 800 mg orally 5×/day",
     "7-10 days",
     "Oral acceptable if stable"],
    ["Severe varicella or\nSevere immunocompromise",
     "Acyclovir 10 mg/kg IV every 8 h",
     "7-10 days",
     "Switch to oral once\nno new lesions + stable"],
    ["Acyclovir-resistant\n(Advanced AIDS / transplant)",
     "Foscarnet 40 mg/kg IV every 8 h",
     "Until all lesions crusted (~2 weeks)",
     "Cross-resistant to ganciclovir, valacyclovir, famciclovir"],
]

vzv_styled = []
for i, row in enumerate(vzv_data):
    styled = []
    for j, cell in enumerate(row):
        if i == 0:
            styled.append(Paragraph(cell, ParagraphStyle("th", fontSize=9,
                fontName="Helvetica-Bold", textColor=WHITE, leading=12)))
        else:
            styled.append(Paragraph(cell, small))
    vzv_styled.append(styled)

t1 = Table(vzv_styled, colWidths=vzv_col, repeatRows=1)
t1.setStyle(TableStyle([
    ("BACKGROUND",    (0,0), (-1,0), NAVY),
    ("ROWBACKGROUNDS",(0,1), (-1,-1), [WHITE, LIGHT_GREY]),
    ("GRID",          (0,0), (-1,-1), 0.3, MID_GREY),
    ("LINEBELOW",     (0,0), (-1,0), 1.2, NAVY),
    ("VALIGN",        (0,0), (-1,-1), "TOP"),
    ("TOPPADDING",    (0,0), (-1,-1), 5),
    ("BOTTOMPADDING", (0,0), (-1,-1), 5),
    ("LEFTPADDING",   (0,0), (-1,-1), 5),
    ("RIGHTPADDING",  (0,0), (-1,-1), 5),
    # Highlight preferred row
    ("BACKGROUND",    (0,4), (-1,4), LIGHT_TEAL),
]))
story.append(t1)
story.append(Spacer(1, 2*mm))
story.append(note_box(
    "★ Preferred: Valacyclovir & Famciclovir are preferred over Acyclovir because VZV is ~10× less sensitive "
    "to acyclovir than HSV, and prodrugs achieve higher blood levels with simpler TID dosing. "
    "(Fitzpatrick's Dermatology, p. 3082)",
    bg=LIGHT_TEAL, border=TEAL))
story.append(Spacer(1, 4*mm))

# ═══════════════════════════════════════════════════════════════
# SECTION 2: HERPES ZOSTER
# ═══════════════════════════════════════════════════════════════
story.append(KeepTogether([
    banner("② HERPES ZOSTER (Shingles)  —  No Encephalitis", TEAL),
    Spacer(1, 2*mm),
]))

story.append(note_box(
    "⏱  TIMING: Start antiviral within 72 h of rash onset. May still benefit if started >72 h when: "
    "new vesicles forming | ophthalmic/cranial nerve involvement | age >50 | immunocompromised.",
    bg=LIGHT_AMB, border=AMBER,
    style=S("WarnSmall", fontSize=8, leading=11, textColor=HexColor("#7D4D00"),
            fontName="Helvetica-Bold")))
story.append(Spacer(1, 2*mm))

hz_col = [42*mm, 62*mm, 28*mm, 44*mm]
hz_data = [
    ["Patient Group", "Preferred Regimen", "Duration", "Notes"],
    ["Immunocompetent\nAdolescent & Adult",
     "Valacyclovir 1 g orally every 8 h  ★\nOR Famciclovir 500 mg orally every 8 h\nOR Acyclovir 800 mg orally 5×/day",
     "7 days",
     "All 3 equivalent in efficacy; Val/Fam preferred for easier dosing & pharmacokinetics"],
    ["Patients <50 y (mild zoster)",
     "Antiviral often NOT required unless:\n• Moderate-severe pain\n• Eye/cranial nerve involvement\n• Immunocompromise",
     "7 days\nif treating",
     "Clinical judgement; young patients usually have minimal PHN risk"],
    ["Mild immunocompromise\n(localized zoster)",
     "Valacyclovir 1 g orally every 8 h\nOR Famciclovir 500 mg orally every 8 h",
     "7-10 days",
     "Oral acceptable if no visceral spread"],
    ["Severe immunocompromise",
     "Acyclovir 10 mg/kg IV every 8 h\nor 500 mg/m² IV every 8 h",
     "7-10 days\nor until all lesions crusted",
     "Switch to oral Val/Fam once new lesions cease and patient stable"],
    ["Ophthalmic Zoster\n(Zoster ophthalmicus)",
     "Valacyclovir 1 g orally every 8 h  ★\nOR Famciclovir 500 mg orally every 8 h",
     "7 days",
     "Ophthalmology referral mandatory. Treat even if >72 h post-onset."],
    ["Ramsay Hunt Syndrome\n(CN VII reactivation)",
     "Valacyclovir 1 g orally every 8 h\n± Prednisone 1 mg/kg/day (selective use)",
     "7 days",
     "Combination with corticosteroids: specialist decision"],
    ["Acyclovir-resistant VZV",
     "Foscarnet 40 mg/kg IV every 8 h",
     "~2 weeks or until healed",
     "Usually only in AIDS/transplant patients"],
]

hz_styled = []
for i, row in enumerate(hz_data):
    styled = []
    for j, cell in enumerate(row):
        if i == 0:
            styled.append(Paragraph(cell, ParagraphStyle("th2", fontSize=9,
                fontName="Helvetica-Bold", textColor=WHITE, leading=12)))
        else:
            styled.append(Paragraph(cell, small))
    hz_styled.append(styled)

t2 = Table(hz_styled, colWidths=hz_col, repeatRows=1)
t2.setStyle(TableStyle([
    ("BACKGROUND",    (0,0), (-1,0), TEAL),
    ("ROWBACKGROUNDS",(0,1), (-1,-1), [WHITE, LIGHT_GREY]),
    ("GRID",          (0,0), (-1,-1), 0.3, MID_GREY),
    ("LINEBELOW",     (0,0), (-1,0), 1.2, TEAL),
    ("VALIGN",        (0,0), (-1,-1), "TOP"),
    ("TOPPADDING",    (0,0), (-1,-1), 5),
    ("BOTTOMPADDING", (0,0), (-1,-1), 5),
    ("LEFTPADDING",   (0,0), (-1,-1), 5),
    ("RIGHTPADDING",  (0,0), (-1,-1), 5),
    ("BACKGROUND",    (0,1), (-1,1), LIGHT_TEAL),
]))
story.append(t2)
story.append(Spacer(1, 4*mm))

# ═══════════════════════════════════════════════════════════════
# SECTION 3: ADJUNCT & POST-HERPETIC NEURALGIA
# ═══════════════════════════════════════════════════════════════
story.append(KeepTogether([
    banner("③ ADJUNCT THERAPY & POST-HERPETIC NEURALGIA (PHN)", HexColor("#5D4037")),
    Spacer(1, 2*mm),
]))

adj_col = [50*mm, 90*mm, 36*mm]
adj_data = [
    ["Agent / Indication", "Dosing", "Notes"],
    ["Corticosteroids\n(Acute zoster pain, >50 y)",
     "Prednisone 60 mg/day → taper over 21 days\n(combined with acyclovir/valacyclovir)",
     "Reduces acute pain & improves QoL.\nDoes NOT prevent PHN.\nUse selectively; avoid if contraindicated."],
    ["Gabapentin\n(PHN)",
     "Start: 300 mg at bedtime\nTitrate to: 1,200 mg three times daily (max)",
     "First-line for PHN neuropathic pain"],
    ["Pregabalin\n(PHN)",
     "Start: 75 mg at bedtime\nTitrate to: 300 mg twice daily (max)",
     "Renal dose adjustment required"],
    ["Nortriptyline\n(PHN)",
     "Start: 25 mg at bedtime\nTitrate to: 150 mg daily (max)",
     "TCA; monitor for anticholinergic effects"],
    ["Lidocaine Patch 5%\n(PHN - localized)",
     "Apply to affected area ×12 h on/12 h off",
     "Useful for allodynia"],
    ["Topical Capsaicin\n(PHN)",
     "0.025-0.075% cream 3-4×/day",
     "Initial burning limits tolerability in ~1/3 patients"],
    ["Opioid Analgesics\n(Acute zoster pain)",
     "As per pain ladder — oxycodone effective\nfor moderate-severe acute pain",
     "Use cautiously; titrate to pain <3/10.\nShort-term use preferred."],
    ["Topical Therapy\n(Acute zoster, skin only)",
     "Cool compresses, calamine lotion,\ncornstarch or baking soda",
     "Topical antivirals NOT effective or recommended"],
]

adj_styled = []
for i, row in enumerate(adj_data):
    styled = []
    for j, cell in enumerate(row):
        if i == 0:
            styled.append(Paragraph(cell, ParagraphStyle("th3", fontSize=9,
                fontName="Helvetica-Bold", textColor=WHITE, leading=12)))
        else:
            styled.append(Paragraph(cell, small))
    adj_styled.append(styled)

t3 = Table(adj_styled, colWidths=adj_col, repeatRows=1)
t3.setStyle(TableStyle([
    ("BACKGROUND",    (0,0), (-1,0), HexColor("#5D4037")),
    ("ROWBACKGROUNDS",(0,1), (-1,-1), [WHITE, LIGHT_GREY]),
    ("GRID",          (0,0), (-1,-1), 0.3, MID_GREY),
    ("LINEBELOW",     (0,0), (-1,0), 1.2, HexColor("#5D4037")),
    ("VALIGN",        (0,0), (-1,-1), "TOP"),
    ("TOPPADDING",    (0,0), (-1,-1), 5),
    ("BOTTOMPADDING", (0,0), (-1,-1), 5),
    ("LEFTPADDING",   (0,0), (-1,-1), 5),
    ("RIGHTPADDING",  (0,0), (-1,-1), 5),
]))
story.append(t3)
story.append(Spacer(1, 4*mm))

# ═══════════════════════════════════════════════════════════════
# SECTION 4: RENAL DOSE ADJUSTMENTS (side by side)
# ═══════════════════════════════════════════════════════════════
story.append(KeepTogether([
    banner("④ RENAL DOSE ADJUSTMENTS", HexColor("#1A6B3A")),
    Spacer(1, 2*mm),
]))

renal_col = [48*mm, 30*mm, 32*mm, 32*mm, 34*mm]
renal_data = [
    ["Drug & Indication", "Normal", "CrCl 25-50", "CrCl 10-25", "CrCl <10 / HD"],
    ["Valacyclovir\n(Zoster)", "1 g q8h", "1 g q12h", "1 g q24h", "500 mg q24h"],
    ["Famciclovir\n(Zoster)", "500 mg q8h", "500 mg q12h", "500 mg q24h", "250 mg q24h\n(post-HD)"],
    ["Acyclovir oral\n(Zoster - 800 mg)", "800 mg 5×/day", "800 mg q8h", "800 mg q12h", "800 mg q24h"],
    ["Acyclovir IV\n(Severe)", "10 mg/kg q8h", "10 mg/kg q12h", "10 mg/kg q24h", "5 mg/kg q24h\n(post-HD)"],
    ["Foscarnet IV\n(Resistant)", "40 mg/kg q8h", "Dose adjust per\nrenal protocol", "Dose adjust", "Avoid / specialist"],
]

renal_styled = []
for i, row in enumerate(renal_data):
    styled = []
    for j, cell in enumerate(row):
        if i == 0:
            styled.append(Paragraph(cell, ParagraphStyle("th4", fontSize=8.5,
                fontName="Helvetica-Bold", textColor=WHITE, leading=11)))
        else:
            styled.append(Paragraph(cell, small))
    renal_styled.append(styled)

t4 = Table(renal_styled, colWidths=renal_col, repeatRows=1)
t4.setStyle(TableStyle([
    ("BACKGROUND",    (0,0), (-1,0), HexColor("#1A6B3A")),
    ("ROWBACKGROUNDS",(0,1), (-1,-1), [WHITE, LIGHT_GRN]),
    ("GRID",          (0,0), (-1,-1), 0.3, MID_GREY),
    ("LINEBELOW",     (0,0), (-1,0), 1.2, HexColor("#1A6B3A")),
    ("VALIGN",        (0,0), (-1,-1), "TOP"),
    ("ALIGN",         (1,0), (-1,-1), "CENTER"),
    ("TOPPADDING",    (0,0), (-1,-1), 5),
    ("BOTTOMPADDING", (0,0), (-1,-1), 5),
    ("LEFTPADDING",   (0,0), (-1,-1), 5),
    ("RIGHTPADDING",  (0,0), (-1,-1), 5),
]))
story.append(t4)
story.append(Spacer(1, 4*mm))

# ═══════════════════════════════════════════════════════════════
# SECTION 5: CONTRAINDICATIONS & CAUTIONS (2-col layout)
# ═══════════════════════════════════════════════════════════════
story.append(KeepTogether([
    banner("⑤ KEY CAUTIONS & WHEN TO ESCALATE TO IV", RED_WARN),
    Spacer(1, 2*mm),
]))

cc_left = [
    ["⚠ Acyclovir / Valacyclovir Toxicity"],
    ["• Renal impairment — dose reduce (table above)"],
    ["• Neurotoxicity: tremor, confusion, hallucinations"],
    ["• Ensure adequate hydration with IV therapy"],
    ["• Neutropenia (rare)"],
    [""],
    ["⚠ Foscarnet Toxicity"],
    ["• Nephrotoxicity — monitor creatinine & electrolytes"],
    ["• Hypocalcaemia, hypomagnesaemia"],
    ["• Penile/vulvar ulcers with inadequate hydration"],
]

cc_right = [
    ["🔴 Escalate to IV Acyclovir if:"],
    ["• Varicella or zoster pneumonitis"],
    ["• Visceral dissemination (hepatitis, thrombocytopenia)"],
    ["• Severe immunocompromise (CD4 <200, transplant)"],
    ["• Neonatal varicella"],
    ["• Any ophthalmologic emergency (ARN, uveitis)"],
    ["• CNS symptoms → urgent MRI + IV Acyclovir"],
    [""],
    ["🔴 Switch to Foscarnet if:"],
    ["• Failure to respond to acyclovir/valacyclovir"],
    ["• Documented acyclovir-resistant VZV (AIDS/transplant)"],
]

def mini_table(data, bg, border_color):
    rows = [[Paragraph(r[0],
        ParagraphStyle("ct", fontSize=8, leading=11,
            fontName="Helvetica-Bold" if r[0].startswith("⚠") or r[0].startswith("🔴") else "Helvetica",
            textColor=border_color if r[0].startswith("⚠") or r[0].startswith("🔴") else DARK_GREY))]
        for r in data]
    t = Table(rows, colWidths=[85*mm])
    t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,-1), bg),
        ("LINEBEFORE",    (0,0), (-1,-1), 3, border_color),
        ("TOPPADDING",    (0,0), (-1,-1), 2),
        ("BOTTOMPADDING", (0,0), (-1,-1), 2),
        ("LEFTPADDING",   (0,0), (-1,-1), 8),
    ]))
    return t

two_col = Table([
    [mini_table(cc_left, LIGHT_AMB, AMBER), mini_table(cc_right, LIGHT_RED, RED_WARN)]
], colWidths=[91*mm, 91*mm])
two_col.setStyle(TableStyle([
    ("VALIGN",  (0,0), (-1,-1), "TOP"),
    ("LEFTPADDING",  (0,0), (-1,-1), 0),
    ("RIGHTPADDING", (0,0), (-1,-1), 0),
    ("TOPPADDING",   (0,0), (-1,-1), 0),
    ("BOTTOMPADDING",(0,0), (-1,-1), 0),
    ("INNERGRID",    (0,0), (-1,-1), 0, WHITE),
]))
story.append(two_col)
story.append(Spacer(1, 4*mm))

# ═══════════════════════════════════════════════════════════════
# PREVENTION REMINDER
# ═══════════════════════════════════════════════════════════════
story.append(note_box(
    "💉  PREVENTION: Varicella vaccine (2 doses SC) for susceptible children ≥1 y and adults. "
    "Recombinant zoster vaccine (RZV / Shingrix) 2 doses IM — preferred for adults ≥50 y (>90% efficacy). "
    "Post-exposure: VZIG within 10 days for susceptible immunocompromised contacts.",
    bg=LIGHT_GRN, border=GREEN,
    style=S("GreenNote2", fontSize=8, leading=11, textColor=HexColor("#1A4A2E"),
            fontName="Helvetica")))
story.append(Spacer(1, 3*mm))

# ═══════════════════════════════════════════════════════════════
# FOOTER
# ═══════════════════════════════════════════════════════════════
footer_data = [[
    Paragraph(
        "Sources: Fitzpatrick's Dermatology Vol.1-2, Tables 165-3 & 165-4 (pp. 3081-3082)  •  "
        "Goldman-Cecil Medicine, Ch. 346  •  Harrison's Principles of Internal Medicine 22E, Ch. 328  •  "
        "Sherris & Ryan's Medical Microbiology 8E",
        footnote),
    Paragraph(
        "⚕ This reference is for educational use only. Verify doses against current formulary. "
        "This does not replace clinical judgment.",
        ParagraphStyle("FW", fontSize=7, leading=9, textColor=RED_WARN,
                       fontName="Helvetica-Bold", alignment=TA_RIGHT)),
]]
footer_tbl = Table(footer_data, colWidths=[118*mm, 64*mm])
footer_tbl.setStyle(TableStyle([
    ("VALIGN",  (0,0), (-1,-1), "TOP"),
    ("TOPPADDING",   (0,0), (-1,-1), 4),
    ("BOTTOMPADDING",(0,0), (-1,-1), 4),
    ("LINEABOVE", (0,0), (-1,0), 0.5, MID_GREY),
]))
story.append(footer_tbl)

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

cd /home/daytona/workspace/vzv-pdf && python generate_vzv_pdf.py

Running Command

ls -lh /home/daytona/workspace/vzv-pdf/

Your PDF is ready. Here's the download link:

VZV Treatment Quick Reference

PDF Document · PDF


The single-page A4 reference includes 5 color-coded sections:
SectionColorContents
① Varicella (Chickenpox)NavyDosing by patient group (neonate → adult → immunocompromised)
② Herpes Zoster (Shingles)TealOral/IV regimens, timing rules, ophthalmic & Ramsay Hunt
③ Adjunct & PHN therapyBrownCorticosteroids, gabapentin, pregabalin, nortriptyline, capsaicin
④ Renal dose adjustmentsGreenValacyclovir, famciclovir, acyclovir (oral + IV), foscarnet
⑤ Cautions & Escalation triggersRed/AmberWhen to switch to IV, foscarnet, toxicity watch points
  • A top summary bar gives the at-a-glance drug-of-choice for each scenario
  • A prevention reminder for vaccines and VZIG is included at the bottom
  • Source citations (Fitzpatrick's, Goldman-Cecil, Harrison's 22E) and a clinical disclaimer are in the footer

Patient counseling about this antiviral therapy and varicella zoster infection

Asking for Preferences
Finding Sources
Finding Sources
Reading File
Reading File
Reading File
Writing File

~/vzv-pdf/generate_patient_counseling.py

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

OUTPUT = "/home/daytona/workspace/vzv-pdf/VZV_Patient_Counseling.pdf"

# ── Colour palette ────────────────────────────────────────────────
NAVY      = HexColor("#1B3A6B")
TEAL      = HexColor("#0D7A8A")
LIGHT_TEAL= HexColor("#E5F4F6")
AMBER     = HexColor("#E8960A")
LIGHT_AMB = HexColor("#FFF6E0")
RED       = HexColor("#C0392B")
LIGHT_RED = HexColor("#FDECEA")
GREEN     = HexColor("#1E8449")
LIGHT_GRN = HexColor("#EAF7EF")
PURPLE    = HexColor("#6C3483")
LIGHT_PUR = HexColor("#F5EEF8")
LIGHT_GREY= HexColor("#F4F6F8")
MID_GREY  = HexColor("#BDC3C7")
DARK_GREY = HexColor("#444444")
WHITE     = colors.white
BLACK     = colors.black

W, H = A4

doc = SimpleDocTemplate(
    OUTPUT,
    pagesize=A4,
    leftMargin=15*mm, rightMargin=15*mm,
    topMargin=12*mm, bottomMargin=12*mm,
)

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

title_s = S("Title", fontSize=22, leading=27, fontName="Helvetica-Bold",
            textColor=WHITE, alignment=TA_CENTER)
subtitle_s = S("Sub", fontSize=10, leading=14, fontName="Helvetica",
               textColor=HexColor("#C8E6F0"), alignment=TA_CENTER)
date_s = S("Date", fontSize=8, leading=10, fontName="Helvetica",
           textColor=HexColor("#90C4D4"), alignment=TA_CENTER)

sec_title = S("SecTitle", fontSize=13, leading=17, fontName="Helvetica-Bold",
              textColor=WHITE)
sec_title_dark = S("SecTitleDark", fontSize=12, leading=15,
                   fontName="Helvetica-Bold", textColor=NAVY)

body = S("Body", fontSize=9.5, leading=14, fontName="Helvetica",
         textColor=DARK_GREY, alignment=TA_JUSTIFY)
body_bold = S("BodyBold", fontSize=9.5, leading=14,
              fontName="Helvetica-Bold", textColor=BLACK)
body_small = S("BodySmall", fontSize=8.5, leading=12,
               fontName="Helvetica", textColor=DARK_GREY)
body_small_bold = S("BodySmallBold", fontSize=8.5, leading=12,
                    fontName="Helvetica-Bold", textColor=BLACK)

highlight = S("Highlight", fontSize=9.5, leading=14,
              fontName="Helvetica-Bold", textColor=TEAL)
warn_s = S("Warn", fontSize=9.5, leading=14,
           fontName="Helvetica-Bold", textColor=RED)
green_s = S("Green", fontSize=9.5, leading=14,
            fontName="Helvetica-Bold", textColor=GREEN)
footnote_s = S("Foot", fontSize=7.5, leading=10,
               fontName="Helvetica-Oblique", textColor=MID_GREY)

# ── Helpers ───────────────────────────────────────────────────────
CW = W - 30*mm  # content width

def section_banner(icon, title, color=NAVY):
    text = f"{icon}  {title}"
    t = Table([[Paragraph(text, sec_title)]],
              colWidths=[CW])
    t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,-1), color),
        ("TOPPADDING",    (0,0), (-1,-1), 7),
        ("BOTTOMPADDING", (0,0), (-1,-1), 7),
        ("LEFTPADDING",   (0,0), (-1,-1), 10),
        ("RIGHTPADDING",  (0,0), (-1,-1), 10),
    ]))
    return t

def colored_box(paragraphs, bg, border, min_height=None):
    rows = [[p] for p in paragraphs]
    flat = [[Paragraph("", body)]] if not rows else rows
    # stack multiple paragraphs as one cell via nested table
    inner = Table([[p] for p in paragraphs], colWidths=[CW - 20])
    inner.setStyle(TableStyle([
        ("TOPPADDING",    (0,0), (-1,-1), 1),
        ("BOTTOMPADDING", (0,0), (-1,-1), 2),
        ("LEFTPADDING",   (0,0), (-1,-1), 0),
        ("RIGHTPADDING",  (0,0), (-1,-1), 0),
    ]))
    outer = Table([[inner]], colWidths=[CW])
    style = [
        ("BACKGROUND",    (0,0), (-1,-1), bg),
        ("LINEBEFORE",    (0,0), (-1,-1), 4, border),
        ("TOPPADDING",    (0,0), (-1,-1), 7),
        ("BOTTOMPADDING", (0,0), (-1,-1), 7),
        ("LEFTPADDING",   (0,0), (-1,-1), 12),
        ("RIGHTPADDING",  (0,0), (-1,-1), 10),
    ]
    if min_height:
        style.append(("MINROWHEIGHT", (0,0), (-1,-1), min_height))
    outer.setStyle(TableStyle(style))
    return outer

def bullet_box(items, bg, border, bullet_color=None):
    """items = list of (bold_label, text) or just strings"""
    paras = []
    for item in items:
        if isinstance(item, tuple):
            label, text = item
            paras.append(Paragraph(f"<b>{label}</b>  {text}", body_small))
        else:
            paras.append(Paragraph(f"• {item}", body_small))
    return colored_box(paras, bg, border)

def two_col(left_flowables, right_flowables, left_w=None, right_w=None):
    lw = left_w or (CW / 2 - 2*mm)
    rw = right_w or (CW / 2 - 2*mm)
    t = Table([[left_flowables, right_flowables]],
              colWidths=[lw + 2*mm, rw + 2*mm])
    t.setStyle(TableStyle([
        ("VALIGN",        (0,0), (-1,-1), "TOP"),
        ("LEFTPADDING",   (0,0), (-1,-1), 0),
        ("RIGHTPADDING",  (0,0), (-1,-1), 0),
        ("TOPPADDING",    (0,0), (-1,-1), 0),
        ("BOTTOMPADDING", (0,0), (-1,-1), 0),
    ]))
    return t

def pill_table(data, col_widths, header_bg, alt_bg):
    """Simple styled table"""
    styled = []
    for i, row in enumerate(data):
        if i == 0:
            styled.append([Paragraph(c, S(f"th{i}", fontSize=8.5,
                fontName="Helvetica-Bold", textColor=WHITE, leading=11))
                for c in row])
        else:
            styled.append([Paragraph(c, body_small) for c in row])
    t = Table(styled, colWidths=col_widths, repeatRows=1)
    t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,0), header_bg),
        ("ROWBACKGROUNDS",(0,1), (-1,-1), [WHITE, alt_bg]),
        ("GRID",          (0,0), (-1,-1), 0.3, MID_GREY),
        ("LINEBELOW",     (0,0), (-1,0), 1.2, header_bg),
        ("VALIGN",        (0,0), (-1,-1), "TOP"),
        ("TOPPADDING",    (0,0), (-1,-1), 5),
        ("BOTTOMPADDING", (0,0), (-1,-1), 5),
        ("LEFTPADDING",   (0,0), (-1,-1), 6),
        ("RIGHTPADDING",  (0,0), (-1,-1), 6),
        ("ALIGN",         (0,0), (-1,-1), "LEFT"),
    ]))
    return t

# ═══════════════════════════════════════════════════════════════
story = []

# ── HEADER ───────────────────────────────────────────────────────
header = Table([
    [Paragraph("Patient Information Sheet", subtitle_s)],
    [Paragraph("Chickenpox (Varicella) & Your Antiviral Medication", title_s)],
    [Paragraph(
        f"Prepared by your healthcare provider  •  {datetime.date.today().strftime('%B %d, %Y')}",
        date_s)],
], colWidths=[CW])
header.setStyle(TableStyle([
    ("BACKGROUND",    (0,0), (-1,-1), NAVY),
    ("TOPPADDING",    (0,0), (-1,-1), 8),
    ("BOTTOMPADDING", (0,0), (-1,-1), 8),
    ("LEFTPADDING",   (0,0), (-1,-1), 10),
    ("RIGHTPADDING",  (0,0), (-1,-1), 10),
]))
story.append(header)
story.append(Spacer(1, 5*mm))

# ── INTRO BOX ────────────────────────────────────────────────────
story.append(colored_box([
    Paragraph(
        "You have been diagnosed with <b>chickenpox (varicella)</b> and prescribed an antiviral medicine. "
        "This sheet explains what chickenpox is, how your medicine works, how to take it correctly, "
        "what side effects to watch for, and how to protect the people around you.",
        body),
], LIGHT_TEAL, TEAL))
story.append(Spacer(1, 4*mm))

# ── SECTION 1: WHAT IS CHICKENPOX ───────────────────────────────
story.append(section_banner("🦠", "What is Chickenpox?", NAVY))
story.append(Spacer(1, 2*mm))

# two-col layout: text left, quick facts right
left_paras = Table([
    [Paragraph(
        "Chickenpox is caused by the <b>Varicella-Zoster Virus (VZV)</b>. "
        "It spreads very easily from person to person through the air (coughing, sneezing) "
        "and by touching the fluid in blisters.", body)],
    [Spacer(1, 3*mm)],
    [Paragraph(
        "The infection usually starts with <b>fever, tiredness, and loss of appetite</b> for 1-2 days, "
        "followed by a <b>widespread, itchy, blister-like rash</b>. "
        "The rash appears first on the head, then spreads to the chest, back, and limbs.", body)],
    [Spacer(1, 3*mm)],
    [Paragraph(
        "Blisters go through stages: <b>red spots → fluid-filled blisters → cloudy pustules → crusts</b>. "
        "You may have 200 to 500 spots at different stages at the same time. "
        "New spots usually stop appearing after about 5 days, and all crusts form within 2 weeks.", body)],
], colWidths=[90*mm])
left_paras.setStyle(TableStyle([
    ("TOPPADDING", (0,0), (-1,-1), 1), ("BOTTOMPADDING", (0,0), (-1,-1), 1),
    ("LEFTPADDING", (0,0), (-1,-1), 0), ("RIGHTPADDING", (0,0), (-1,-1), 3),
]))

facts = [
    ("Incubation", "10-21 days after exposure\n(usually ~2 weeks)"),
    ("Contagious", "From 1-2 days BEFORE rash\nuntil ALL blisters have crusted"),
    ("Rash duration", "New spots: up to 5 days\nFull crusting: ~2 weeks"),
    ("Spots", "200-500 blisters, different\nstages at the same time"),
]
fact_rows = [[Paragraph("Quick Facts", S("FH", fontSize=8.5, fontName="Helvetica-Bold",
                textColor=WHITE, leading=11))]]
for label, val in facts:
    fact_rows.append([
        Table([[Paragraph(f"<b>{label}</b>", body_small)],
               [Paragraph(val, body_small)]],
              colWidths=[78*mm])
    ])
fact_table = Table(fact_rows, colWidths=[80*mm])
fact_table.setStyle(TableStyle([
    ("BACKGROUND",    (0,0), (-1,0), TEAL),
    ("ROWBACKGROUNDS",(0,1), (-1,-1), [LIGHT_TEAL, WHITE]),
    ("GRID",          (0,0), (-1,-1), 0.3, MID_GREY),
    ("TOPPADDING",    (0,0), (-1,-1), 5),
    ("BOTTOMPADDING", (0,0), (-1,-1), 5),
    ("LEFTPADDING",   (0,0), (-1,-1), 6),
    ("VALIGN",        (0,0), (-1,-1), "TOP"),
]))

story.append(two_col(left_paras, fact_table, left_w=94*mm, right_w=82*mm))
story.append(Spacer(1, 4*mm))

# ── SECTION 2: YOUR MEDICINE ──────────────────────────────────────
story.append(section_banner("💊", "Your Antiviral Medicine", TEAL))
story.append(Spacer(1, 2*mm))

story.append(colored_box([
    Paragraph(
        "Your doctor has prescribed an antiviral medicine — most likely <b>Valacyclovir</b> or <b>Acyclovir</b>. "
        "These medicines do <b>not cure</b> chickenpox, but they <b>shorten</b> how long you are sick, "
        "reduce the number of blisters, and lower the chance of serious complications.",
        body),
], LIGHT_TEAL, TEAL))
story.append(Spacer(1, 3*mm))

med_data = [
    ["Medicine", "Usual Adult Dose", "How Often", "Duration", "Key Point"],
    ["Valacyclovir\n(Valtrex)\n— PREFERRED",
     "1,000 mg (1 g)",
     "3 times a day\n(every 8 hours)",
     "7 days",
     "Take with or without food.\nHigher levels in blood than acyclovir.\nEasier dosing schedule."],
    ["Famciclovir\n(Famvir)\n— PREFERRED",
     "500 mg",
     "3 times a day\n(every 8 hours)",
     "7 days",
     "Take with or without food.\nEqually effective to valacyclovir."],
    ["Acyclovir\n(Zovirax)\n— Alternative",
     "800 mg",
     "5 times a day\n(every 4-5 hours\nwhile awake)",
     "7 days",
     "Take with plenty of water.\nMore doses per day than the others."],
]
story.append(pill_table(med_data,
    [32*mm, 26*mm, 28*mm, 20*mm, 74*mm],
    TEAL, LIGHT_TEAL))
story.append(Spacer(1, 2*mm))

story.append(colored_box([
    Paragraph(
        "⏰ <b>START EARLY — THIS IS IMPORTANT.</b>  Your medicine works best when started "
        "<b>within 24 hours</b> of your rash first appearing. "
        "The sooner you take it, the better it works.",
        S("EarlyWarn", fontSize=10, leading=14, fontName="Helvetica-Bold",
          textColor=HexColor("#7D4D00"), alignment=TA_LEFT)),
], LIGHT_AMB, AMBER))
story.append(Spacer(1, 4*mm))

# ── SECTION 3: HOW TO TAKE YOUR MEDICINE ──────────────────────────
story.append(section_banner("📋", "How to Take Your Medicine Correctly", HexColor("#1A5276")))
story.append(Spacer(1, 2*mm))

dos = [
    "Take <b>every dose</b> at evenly spaced times throughout the day (e.g., 8am / 4pm / midnight for 3x/day).",
    "Take with a <b>full glass of water</b> (250 mL / 8 oz) each time.",
    "Can be taken <b>with or without food</b> — if it upsets your stomach, take it with a meal.",
    "<b>Finish the full course</b> — do not stop early even if you feel better, as the virus may still be active.",
    "If you <b>miss a dose</b>, take it as soon as you remember — unless it is nearly time for your next dose. Never double up.",
    "<b>Drink plenty of fluids</b> throughout the day. Good hydration protects your kidneys while taking this medicine.",
]
donts = [
    "<b>Do NOT take aspirin or ibuprofen</b> (especially in children) — it can cause a rare but serious condition called Reye syndrome. Use paracetamol (acetaminophen) for fever or pain.",
    "<b>Do NOT use topical (skin) antiviral cream</b> — it does not work for chickenpox.",
    "<b>Do NOT scratch</b> the blisters — it can cause scarring and bacterial infection.",
    "<b>Do NOT share your medicine</b> with anyone else.",
    "<b>Avoid alcohol</b> while taking antivirals — it can worsen side effects like dizziness and nausea.",
]

do_box = colored_box(
    [Paragraph("✅  DO:", S("DH", fontSize=9.5, fontName="Helvetica-Bold",
                textColor=GREEN, leading=14))] +
    [Paragraph(f"• {d}", body_small) for d in dos],
    LIGHT_GRN, GREEN)

dont_box = colored_box(
    [Paragraph("❌  DO NOT:", S("DNH", fontSize=9.5, fontName="Helvetica-Bold",
                textColor=RED, leading=14))] +
    [Paragraph(f"• {d}", body_small) for d in donts],
    LIGHT_RED, RED)

story.append(two_col(do_box, dont_box, left_w=88*mm, right_w=88*mm))
story.append(Spacer(1, 4*mm))

# ── SECTION 4: SIDE EFFECTS ───────────────────────────────────────
story.append(section_banner("⚠️", "Possible Side Effects", HexColor("#7D6608")))
story.append(Spacer(1, 2*mm))

se_data = [
    ["Side Effect", "How Common", "What to Do"],
    ["Nausea, upset stomach, diarrhoea",
     "Common\n(mild, usually passes)",
     "Take with food. Stay hydrated.\nTell your doctor if severe."],
    ["Headache",
     "Common",
     "Paracetamol for relief.\nUsually mild and temporary."],
    ["Dizziness",
     "Uncommon",
     "Avoid driving until you know\nhow the medicine affects you."],
    ["Decreased urination, dark urine,\nback/flank pain (kidney strain)",
     "Rare — more likely if\ndehydrated or kidney disease",
     "Drink more water immediately.\nContact your doctor or go to\nan emergency department."],
    ["Confusion, tremor, agitation,\nhallucinations (CNS effects)",
     "Rare — more likely\nwith kidney problems",
     "STOP the medicine and seek\nmedical care immediately."],
    ["Rash or allergic reaction\n(hives, swelling, difficulty breathing)",
     "Very rare",
     "STOP medicine. Call emergency\nservices (severe reaction)."],
]
story.append(pill_table(se_data,
    [55*mm, 40*mm, 85*mm],
    HexColor("#7D6608"), LIGHT_AMB))
story.append(Spacer(1, 2*mm))
story.append(colored_box([
    Paragraph(
        "💧 <b>Most important tip:</b>  Drink at least 6-8 glasses of water per day while taking "
        "your antiviral. This helps your kidneys clear the medicine safely and prevents the most "
        "serious side effects.",
        S("WaterTip", fontSize=9.5, leading=14, fontName="Helvetica-Bold",
          textColor=HexColor("#154360"))),
], LIGHT_TEAL, TEAL))
story.append(Spacer(1, 4*mm))

# ── SECTION 5: MANAGING SYMPTOMS AT HOME ─────────────────────────
story.append(section_banner("🏠", "Managing Symptoms at Home", HexColor("#1E5631")))
story.append(Spacer(1, 2*mm))

symptom_data = [
    ["Symptom", "What Helps"],
    ["Itching (rash)",
     "• Cool, damp cloths on the skin\n• Calamine lotion or Caladryl\n• Trim nails short (prevents scratching)\n• Oatmeal baths (lukewarm water)\n• Antihistamine (ask your pharmacist)"],
    ["Fever",
     "• Paracetamol (acetaminophen) — follow dosing instructions\n• Lukewarm (not cold) sponge bath\n• Keep room cool and well-ventilated\n• AVOID aspirin and ibuprofen"],
    ["Sore mouth/throat\n(if sores inside mouth)",
     "• Cold drinks and ice chips\n• Avoid spicy, salty, or acidic foods\n• Salt-water mouth rinses"],
    ["Tiredness / fatigue",
     "• Rest as much as possible\n• Stay in bed during fever\n• Light meals — don't force eating"],
    ["Skin sores (risk of\nbacterial infection)",
     "• Keep skin clean and dry\n• Wash hands before touching sores\n• Watch for increasing redness, warmth, or pus — see a doctor immediately"],
]
story.append(pill_table(symptom_data, [45*mm, CW - 45*mm],
    HexColor("#1E5631"), LIGHT_GRN))
story.append(Spacer(1, 4*mm))

# ── SECTION 6: STAYING HOME / ISOLATION ─────────────────────────
story.append(section_banner("🔒", "Protecting Others — Isolation & Infection Control", HexColor("#5B2C6F")))
story.append(Spacer(1, 2*mm))

iso_left_items = [
    Paragraph("When are you contagious?", S("IH", fontSize=9.5, fontName="Helvetica-Bold",
              textColor=PURPLE, leading=13)),
    Spacer(1, 2*mm),
    Paragraph(
        "You are contagious from <b>1-2 days before the rash appears</b> "
        "until <b>every single blister has completely crusted over</b> "
        "(usually about 5-7 days after the rash starts).",
        body),
    Spacer(1, 3*mm),
    Paragraph("You MUST stay home and away from others until:", S("IH2", fontSize=9,
              fontName="Helvetica-Bold", textColor=DARK_GREY, leading=12)),
    Spacer(1, 1*mm),
    Paragraph("• All blisters have crusted over (no open/weeping sores)", body_small),
    Paragraph("• You have no fever for 24 hours (without medication)", body_small),
    Spacer(1, 3*mm),
    Paragraph("How does it spread?", S("IH", fontSize=9.5, fontName="Helvetica-Bold",
              textColor=PURPLE, leading=13)),
    Spacer(1, 2*mm),
    Paragraph("• <b>Airborne</b> — breathing in virus from coughs/sneezes", body_small),
    Paragraph("• <b>Direct contact</b> — touching blister fluid", body_small),
    Paragraph("• <b>Contaminated surfaces</b> — touching then touching your face", body_small),
]

iso_right_items = [
    Paragraph("Who is most at risk?", S("IH", fontSize=9.5, fontName="Helvetica-Bold",
              textColor=RED, leading=13)),
    Spacer(1, 2*mm),
    Paragraph(
        "Keep away from these people until you are no longer contagious:",
        body_small),
    Spacer(1, 2*mm),
    Paragraph("🚨 <b>Newborns and young babies</b>", body_small),
    Paragraph("🚨 <b>Pregnant women</b> (especially 1st and 3rd trimester)", body_small),
    Paragraph("🚨 <b>Anyone on chemotherapy or immunosuppressive drugs</b>", body_small),
    Paragraph("🚨 <b>HIV/AIDS patients</b>", body_small),
    Paragraph("🚨 <b>People on long-term steroids</b>", body_small),
    Spacer(1, 3*mm),
    Paragraph("Infection control at home:", S("IH2", fontSize=9,
              fontName="Helvetica-Bold", textColor=DARK_GREY, leading=12)),
    Spacer(1, 1*mm),
    Paragraph("• Wash hands frequently with soap for 20 seconds", body_small),
    Paragraph("• Do not share towels, clothing, or bedding", body_small),
    Paragraph("• Cover mouth/nose when coughing or sneezing", body_small),
    Paragraph("• Your own room if possible; ventilate the space", body_small),
]

def mini_col(items, bg, border, w):
    inner = Table([[i] for i in items], colWidths=[w - 16])
    inner.setStyle(TableStyle([
        ("TOPPADDING", (0,0), (-1,-1), 1), ("BOTTOMPADDING", (0,0), (-1,-1), 2),
        ("LEFTPADDING", (0,0), (-1,-1), 0), ("RIGHTPADDING", (0,0), (-1,-1), 0),
    ]))
    outer = Table([[inner]], colWidths=[w])
    outer.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,-1), bg),
        ("LINEBEFORE",    (0,0), (-1,-1), 4, border),
        ("TOPPADDING",    (0,0), (-1,-1), 8),
        ("BOTTOMPADDING", (0,0), (-1,-1), 8),
        ("LEFTPADDING",   (0,0), (-1,-1), 10),
        ("RIGHTPADDING",  (0,0), (-1,-1), 6),
    ]))
    return outer

lbox = mini_col(iso_left_items, LIGHT_PUR, PURPLE, 88*mm)
rbox = mini_col(iso_right_items, LIGHT_RED, RED, 88*mm)
story.append(two_col(lbox, rbox, left_w=88*mm, right_w=88*mm))
story.append(Spacer(1, 4*mm))

# ── SECTION 7: WHEN TO SEEK URGENT HELP ─────────────────────────
story.append(section_banner("🚑", "WHEN TO SEE A DOCTOR URGENTLY", RED))
story.append(Spacer(1, 2*mm))

urgent_left = [
    "High fever (above 39°C / 102°F) that does not come down with paracetamol",
    "Breathing difficulty or rapid breathing (may signal pneumonia)",
    "Severe headache, stiff neck, sensitivity to light",
    "Confusion, drowsiness, or seizures",
    "Skin sores becoming very red, warm, swollen, or producing pus (bacterial infection)",
]
urgent_right = [
    "Red, painful, swollen eye, or blurred vision",
    "Rash spreading to involve most of the face, or near eyes/ear",
    "Stomach pain, vomiting, or yellowing of skin/eyes (jaundice)",
    "Reduced urination or dark yellow urine (kidney strain)",
    "Rash that is still spreading or new blisters after 7 days",
]

urgent_l_box = colored_box(
    [Paragraph("Contact your doctor or go to emergency if you have:", S("UH",
         fontSize=9, fontName="Helvetica-Bold", textColor=RED, leading=12))] +
    [Paragraph(f"🔴  {u}", body_small) for u in urgent_left],
    LIGHT_RED, RED)
urgent_r_box = colored_box(
    [Paragraph("Also seek urgent care for:", S("UH2",
         fontSize=9, fontName="Helvetica-Bold", textColor=RED, leading=12))] +
    [Paragraph(f"🔴  {u}", body_small) for u in urgent_right],
    LIGHT_RED, RED)

story.append(two_col(urgent_l_box, urgent_r_box, left_w=88*mm, right_w=88*mm))
story.append(Spacer(1, 4*mm))

# ── SECTION 8: AFTER RECOVERY ────────────────────────────────────
story.append(section_banner("🔄", "After Recovery — What You Should Know", HexColor("#154360")))
story.append(Spacer(1, 2*mm))

after_items = [
    ("<b>The virus stays in your body for life.</b>",
     "VZV remains dormant (sleeping) in your nerve cells. It can reactivate later in life "
     "as <b>shingles (herpes zoster)</b> — a painful rash on one side of the body. "
     "This is more likely if your immune system becomes weakened."),
    ("<b>You are immune to chickenpox.</b>",
     "Having chickenpox once gives you lasting immunity. "
     "You will not get chickenpox again (though shingles is possible later)."),
    ("<b>Shingles vaccine (RZV / Shingrix).</b>",
     "If you are 50 years or older, ask your doctor about the shingles vaccine "
     "(Shingrix — 2 doses). It is over 90% effective at preventing shingles and "
     "its complications, even if you have had chickenpox before."),
    ("<b>Chickenpox vaccine for your household.</b>",
     "Unvaccinated household members (especially children) should receive the "
     "varicella vaccine to protect them."),
    ("<b>Follow-up appointment.</b>",
     "Return to your doctor if you have any concerns, or as advised. "
     "Mention if you have new rashes, pain, or feel unwell after your course of medicine."),
]

after_rows = []
for label, text in after_items:
    after_rows.append([
        Table([[Paragraph(label, body_bold)], [Paragraph(text, body_small)]],
              colWidths=[CW - 20])
    ])

after_table = Table(after_rows, colWidths=[CW])
after_table.setStyle(TableStyle([
    ("ROWBACKGROUNDS", (0,0), (-1,-1), [WHITE, LIGHT_TEAL]),
    ("GRID",          (0,0), (-1,-1), 0.3, MID_GREY),
    ("VALIGN",        (0,0), (-1,-1), "TOP"),
    ("TOPPADDING",    (0,0), (-1,-1), 6),
    ("BOTTOMPADDING", (0,0), (-1,-1), 6),
    ("LEFTPADDING",   (0,0), (-1,-1), 8),
    ("RIGHTPADDING",  (0,0), (-1,-1), 8),
]))
story.append(after_table)
story.append(Spacer(1, 4*mm))

# ── CONTACT SECTION ───────────────────────────────────────────────
contact_table = Table([
    [
        Table([
            [Paragraph("📞  Emergency / Ambulance", S("CH", fontSize=9.5,
                fontName="Helvetica-Bold", textColor=WHITE, leading=12))],
            [Paragraph("Call emergency services if you have severe symptoms:\n"
                       "breathing difficulty, confusion, or seizures.",
                       S("CB", fontSize=8.5, fontName="Helvetica",
                         textColor=HexColor("#FFE0D0"), leading=11))],
        ], colWidths=[82*mm]),
        Table([
            [Paragraph("👨‍⚕️  Your Doctor / Clinic", S("CH2", fontSize=9.5,
                fontName="Helvetica-Bold", textColor=WHITE, leading=12))],
            [Paragraph("Name: _____________________________\n"
                       "Phone: ____________________________\n"
                       "Next appointment: _________________",
                       S("CB2", fontSize=8.5, fontName="Helvetica",
                         textColor=HexColor("#D5EAF7"), leading=13, leading=13))],
        ], colWidths=[90*mm]),
    ]
], colWidths=[88*mm, 92*mm])
contact_table.setStyle(TableStyle([
    ("BACKGROUND",    (0,0), (0,0), RED),
    ("BACKGROUND",    (1,0), (1,0), TEAL),
    ("VALIGN",        (0,0), (-1,-1), "TOP"),
    ("TOPPADDING",    (0,0), (-1,-1), 8),
    ("BOTTOMPADDING", (0,0), (-1,-1), 8),
    ("LEFTPADDING",   (0,0), (-1,-1), 10),
    ("RIGHTPADDING",  (0,0), (-1,-1), 10),
]))
story.append(contact_table)
story.append(Spacer(1, 3*mm))

# ── FOOTER ───────────────────────────────────────────────────────
footer = Table([
    [
        Paragraph(
            "Clinical sources: Goldman-Cecil Medicine  •  Fitzpatrick's Dermatology  •  "
            "Harrison's Principles of Internal Medicine 22E  •  Goodman & Gilman's Pharmacology",
            footnote_s),
        Paragraph(
            "This handout is for general patient education only and does not replace your "
            "doctor's advice. Always follow the instructions given to you by your healthcare provider.",
            S("FW", fontSize=7.5, leading=10, fontName="Helvetica-Oblique",
              textColor=RED, alignment=TA_RIGHT)),
    ]
], colWidths=[100*mm, 80*mm])
footer.setStyle(TableStyle([
    ("LINEABOVE",     (0,0), (-1,0), 0.5, MID_GREY),
    ("VALIGN",        (0,0), (-1,-1), "TOP"),
    ("TOPPADDING",    (0,0), (-1,-1), 4),
    ("BOTTOMPADDING", (0,0), (-1,-1), 4),
    ("LEFTPADDING",   (0,0), (-1,-1), 0),
    ("RIGHTPADDING",  (0,0), (-1,-1), 0),
]))
story.append(footer)

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

cd /home/daytona/workspace/vzv-pdf && python generate_patient_counseling.py 2>&1

Editing File

~/vzv-pdf/generate_patient_counseling.py

Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/vzv-pdf/generate_patient_counseling.py'
Running Command

cd /home/daytona/workspace/vzv-pdf && python generate_patient_counseling.py 2>&1

Running Command

ls -lh /home/daytona/workspace/vzv-pdf/

Your patient counseling handout is ready:

VZV Patient Counseling

PDF Document · PDF


Here's what's covered across the 8 color-coded sections in plain, patient-friendly language:
SectionContents
🦠 What is Chickenpox?How VZV spreads, rash stages (spots → blisters → crusts), timeline, Quick Facts box
💊 Your Antiviral MedicineValacyclovir / Famciclovir / Acyclovir dosing table with plain explanations; "Start Early" timing warning
📋 How to Take It CorrectlySide-by-side DO vs. DO NOT boxes — hydration, timing, missed doses, aspirin warning
⚠️ Possible Side EffectsTable of common to serious effects with plain-language "what to do" for each
🏠 Managing Symptoms at HomeItch, fever, mouth sores, fatigue, skin care — practical home remedies
🔒 Isolation & Infection ControlWhen contagious, who is most at risk, how to protect vulnerable household members
🚑 URGENT Warning SignsRed flag symptoms requiring emergency or same-day medical care
🔄 After RecoveryShingles reactivation risk, immunity, Shingrix vaccine, household vaccination
A fillable doctor contact / appointment section is included at the bottom for clinic personalization.
This is a shared conversation. Sign in to Orris to start your own chat.