Make a medical oncology discharge summary for supportive care for Indian patient cancer patients

Asking for Preferences

What kind of discharge summary do you need? · What format do you want the document in? · Should it cover a specific cancer type or be generic for all cancers?

Reading File
Reading File
Good - I have enough info on reportlab. Now let me write the Python script to generate the PDF discharge summary.
Running Command

mkdir -p /home/daytona/workspace/oncology-discharge && ls /home/daytona/workspace/

Writing File

~/oncology-discharge/generate_discharge.py

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

OUTPUT_PATH = "/home/daytona/workspace/oncology-discharge/Oncology_Discharge_Summary.pdf"

# ── Colour palette ──────────────────────────────────────────────────────────
DEEP_BLUE   = HexColor("#1A3A5C")   # headers / title bar
MID_BLUE    = HexColor("#2E6DA4")   # section headings
LIGHT_BLUE  = HexColor("#D6E8F7")   # table header row background
ORANGE      = HexColor("#E07B39")   # accent line under hospital name
LIGHT_GREY  = HexColor("#F5F5F5")   # alternating table row bg
RED_ALERT   = HexColor("#C0392B")   # allergy / alert text
WHITE       = colors.white
BLACK       = colors.black

W, H = A4

# ── Document setup ──────────────────────────────────────────────────────────
doc = BaseDocTemplate(
    OUTPUT_PATH,
    pagesize=A4,
    leftMargin=18*mm, rightMargin=18*mm,
    topMargin=22*mm, bottomMargin=20*mm,
    title="Medical Oncology Discharge Summary – Supportive Care",
    author="Tata Cancer Care Centre, Mumbai",
)

content_width = W - 36*mm   # usable width

frame = Frame(
    doc.leftMargin, doc.bottomMargin,
    content_width, H - doc.topMargin - doc.bottomMargin,
    id='normal'
)

def header_footer(canvas, doc_obj):
    canvas.saveState()
    # Top rule
    canvas.setStrokeColor(MID_BLUE)
    canvas.setLineWidth(0.8)
    canvas.line(18*mm, H - 14*mm, W - 18*mm, H - 14*mm)
    # Footer
    canvas.setFont("Helvetica", 7)
    canvas.setFillColor(colors.grey)
    canvas.drawString(18*mm, 12*mm, "CONFIDENTIAL – For treating physician use only")
    canvas.drawRightString(W - 18*mm, 12*mm, f"Page {doc_obj.page}")
    canvas.line(18*mm, 14*mm, W - 18*mm, 14*mm)
    canvas.restoreState()

doc.addPageTemplates([PageTemplate(id='main', frames=[frame], onPage=header_footer)])

# ── Styles ───────────────────────────────────────────────────────────────────
base = getSampleStyleSheet()

def s(name, **kw):
    p = ParagraphStyle(name, parent=base['Normal'], **kw)
    return p

HOSPITAL = s("hospital", fontSize=16, fontName="Helvetica-Bold",
             textColor=WHITE, alignment=TA_CENTER, spaceAfter=0)
HOSP_SUB = s("hsub", fontSize=8.5, fontName="Helvetica",
             textColor=HexColor("#BDD9F2"), alignment=TA_CENTER, spaceAfter=0)
DOC_TITLE = s("docTitle", fontSize=13, fontName="Helvetica-Bold",
              textColor=DEEP_BLUE, alignment=TA_CENTER, spaceAfter=4)
SEC_HEAD  = s("secHead", fontSize=10, fontName="Helvetica-Bold",
              textColor=WHITE, alignment=TA_LEFT,
              leftIndent=4, spaceAfter=0, spaceBefore=8)
LABEL     = s("label", fontSize=8.5, fontName="Helvetica-Bold",
              textColor=DEEP_BLUE, spaceAfter=1)
VALUE     = s("value", fontSize=9, fontName="Helvetica",
              textColor=BLACK, spaceAfter=2)
SMALL     = s("small", fontSize=8, fontName="Helvetica",
              textColor=colors.grey)
ALERT     = s("alert", fontSize=9, fontName="Helvetica-Bold",
              textColor=RED_ALERT)
BODY      = s("body", fontSize=9, fontName="Helvetica",
              textColor=BLACK, leading=13, spaceAfter=3,
              alignment=TA_JUSTIFY)
BULLET    = s("bullet", fontSize=9, fontName="Helvetica",
              textColor=BLACK, leading=12, leftIndent=10, bulletIndent=0,
              spaceAfter=2)
SIG_STYLE = s("sig", fontSize=8.5, fontName="Helvetica",
              textColor=colors.grey, alignment=TA_CENTER)

def section_header(text):
    """Returns a full-width coloured banner for a section."""
    tbl = Table([[Paragraph(text, SEC_HEAD)]], colWidths=[content_width])
    tbl.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,-1), MID_BLUE),
        ('TOPPADDING',  (0,0), (-1,-1), 4),
        ('BOTTOMPADDING', (0,0), (-1,-1), 4),
        ('LEFTPADDING',  (0,0), (-1,-1), 6),
    ]))
    return tbl

def kv_table(rows, col_ratios=(0.30, 0.70)):
    """Two-column label-value table."""
    cw = [content_width * r for r in col_ratios]
    data = [[Paragraph(lbl, LABEL), Paragraph(val, VALUE)] for lbl, val in rows]
    tbl = Table(data, colWidths=cw)
    tbl.setStyle(TableStyle([
        ('VALIGN', (0,0), (-1,-1), 'TOP'),
        ('TOPPADDING', (0,0), (-1,-1), 3),
        ('BOTTOMPADDING', (0,0), (-1,-1), 3),
        ('LEFTPADDING', (0,0), (-1,-1), 4),
        ('ROWBACKGROUNDS', (0,0), (-1,-1), [WHITE, LIGHT_GREY]),
        ('LINEBELOW', (0,-1), (-1,-1), 0.3, colors.lightgrey),
    ]))
    return tbl

def three_col_table(rows, headers, ratios=(0.35, 0.35, 0.30)):
    cw = [content_width * r for r in ratios]
    head_row = [Paragraph(f"<b>{h}</b>", s("th", fontSize=8.5, fontName="Helvetica-Bold",
                           textColor=DEEP_BLUE)) for h in headers]
    data = [head_row]
    for row in rows:
        data.append([Paragraph(cell, VALUE) for cell in row])
    tbl = Table(data, colWidths=cw)
    tbl.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,0), LIGHT_BLUE),
        ('ROWBACKGROUNDS', (0,1), (-1,-1), [WHITE, LIGHT_GREY]),
        ('VALIGN', (0,0), (-1,-1), 'TOP'),
        ('TOPPADDING', (0,0), (-1,-1), 3),
        ('BOTTOMPADDING', (0,0), (-1,-1), 3),
        ('LEFTPADDING', (0,0), (-1,-1), 4),
        ('GRID', (0,0), (-1,-1), 0.3, colors.lightgrey),
    ]))
    return tbl

def four_col_table(rows, headers, ratios=(0.22, 0.22, 0.28, 0.28)):
    cw = [content_width * r for r in ratios]
    head_row = [Paragraph(f"<b>{h}</b>", s("th4", fontSize=8.5, fontName="Helvetica-Bold",
                           textColor=DEEP_BLUE)) for h in headers]
    data = [head_row]
    for row in rows:
        data.append([Paragraph(cell, VALUE) for cell in row])
    tbl = Table(data, colWidths=cw)
    tbl.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,0), LIGHT_BLUE),
        ('ROWBACKGROUNDS', (0,1), (-1,-1), [WHITE, LIGHT_GREY]),
        ('VALIGN', (0,0), (-1,-1), 'TOP'),
        ('TOPPADDING', (0,0), (-1,-1), 3),
        ('BOTTOMPADDING', (0,0), (-1,-1), 3),
        ('LEFTPADDING', (0,0), (-1,-1), 4),
        ('GRID', (0,0), (-1,-1), 0.3, colors.lightgrey),
    ]))
    return tbl

# ═══════════════════════════════════════════════════════════════════════════
# BUILD STORY
# ═══════════════════════════════════════════════════════════════════════════
story = []

# ── Hospital Header Banner ───────────────────────────────────────────────────
hosp_banner = Table(
    [[Paragraph("TATA CANCER CARE CENTRE", HOSPITAL)],
     [Paragraph("Department of Medical Oncology | Supportive & Palliative Care Unit", HOSP_SUB)],
     [Paragraph("Mumbai – 400 012, Maharashtra | Tel: 022-6750-0000 | www.tatacancercare.org", HOSP_SUB)]],
    colWidths=[content_width]
)
hosp_banner.setStyle(TableStyle([
    ('BACKGROUND', (0,0), (-1,-1), DEEP_BLUE),
    ('TOPPADDING',  (0,0), (-1,-1), 5),
    ('BOTTOMPADDING', (0,-1), (-1,-1), 8),
    ('LEFTPADDING',  (0,0), (-1,-1), 4),
]))
story.append(hosp_banner)
story.append(HRFlowable(width=content_width, thickness=3, color=ORANGE, spaceAfter=6))

# Document title
story.append(Paragraph("MEDICAL ONCOLOGY DISCHARGE SUMMARY", DOC_TITLE))
story.append(Paragraph("(Supportive Care Admission)", s("subT", fontSize=9.5,
             fontName="Helvetica-Oblique", textColor=MID_BLUE, alignment=TA_CENTER)))
story.append(Spacer(1, 6))

# ── SECTION 1: Patient Demographics ─────────────────────────────────────────
story.append(section_header("1.  PATIENT DEMOGRAPHICS"))
story.append(Spacer(1, 3))

# Top row: two side-by-side mini tables
left_kv = [
    ("Patient Name:", "Smt. Sunita Ramesh Patil"),
    ("Age / Sex / Blood Group:", "52 Years / Female / B+ve"),
    ("Date of Birth:", "14 March 1974"),
    ("Marital Status:", "Married"),
    ("Religion:", "Hindu"),
    ("Address:", "Flat No. 7, Shivaji Nagar, Pune – 411 005"),
    ("Contact Number:", "+91 98765 43210"),
    ("Guardian / Attendant:", "Shri Ramesh Patil (Husband) – +91 98765 43211"),
    ("Referred By:", "Dr. P. K. Nair, MBBS MD – Nair Hospital, Pune"),
    ("UHID / MRD No.:", "TATA-ONC-2026-04872"),
]
right_kv = [
    ("Admission Date:", "30 June 2026"),
    ("Discharge Date:", "09 July 2026"),
    ("Total Days Admitted:", "10 Days"),
    ("Ward / Bed:", "Oncology Ward – Bed No. 14B"),
    ("Treating Consultant:", "Dr. Anjali Mehta, MD DM Medical Oncology"),
    ("Co-consultant:", "Dr. Suresh Kadam, MD Palliative Medicine"),
    ("Resident / JR:", "Dr. Priya Soni (Junior Resident)"),
    ("Nursing In-charge:", "Nurse Kavita Joshi"),
    ("Insurance / Ayushman:", "Ayushman Bharat PMJAY – Policy No. 0045-2026"),
    ("ECOG Performance Status:", "2 (at admission) → 1 (at discharge)"),
]
demo_left  = kv_table(left_kv,  (0.40, 0.60))
demo_right = kv_table(right_kv, (0.44, 0.56))
demo_outer = Table([[demo_left, Spacer(4,1), demo_right]],
                   colWidths=[content_width*0.49, 4, content_width*0.49])
demo_outer.setStyle(TableStyle([('VALIGN', (0,0), (-1,-1), 'TOP')]))
story.append(demo_outer)
story.append(Spacer(1, 6))

# ── SECTION 2: Oncological Diagnosis ────────────────────────────────────────
story.append(section_header("2.  ONCOLOGICAL DIAGNOSIS"))
story.append(Spacer(1, 3))
story.append(kv_table([
    ("Primary Diagnosis:", "Carcinoma Left Breast – Invasive Ductal Carcinoma (IDC), Grade III"),
    ("Stage:", "Stage IIIB (T4b N2 M0) – Locally Advanced Breast Cancer"),
    ("Receptor Status:", "ER +ve (80%), PR +ve (60%), HER2 2+ (FISH Non-amplified) – Luminal B (HER2-negative)"),
    ("Date of Histopathology:", "12 April 2026  |  Biopsy Lab Ref: TATA-PATH-2026-1122"),
    ("Ki-67 Index:", "35% (High Proliferative)"),
    ("Date of Initial Diagnosis:", "10 April 2026"),
    ("Comorbidities:", "Type 2 Diabetes Mellitus (on OHA), Hypertension (controlled), Iron-Deficiency Anaemia"),
    ("Known Allergies:", "<font color='#C0392B'><b>ALLERGIC TO PENICILLIN (Rash) – Documented on armband</b></font>"),
], (0.30, 0.70)))
story.append(Spacer(1, 6))

# ── SECTION 3: Current Admission – Reason ───────────────────────────────────
story.append(section_header("3.  REASON FOR THIS ADMISSION"))
story.append(Spacer(1, 3))
story.append(Paragraph(
    "Patient was admitted with <b>Grade 3 febrile neutropaenia</b> following Cycle 3 of neoadjuvant "
    "chemotherapy (AC-T regimen: Doxorubicin + Cyclophosphamide). She presented with high-grade fever "
    "(39.8°C), rigors, oral mucositis (WHO Grade II), nausea/vomiting (CTCAE Grade 2), and significant "
    "fatigue. She also had worsening oral intake with body weight loss of 3 kg over 3 weeks, raising "
    "concern for nutritional compromise. Admission was also prompted by uncontrolled pain (NRS 7/10) "
    "from bone metastasis-related back pain.", BODY))
story.append(Spacer(1, 6))

# ── SECTION 4: Treatment Given During Admission ───────────────────────────────
story.append(section_header("4.  TREATMENT ADMINISTERED DURING ADMISSION"))
story.append(Spacer(1, 3))

story.append(Paragraph("<b>A. Anti-infective / Neutropaenia Management</b>", LABEL))
story.append(Spacer(1,2))
story.append(four_col_table([
    ["Inj. Piperacillin-Tazobactam 4.5 g IV Q8H", "30 Jun – 06 Jul 2026", "7 days", "Empiric broad-spectrum; de-escalated on culture sensitivity"],
    ["Inj. Amikacin 15 mg/kg IV OD", "30 Jun – 03 Jul 2026", "4 days", "Gram-negative cover; stopped on Day 4 (culture guided)"],
    ["Inj. Fluconazole 400 mg IV OD", "30 Jun – 05 Jul 2026", "6 days", "Antifungal prophylaxis; switched to oral on Day 6"],
    ["Inj. Filgrastim 300 mcg SC OD", "01 Jul – 06 Jul 2026", "6 days", "G-CSF – neutropaenia recovery"],
    ["Inj. Acyclovir 5 mg/kg IV Q8H", "01 Jul – 04 Jul 2026", "4 days", "HSV prophylaxis in immunocompromised state"],
], ["Drug / Dose / Route / Frequency", "Duration", "No. of Days", "Remarks"],
   ratios=(0.35, 0.22, 0.12, 0.31)))
story.append(Spacer(1, 4))

story.append(Paragraph("<b>B. Supportive Care – Antiemetics, Pain &amp; Nutrition</b>", LABEL))
story.append(Spacer(1,2))
story.append(four_col_table([
    ["Inj. Ondansetron 8 mg IV TID", "30 Jun – 08 Jul", "9 days", "5-HT3 antagonist; nausea control"],
    ["Tab. Metoclopramide 10 mg TID (PO) – after Day 5", "05 Jul – 08 Jul", "4 days", "Prokinetic; gastric stasis"],
    ["Inj. Pantoprazole 40 mg IV OD", "30 Jun – 09 Jul", "10 days", "Gastroprotection"],
    ["Tab. Pantoprazole 40 mg PO OD at discharge", "Discharge Rx", "Continue", "Step-down oral PPI"],
    ["Inj. Tramadol 50 mg IV Q8H (PRN)", "30 Jun – 03 Jul", "4 days", "Moderate-severe pain (NRS 7/10)"],
    ["Tab. Oxycodone CR 10 mg PO Q12H", "04 Jul onwards", "Continue", "Opioid rotation – better pain control"],
    ["Tab. Pregabalin 75 mg BD", "02 Jul onwards", "Continue", "Neuropathic component"],
    ["Tab. Paracetamol 500 mg TID (PRN)", "30 Jun onwards", "Continue", "Adjuvant analgesic / antipyretic"],
    ["Ryle's Tube feeding: Ensure Plus 200 mL Q4H", "01 Jul – 05 Jul", "5 days", "Enteral nutrition support – poor oral intake"],
    ["High-protein oral nutritional supplement (Protinex)", "06 Jul onwards", "Continue", "Oral sip feeds – 2 servings/day"],
    ["IV Fluids: NS 0.9% + KCl 20 mEq @ 80 mL/hr", "30 Jun – 04 Jul", "5 days", "Hydration, electrolyte correction"],
    ["Inj. Iron Sucrose 200 mg IV in 100 mL NS", "02, 04, 06 Jul", "3 doses", "Iron deficiency anaemia (Hb 7.8 on admission)"],
    ["PRBC transfusion 2 units", "03 July 2026", "1 session", "Hb 7.2 g/dL; symptomatic anaemia"],
], ["Drug / Intervention", "Duration", "Days", "Remarks"],
   ratios=(0.33, 0.17, 0.10, 0.40)))
story.append(Spacer(1, 4))

story.append(Paragraph("<b>C. Oral Care / Mucositis Management</b>", LABEL))
story.append(Spacer(1,2))
story.append(three_col_table([
    ["Magic Mouthwash (Lignocaine + Antacid + Diphenhydramine) 10 mL QID swish-spit", "07 days", "WHO Grade II Mucositis"],
    ["Benzydamine HCl 0.15% mouthwash TID", "07 days", "Anti-inflammatory rinse"],
    ["Clotrimazole oral lozenges 10 mg 5 x/day", "05 days", "Oral candidiasis prevention"],
    ["Soft / liquid diet; calorie-dense foods advised", "Ongoing", "Dietitian reviewed on Day 3"],
], ["Intervention", "Duration", "Reason / Notes"],
   ratios=(0.45, 0.15, 0.40)))
story.append(Spacer(1, 4))

story.append(Paragraph("<b>D. Psycho-Social &amp; Palliative Care Input</b>", LABEL))
story.append(Spacer(1,2))
story.append(three_col_table([
    ["Palliative care team review", "Day 2 &amp; Day 8", "Goals-of-care discussion; advance care planning initiated"],
    ["Psychiatric / Counselling referral", "Day 4", "Moderate anxiety-depression (PHQ-9 score 14); initiated Tab. Escitalopram 10 mg OD"],
    ["Social worker assessment", "Day 5", "Ayushman Bharat reimbursement facilitated; home nursing arranged"],
    ["Patient &amp; caregiver education (verbal + printed pamphlet in Marathi)", "Day 8 &amp; 9", "Chemotherapy side-effects, infection precautions, diet, pain diary"],
], ["Intervention", "Timing", "Details"],
   ratios=(0.28, 0.18, 0.54)))
story.append(Spacer(1, 6))

# ── SECTION 5: Investigations ────────────────────────────────────────────────
story.append(section_header("5.  KEY INVESTIGATIONS"))
story.append(Spacer(1, 3))

story.append(Paragraph("<b>Haematology &amp; Biochemistry (Admission vs Discharge)</b>", LABEL))
story.append(Spacer(1,2))
story.append(four_col_table([
    ["Haemoglobin (g/dL)",       "7.2",   "10.4",  "Post 2U PRBC transfusion + Iron sucrose"],
    ["Total WBC (cells/µL)",     "380",   "4,200", "Nadir on Day 1; recovery with G-CSF"],
    ["ANC (cells/µL)",           "80",    "2,800", "Severe neutropaenia → resolved"],
    ["Platelets (lakh/µL)",      "0.9",   "1.4",   "Mild thrombocytopenia; recovered spontaneously"],
    ["Serum Creatinine (mg/dL)", "1.1",   "0.9",   "Within normal limits"],
    ["Serum Sodium (mEq/L)",     "131",   "138",   "Mild hyponatraemia corrected"],
    ["Serum Potassium (mEq/L)",  "3.1",   "3.9",   "Hypokalaemia corrected with supplementation"],
    ["ALT / SGPT (U/L)",         "68",    "34",    "Mild transaminitis; resolved"],
    ["Serum Albumin (g/dL)",     "2.6",   "2.9",   "Improving with nutritional support"],
    ["Blood Sugar Fasting (mg/dL)","178", "112",   "Glycaemic control improved; OHA adjusted"],
    ["Blood Culture (Day 1)",    "E. coli (ESBL negative, sensitive to Pip-Taz)", "—", "Guided antibiotic therapy"],
    ["CXR (01 Jul 2026)",        "No consolidation; small left pleural effusion", "—", "Stable at discharge"],
    ["ECHO (03 Jul 2026)",       "EF 58%; no regional wall motion abnormality", "—", "Baseline pre-anthracycline"],
], ["Parameter", "At Admission", "At Discharge", "Remarks"],
   ratios=(0.30, 0.18, 0.18, 0.34)))
story.append(Spacer(1, 6))

# ── SECTION 6: Summary of Hospital Course ────────────────────────────────────
story.append(section_header("6.  SUMMARY OF HOSPITAL COURSE"))
story.append(Spacer(1, 3))
story.append(Paragraph(
    "Patient was admitted with post-chemotherapy febrile neutropaenia (Grade 3) after Cycle 3 "
    "of AC chemotherapy. She was started promptly on empiric broad-spectrum IV antibiotics "
    "(Piperacillin-Tazobactam + Amikacin) as per institutional febrile neutropaenia protocol "
    "(MASCC Score 17 – Low Risk; admitted as per patient preference and logistic considerations). "
    "Blood culture grew E. coli, sensitive to the empiric regimen. Antibiotics were continued for a "
    "total of 7 days and successfully de-escalated. G-CSF (Filgrastim) was administered for 6 days; "
    "ANC recovered to &gt;2,000 by Day 7.", BODY))
story.append(Paragraph(
    "Symptomatic anaemia (Hb 7.2 g/dL) was managed with 2 units of packed red blood cells and "
    "3 doses of IV iron sucrose. Nutritional status was supported initially via nasogastric enteral feeds "
    "(5 days) and transitioned to oral sip feeds with dietitian supervision. Body weight improved by "
    "1.2 kg over the admission.", BODY))
story.append(Paragraph(
    "Pain was initially controlled with IV tramadol and was effectively rotated to oral sustained-release "
    "oxycodone with addition of pregabalin for the neuropathic component; NRS score improved from 7/10 "
    "to 2/10 at discharge. Oral mucositis healed to Grade I by Day 8. Electrolyte imbalances (hyponatraemia, "
    "hypokalaemia) were corrected. Blood sugar was stabilised with dose adjustment of oral hypoglycaemics. "
    "Palliative care and psychiatry teams were involved for goals-of-care planning and management of "
    "anxiety-depression. Patient and caregivers were educated in Marathi and English; home nursing "
    "services were arranged via the social worker.", BODY))
story.append(Spacer(1, 4))

# ── SECTION 7: Condition at Discharge ────────────────────────────────────────
story.append(section_header("7.  CONDITION AT DISCHARGE"))
story.append(Spacer(1, 3))
story.append(kv_table([
    ("General Condition:", "Stable, afebrile (Temp 37.1°C), ambulatory"),
    ("ECOG Performance Status:", "1 (improved from 2 at admission)"),
    ("Vital Signs at Discharge:", "BP 128/80 mmHg | PR 82/min, regular | SpO₂ 98% (RA) | RR 16/min"),
    ("Pain (NRS):", "2/10 – well-controlled on oral opioid regimen"),
    ("Oral Intake:", "Tolerating soft diet + high-protein sip feeds"),
    ("Emotional Status:", "Improved; less anxious; engaged with counselling"),
    ("Caregiver Status:", "Husband present; educated and capable of home care"),
], (0.28, 0.72)))
story.append(Spacer(1, 6))

# ── SECTION 8: Discharge Medications ─────────────────────────────────────────
story.append(section_header("8.  DISCHARGE MEDICATIONS (Rx)"))
story.append(Spacer(1, 3))
story.append(Paragraph(
    "<font color='#C0392B'><b>ALLERGY: PENICILLIN</b></font>  –  ensure no beta-lactam penicillins are prescribed in the community.",
    ALERT))
story.append(Spacer(1, 3))
story.append(four_col_table([
    ["Tab. Oxycodone CR (OxyContin) 10 mg", "Twice daily (every 12 hrs)", "14 days", "Opioid – for persistent cancer pain; do NOT crush"],
    ["Tab. Paracetamol 500 mg", "Three times daily (TID)", "14 days", "Adjuvant analgesic / breakthrough pain"],
    ["Tab. Pregabalin 75 mg", "Twice daily (BD)", "14 days", "Neuropathic pain"],
    ["Tab. Pantoprazole 40 mg", "Once daily (before breakfast)", "30 days", "Gastroprotection with NSAIDs/steroids"],
    ["Tab. Ondansetron 4 mg", "Twice daily (PRN for nausea)", "10 days", "As-needed antiemetic"],
    ["Tab. Metoclopramide 10 mg", "Three times daily (before meals)", "10 days", "Prokinetic – nausea/gastric stasis"],
    ["Tab. Escitalopram 10 mg", "Once daily (morning)", "30 days", "Anxio-depressive symptoms; psychiatry follow-up"],
    ["Cap. Fluconazole 150 mg", "Once weekly", "4 weeks", "Antifungal maintenance; immunosuppressed state"],
    ["Tab. Acyclovir 400 mg", "Twice daily", "14 days", "HSV prophylaxis"],
    ["Tab. Cotrimoxazole DS", "Once daily (Mon/Wed/Fri)", "Until ANC &gt;500 sustained", "PCP prophylaxis (CD4 equivalent status)"],
    ["Tab. Metformin 500 mg + Glimepiride 1 mg", "Twice daily (with meals)", "Continue", "Diabetes management; BSFL 112 at discharge"],
    ["Tab. Amlodipine 5 mg", "Once daily (morning)", "Continue", "Antihypertensive"],
    ["Protinex (high-protein supplement) – 2 scoops", "Twice daily in warm milk/water", "Continue", "Nutritional support – target 1.5 g protein/kg/day"],
    ["Iron (Ferrous Ascorbate) 100 mg", "Twice daily (on empty stomach)", "30 days", "Iron deficiency anaemia – ongoing course"],
    ["Tab. Folic Acid 5 mg", "Once daily", "30 days", "Megaloblastic prevention with chemotherapy"],
], ["Drug / Formulation", "Dose / Frequency", "Duration", "Special Instructions"],
   ratios=(0.32, 0.22, 0.18, 0.28)))
story.append(Spacer(1, 4))
story.append(Paragraph(
    "<b>Note:</b> All opioid prescriptions have been issued with Schedule X narcotic prescription slips "
    "(as per NDPS Act India). Caregiver has been counselled on safe opioid storage, "
    "constipation prophylaxis, and signs of opioid toxicity.", SMALL))
story.append(Spacer(1, 6))

# ── SECTION 9: Dietary & Lifestyle Advice ─────────────────────────────────────
story.append(section_header("9.  DIETARY &amp; LIFESTYLE ADVICE (Advised by Dietitian Ms. Rekha More)"))
story.append(Spacer(1, 3))
bullets = [
    "Target caloric intake: <b>2,000–2,200 kcal/day</b>; protein target: <b>75–80 g/day</b> (1.5 g/kg/day).",
    "Foods encouraged: Soft-cooked lentils (dal), curd/yoghurt, mashed potato, soft idli/upma, boiled eggs, fish curry, ragi porridge, banana, papaya, seasonal fruits (washed thoroughly).",
    "Foods to <b>AVOID</b> during chemotherapy: Raw salads, uncooked sprouts, street food (chaat, pani puri), unpasteurised milk/cheese (paneer from street vendors), high-fibre raw vegetables, alcohol, tobacco products.",
    "Maintain oral hygiene: Rinse after every meal with warm saline or prescribed mouthwash.",
    "Adequate hydration: Minimum <b>2–2.5 litres/day</b> of boiled/filtered water.",
    "Light physical activity as tolerated: 15–20-minute walks twice daily (if ANC &gt;1,000).",
    "Avoid crowded places, religious gatherings, sick contacts – infection precautions during neutropaenia phase.",
    "Report any fever ≥38°C immediately to oncology emergency (do not wait – come directly to casualty).",
]
for b in bullets:
    story.append(Paragraph(f"• {b}", BULLET))
story.append(Spacer(1, 6))

# ── SECTION 10: Follow-up Plan ────────────────────────────────────────────────
story.append(section_header("10.  FOLLOW-UP PLAN"))
story.append(Spacer(1, 3))
story.append(three_col_table([
    ["<b>CBC + Blood Sugar + LFT + KFT</b>", "14 July 2026 (Day 5 post-discharge)", "Community lab OR TATA OPD Lab; report to bring on follow-up"],
    ["<b>Medical Oncology OPD – Dr. Anjali Mehta</b>", "16 July 2026 – 10:00 AM (OPD No. 14)", "Pre-Cycle 4 evaluation; dose modification if needed"],
    ["<b>Palliative Medicine – Dr. Suresh Kadam</b>", "18 July 2026", "Pain assessment; opioid dose titration"],
    ["<b>Psychiatry OPD</b>", "20 July 2026", "Escitalopram response evaluation; counselling continuation"],
    ["<b>Dietitian Review</b>", "23 July 2026 (telephonic)", "Nutritional status, weight trend, sip feed compliance"],
    ["<b>Cycle 4 Chemotherapy (Paclitaxel 175 mg/m²)</b>", "28 July 2026 (tentative)", "Subject to ANC &gt;1,500, Hb &gt;9, Platelets &gt;1,00,000"],
], ["Appointment", "Date / Time", "Details"],
   ratios=(0.32, 0.28, 0.40)))
story.append(Spacer(1, 6))

# ── SECTION 11: Emergency Instructions ───────────────────────────────────────
story.append(section_header("11.  EMERGENCY INSTRUCTIONS – WHEN TO COME TO HOSPITAL IMMEDIATELY"))
story.append(Spacer(1, 3))
alert_items = [
    "Fever ≥38°C or chills/rigors at any time (do <b>NOT</b> give paracetamol and wait – come directly)",
    "Bleeding from any site (gums, nose, urine, stool, skin bruising)",
    "Sudden breathlessness, chest pain, or palpitations",
    "Severe pain (NRS &gt;6) not responding to prescribed medications",
    "Inability to take oral medications or severe vomiting for &gt;12 hours",
    "Confusion, altered sensorium, fits or severe headache",
    "Swelling, redness, or pus at the IV cannula site or PICC line",
    "Signs of opioid overdose: excessive drowsiness, slow breathing (&lt;8/min), pinpoint pupils",
]
for item in alert_items:
    story.append(Paragraph(f"⚠  {item}", s("alertBullet", fontSize=9, fontName="Helvetica",
                 textColor=HexColor("#7B2D00"), leading=13, leftIndent=8, spaceAfter=2)))
story.append(Spacer(1, 3))
story.append(Paragraph(
    "<b>Oncology Emergency Helpline (24×7):</b>  022-6750-0911  |  "
    "<b>Oncology Casualty:</b>  Ground Floor, Block C, TATA Cancer Care Centre, Mumbai",
    s("emHelpline", fontSize=9, fontName="Helvetica-Bold", textColor=DEEP_BLUE)))
story.append(Spacer(1, 6))

# ── SECTION 12: Signatures ────────────────────────────────────────────────────
story.append(section_header("12.  SIGNATURES &amp; CERTIFICATION"))
story.append(Spacer(1, 6))

sig_data = [
    [
        Paragraph("<b>Dr. Anjali Mehta</b><br/>MD, DM Medical Oncology<br/>"
                  "Reg. No.: MCI-2003-DM-04891<br/>Date: 09 July 2026", SIG_STYLE),
        Paragraph("<b>Dr. Suresh Kadam</b><br/>MD Palliative Medicine<br/>"
                  "Reg. No.: MCI-2007-MD-11223<br/>Date: 09 July 2026", SIG_STYLE),
        Paragraph("<b>Dr. Priya Soni</b><br/>MBBS, Junior Resident<br/>"
                  "Reg. No.: MMC-2022-01567<br/>Date: 09 July 2026", SIG_STYLE),
        Paragraph("<b>Nurse Kavita Joshi</b><br/>B.Sc Nursing, Oncology Certified<br/>"
                  "Staff ID: TATA-NRS-442<br/>Date: 09 July 2026", SIG_STYLE),
    ]
]
sig_labels = [
    Paragraph("____________________________", SIG_STYLE),
    Paragraph("____________________________", SIG_STYLE),
    Paragraph("____________________________", SIG_STYLE),
    Paragraph("____________________________", SIG_STYLE),
]

sig_tbl = Table(
    [sig_labels, sig_data[0]],
    colWidths=[content_width/4]*4
)
sig_tbl.setStyle(TableStyle([
    ('VALIGN', (0,0), (-1,-1), 'BOTTOM'),
    ('TOPPADDING', (0,0), (-1,-1), 2),
    ('BOTTOMPADDING', (0,0), (-1,-1), 2),
    ('ALIGN', (0,0), (-1,-1), 'CENTER'),
]))
story.append(sig_tbl)
story.append(Spacer(1, 8))

# Patient acknowledgement
ack_tbl = Table([[
    Paragraph(
        "Patient / Guardian Acknowledgement:<br/>"
        "I, <u>Shri Ramesh Patil</u>, have received and understood the discharge instructions, "
        "medication list, and follow-up plan in <b>Marathi and English</b>.<br/><br/>"
        "Signature: ____________________________  &nbsp;&nbsp;&nbsp;"
        "Date: 09 July 2026 &nbsp;&nbsp;&nbsp; Relation to Patient: Husband",
        s("ack", fontSize=8.5, fontName="Helvetica", textColor=BLACK, leading=13))
]], colWidths=[content_width])
ack_tbl.setStyle(TableStyle([
    ('BOX', (0,0), (-1,-1), 0.8, MID_BLUE),
    ('BACKGROUND', (0,0), (-1,-1), LIGHT_BLUE),
    ('TOPPADDING', (0,0), (-1,-1), 6),
    ('BOTTOMPADDING', (0,0), (-1,-1), 6),
    ('LEFTPADDING', (0,0), (-1,-1), 8),
]))
story.append(ack_tbl)
story.append(Spacer(1, 6))

# ── Footer disclaimer ─────────────────────────────────────────────────────────
story.append(HRFlowable(width=content_width, thickness=0.5, color=colors.grey))
story.append(Spacer(1, 3))
story.append(Paragraph(
    "This discharge summary is generated for clinical and documentation purposes. "
    "Details are fictitious and created for demonstration / training. "
    "All drug dosages and regimens should be verified against current institutional guidelines "
    "and the prescribing physician's clinical judgment. "
    "Orris Medical AI – July 2026.",
    s("disclaimer", fontSize=7, fontName="Helvetica-Oblique",
      textColor=colors.grey, alignment=TA_CENTER)))

# ═══════════════════════════════════════════════════════════════════════════
# BUILD
# ═══════════════════════════════════════════════════════════════════════════
doc.build(story)
print(f"PDF saved to: {OUTPUT_PATH}")
Running Command

cd /home/daytona/workspace/oncology-discharge && python generate_discharge.py 2>&1

The issue is a negative available width in one of the tables. The four_col_table helper uses ratios that may produce columns too narrow. Let me fix by adjusting ratios and ensuring all columns have sufficient width.
Editing File

~/oncology-discharge/generate_discharge.py

Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/oncology-discharge/generate_discharge.py'
Now fix the specific four_col_table calls that have tight ratios - especially the discharge medications table:
Editing File

~/oncology-discharge/generate_discharge.py

Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/oncology-discharge/generate_discharge.py'
Editing File

~/oncology-discharge/generate_discharge.py

Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/oncology-discharge/generate_discharge.py'
Editing File

~/oncology-discharge/generate_discharge.py

Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/oncology-discharge/generate_discharge.py'
Editing File

~/oncology-discharge/generate_discharge.py

Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/oncology-discharge/generate_discharge.py'
Running Command

cd /home/daytona/workspace/oncology-discharge && python generate_discharge.py 2>&1

The issue is padding (leftPadding + rightPadding = 6) eating into narrow column widths. Let me add a debug step to find which table is causing the issue, then fix the padding across all tables.
Running Command

cd /home/daytona/workspace/oncology-discharge && python -c " from reportlab.lib.units import mm content_width = 210*mm - 36*mm # A4 - margins print('content_width mm:', content_width/mm) # Check minimum column for 4-col tables for ratios in [(0.34,0.22,0.12,0.32),(0.34,0.16,0.10,0.40),(0.32,0.20,0.16,0.32),(0.30,0.17,0.17,0.36)]: cols = [content_width*r for r in ratios] # Each col needs leftPadding(3)+rightPadding(3)=6 pts clearance narrow = [(i, c/mm, (c-6)/mm) for i,c in enumerate(cols)] print('ratios', ratios, '-> cols mm', [round(c/mm,1) for c in cols], 'available after 6pt pad', [round((c-6)/mm,1) for c in cols]) " 2>&1

Column widths look fine. The error must be coming from the demo_outer table (side-by-side kv tables) or the sig table. The demo_outer uses content_width*0.49 for each, with inner tables also using paddings. The inner kv_table has leftPadding=4 on its cells, so the available width is fine. Let me check if it's the sig table or the demo_outer. Let me add stepwise build to isolate:
Running Command

cd /home/daytona/workspace/oncology-discharge && python -c " import traceback exec(open('generate_discharge.py').read().replace('doc.build(story)', ''' for i, item in enumerate(story): try: from reportlab.lib.pagesizes import A4 from io import BytesIO from reportlab.platypus import SimpleDocTemplate buf = BytesIO() d2 = SimpleDocTemplate(buf, pagesize=A4, leftMargin=18*__import__(\"reportlab.lib.units\",fromlist=[\"mm\"]).mm, rightMargin=18*__import__(\"reportlab.lib.units\",fromlist=[\"mm\"]).mm) d2.build([item]) except Exception as e: print(f\"Item {i}: {type(item).__name__} -> {e}\") break else: print(\"All items OK individually\") ''')) 2>&1 | head -30

The side-by-side demo_outer table nesting inner tables is the likely culprit - nested Tables in ReportLab can cause padding issues. Let me simplify the demographics section to use a flat single table instead of nested tables, and reduce all padding to 2pt:
Editing File

~/oncology-discharge/generate_discharge.py

Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/oncology-discharge/generate_discharge.py'
Now replace the nested side-by-side demo table with a single flat 4-column table:
Editing File

~/oncology-discharge/generate_discharge.py

Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/oncology-discharge/generate_discharge.py'
Running Command

cd /home/daytona/workspace/oncology-discharge && python generate_discharge.py 2>&1

Model call limits exceeded: run limit (20/20)

Find latest ASCO guidelines on palliative care in cancer

The ASCO guideline on palliative care integration in oncology was last updated in 2017, reaffirming that early palliative care should be offered to all patients with advanced cancer alongside standard oncologic treatment. Let me pull the latest evidence and any updates since then.
Searching sources...
Searching PubMed

ASCO guideline palliative care cancer

Searching the Web

ASCO 2024 2025 palliative care oncology guideline update

Excellent - found the key guideline (PMID 38748941). Let me pull its full abstract and also get details on the related opioids and integrative medicine guidelines in parallel.
Reading Article

PMID: 38748941

Reading Article

PMID: 36469839

Reading Article

PMID: 36122322

Reading Web Page

https://ascopubs.org/doi/10.1200/JCO.24.00542

I now have comprehensive data. Let me also quickly pull the opioid conversion guideline (PMID 40029420) since it is the most recent (2025) ASCO-affiliated palliative care guideline.
Reading Article

PMID: 40029420

Here is a comprehensive summary of the latest ASCO guidelines on palliative care in cancer.

ASCO Guidelines on Palliative Care in Cancer - Latest Evidence (2022-2025)


1. PRIMARY GUIDELINE: Palliative Care for Patients With Cancer: ASCO Guideline Update (2024)

Citation: Sanders JJ, Temin S, Ghoshal A, et al. J Clin Oncol. 2024;42(19):2336-2357. Published online May 15, 2024. PMID: 38748941 | DOI: 10.1200/JCO.24.00542
This is the most current and comprehensive ASCO palliative care guideline, updating the previous 2016 version. It reviewed 52 studies (systematic reviews, meta-analyses, RCTs) published 2015-2023.

Core Recommendations

Who should receive specialist palliative care referral:
  • All patients with advanced solid tumors and hematologic malignancies should be referred to specialized interdisciplinary palliative care teams
  • Referral should begin early in the disease course, alongside active cancer treatment - not reserved for end of life
  • Patients on Phase I cancer clinical trials with solid tumors may also be referred to specialist palliative care
  • Patients with uncontrolled symptoms, QOL concerns, psychosocial or spiritual distress should receive early palliative care involvement
Where and how care is delivered:
  • Teams should provide both outpatient and inpatient palliative care
  • Cancer care programs should maintain dedicated specialist palliative care services complementing existing supportive care interventions (nurse navigation, geriatric oncology, psycho-oncology, pain services, telehealth)
  • Telehealth-based palliative care delivery is recognised as an emerging modality
Family caregivers:
  • Oncology clinicians may refer family caregivers and care partners (including chosen family and friends) to palliative care teams for additional support - this is a new explicit recommendation vs. the 2016 guideline
Equity and language:
  • This update explicitly addresses linguistic, geographic, ethical, and contextual factors affecting equitable access to palliative care - particularly relevant for diverse populations including India

What Changed From 2016 to 2024?

Domain2016 Guideline2024 Update
Patient populationAdvanced cancer, early integrationExtended to hematologic malignancies explicitly
Caregiver supportMentionedNow an explicit recommendation
Phase I trialsNot addressedNow included
Equity/languageNot addressedExplicitly addressed
TelehealthNot addressedRecognised as modality
Evidence base2009-2015 literature2015-2023 (52 studies)

2. ASCO Guideline: Use of Opioids for Cancer Pain (2023)

Citation: Paice JA, Bohlke K, Barton D, et al. J Clin Oncol. 2023;41(4):914-930. PMID: 36469839 | DOI: 10.1200/JCO.22.02198
Key recommendations:
  • Opioids should be offered to all patients with moderate-to-severe cancer pain unless contraindicated
  • Initiate PRN at the lowest effective dose, with early assessment and frequent titration
  • For patients with substance use disorder, collaborate with palliative care/pain/addiction specialists
  • Monitor and proactively manage opioid adverse effects (constipation, nausea, sedation)

3. SIO-ASCO Guideline: Integrative Medicine for Pain in Oncology (2022)

Citation: Mao JJ, Ismaila N, Bao T, et al. J Clin Oncol. 2022;40(34):3998-4024. PMID: 36122322 | DOI: 10.1200/JCO.22.01357
Key recommendations (evidence-based, moderate strength):
  • Acupuncture: Recommended for aromatase inhibitor-related joint pain; may be used for general cancer pain
  • Acupressure/Reflexology: May be offered for general cancer or musculoskeletal pain
  • Hypnosis: May be used for procedural pain
  • Massage: Recommended for pain during palliative or hospice care

4. MASCC-ASCO Multi-Society Guideline: Opioid Conversion in Cancer (2025)

Citation: Davis MP, Davies A, McPherson ML, et al. Support Care Cancer. 2025 Mar 3. PMID: 40029420 | DOI: 10.1007/s00520-025-09286-z
This multi-society 2025 guideline (MASCC + ASCO + AAHPM + HPNA + NICSO) addresses practical opioid rotation/conversion:
  • Based on 208 studies + modified Delphi consensus (58/84 statements reached 75% agreement)
  • Covers pre-conversion assessment, conversion strategies, and post-conversion monitoring
  • Goal: reduce risk of overdosing or under-dosing during opioid-to-opioid rotation or route switching
  • Highlights variability in conversion ratios, especially for methadone

5. Companion Commentary on the "Reality Gap" (2025)

Citation: Crowley F, Smith CB, Arnold RM, Afezolli D. Cancer. 2025;131(1):e35656. PMID: 39560401
This 2025 commentary from Mount Sinai highlights the implementation gap - despite strong evidence and guideline recommendations, most cancer patients worldwide (including in LMICs like India) still do not receive early specialist palliative care. Key barriers identified: workforce shortages, poor reimbursement, cultural stigma around "palliative = giving up," and access in rural/semi-urban settings.

Summary Table: ASCO Palliative Care Guidelines at a Glance

GuidelineYearJournalPMIDFocus
Palliative Care Integration2024J Clin Oncol38748941Core - early integration, all advanced cancer
Opioids for Cancer Pain2023J Clin Oncol36469839Opioid initiation & titration
Integrative Medicine (SIO-ASCO)2022J Clin Oncol36122322Acupuncture, massage, hypnosis
Opioid Conversion (MASCC-ASCO)2025Support Care Cancer40029420Opioid rotation/switching

Key Takeaway for Indian Oncology Practice

The 2024 ASCO guideline specifically recognises geographic and equity barriers to palliative care delivery - directly relevant to India. The recommendation for early integration starting from the time of diagnosis of advanced cancer (not at end-of-life) aligns with AIIMS and NMHC national palliative care policy directions. The endorsement of telehealth and generalist oncologist-delivered supportive care as complements to specialist teams is especially actionable in resource-limited settings.
This is a shared conversation. Sign in to Orris to start your own chat.