This is as the refferal and make the SOAP analysis of the sandhya case sheet
python3 << 'EOF' from pptx import Presentation pptx_path = "/tmp/workspace/attachments/d7466835-bc19-436c-be9d-7de0c22d6fbf/5_6095778492585089495 (1).pptx" prs = Presentation(pptx_path) for i, slide in enumerate(prs.slides, 1): print(f"\n=== SLIDE {i} ===") for shape in slide.shapes: if shape.has_text_frame and shape.text_frame.text.strip(): print(shape.text_frame.text) EOF
python3 << 'EOF' import pdfplumber pdf_path = "/tmp/workspace/attachments/d7466835-bc19-436c-be9d-7de0c22d6fbf/Sandhya .pdf" with pdfplumber.open(pdf_path) as pdf: print(f"Total pages: {len(pdf.pages)}") for i, page in enumerate(pdf.pages, 1): print(f"\n=== PAGE {i} ===") text = page.extract_text() if text: print(text) else: print("[No text extracted - may be image-based]") EOF
python3 << 'EOF' # Convert PDF pages to images using pypdf + PIL from pypdf import PdfReader import subprocess, os pdf_path = "/tmp/workspace/attachments/d7466835-bc19-436c-be9d-7de0c22d6fbf/Sandhya .pdf" # Try using pdf2image if available try: from pdf2image import convert_from_path images = convert_from_path(pdf_path, dpi=150, first_page=1, last_page=5) os.makedirs("/tmp/workspace/sandhya_pages", exist_ok=True) for i, img in enumerate(images, 1): img.save(f"/tmp/workspace/sandhya_pages/page_{i}.png") print(f"Converted {len(images)} pages") except Exception as e: print(f"pdf2image error: {e}") # Try using pypdf to extract images reader = PdfReader(pdf_path) print(f"Pages: {len(reader.pages)}") for i, page in enumerate(reader.pages[:5], 1): images = page.images print(f"Page {i}: {len(images)} images") for j, img in enumerate(images): path = f"/tmp/workspace/sandhya_pages/page_{i}_img_{j}.{img.name.split('.')[-1]}" os.makedirs("/tmp/workspace/sandhya_pages", exist_ok=True) with open(path, "wb") as f: f.write(img.data) print(f" Saved: {path}") EOF
python3 << 'EOF' from pdf2image import convert_from_path import os pdf_path = "/tmp/workspace/attachments/d7466835-bc19-436c-be9d-7de0c22d6fbf/Sandhya .pdf" os.makedirs("/tmp/workspace/sandhya_pages", exist_ok=True) # Convert all 46 pages in batches - first get pages 1-20 images = convert_from_path(pdf_path, dpi=120, first_page=1, last_page=20) for i, img in enumerate(images, 1): img.save(f"/tmp/workspace/sandhya_pages/page_{i}.png") print(f"Converted pages 1-20") images2 = convert_from_path(pdf_path, dpi=120, first_page=21, last_page=46) for i, img in enumerate(images2, 21): img.save(f"/tmp/workspace/sandhya_pages/page_{i}.png") print(f"Converted pages 21-46") EOF
python3 << 'EOF' from PIL import Image import os # Create composite images: combine multiple pages into one tall image per call # Group pages: 1-8, 9-16, 17-24, 25-32, 33-46 def combine_pages(page_nums, output_path, max_width=1000): images = [] for n in page_nums: path = f"/tmp/workspace/sandhya_pages/page_{n}.png" if os.path.exists(path): img = Image.open(path) # Resize to uniform width ratio = max_width / img.width new_h = int(img.height * ratio) img = img.resize((max_width, new_h), Image.LANCZOS) images.append(img) if not images: return total_h = sum(img.height for img in images) combined = Image.new("RGB", (max_width, total_h), (255, 255, 255)) y = 0 for img in images: combined.paste(img, (0, y)) y += img.height combined.save(output_path, "PNG") print(f"Saved {output_path} ({len(images)} pages, {total_h}px tall)") os.makedirs("/tmp/workspace/sandhya_combined", exist_ok=True) combine_pages(range(1, 7), "/tmp/workspace/sandhya_combined/group1.png") combine_pages(range(7, 13), "/tmp/workspace/sandhya_combined/group2.png") combine_pages(range(13, 20), "/tmp/workspace/sandhya_combined/group3.png") combine_pages(range(20, 30), "/tmp/workspace/sandhya_combined/group4.png") combine_pages(range(30, 47), "/tmp/workspace/sandhya_combined/group5.png") EOF
python3 << 'EOF' # Extract text from individual pages using pytesseract if available, otherwise use pypdf image extraction import subprocess # Check if tesseract is available result = subprocess.run(["which", "tesseract"], capture_output=True, text=True) print(f"Tesseract: {result.stdout.strip() or 'not found'}") # Try pytesseract try: import pytesseract from PIL import Image img = Image.open("/tmp/workspace/sandhya_pages/page_1.png") text = pytesseract.image_to_string(img) print("Page 1 OCR:") print(text[:2000]) except Exception as e: print(f"pytesseract error: {e}") # Try easyocr try: import easyocr reader = easyocr.Reader(['en']) result = reader.readtext("/tmp/workspace/sandhya_pages/page_1.png", detail=0) print("EasyOCR page 1:", result) except Exception as e: print(f"easyocr error: {e}") EOF
python3 -c " try: import pytesseract from PIL import Image img = Image.open('/tmp/workspace/sandhya_pages/page_1.png') text = pytesseract.image_to_string(img) print('Page 1:') print(text[:3000]) except Exception as e: print(f'pytesseract error: {e}') try: import easyocr print('easyocr available') except Exception as e: print(f'easyocr error: {e}') "
pip install --user pytesseract 2>&1 | tail -3
python3 -c " import sys sys.path.insert(0, '/opt/amazon/genesis1p-tools/var/.local/lib/python3.12/site-packages') import pytesseract from PIL import Image # Test if tesseract binary available import shutil print('Tesseract binary:', shutil.which('tesseract')) "
Note on the Sandhya PDF: The PDF (46 pages) is entirely image/scan-based. OCR (Tesseract) is not available in this sandbox, and the analyse_media tool requires a public HTTP URL. All clinical data below is therefore drawn from the PPTX referral presentation, which contains the complete case information.
| Parameter | Value | Normal Range | Status |
|---|---|---|---|
| Blood Pressure | 100/70 mmHg | 120/80 mmHg | Low-normal / Hypotension |
| Pulse Rate | 111/min | 60-100/min | Tachycardia |
| Respiratory Rate | 20/min | 12-20/min | High-normal |
| Temperature | 98.6°F | 98.6°F | Normal (Afebrile) |
| # | Problem | Evidence |
|---|---|---|
| 1 | DCLD with decompensation | Icterus, ascites (free fluid + RHC tenderness), tachycardia |
| 2 | Hypokalemia (probable) | Muscle cramps B/L UL, giddiness - classic hypokalemia features; KCl prescribed |
| 3 | Ascites | Free fluid on P/A examination, fluid restriction and salt restriction ordered |
| 4 | Hepatic dysfunction | Icterus, right hypochondrial tenderness, DCLD diagnosis |
| 5 | Nutritional deficiency | Alcohol history + DCLD → thiamine (Vit B1), Vitamin B12/B-complex, Vitamin E (Evion LC), Vitamin D/Calcium (Shelcal HD) supplemented |
| 6 | Peptic disease / GERD | Sompraz D 40mg (Pantoprazole + Domperidone) prescribed |
| 7 | Edema / Fluid overload | Dytor 5mg (Torasemide - loop diuretic) prescribed |
| 8 | Right eye cataract | Incidental finding; likely alcohol/steroid-related or unrelated; needs ophthalmology referral |
| 9 | Muscle spasticity/cramps | Baclofen prescribed (muscle relaxant) |
| Drug | Dose/Frequency | Indication | Class |
|---|---|---|---|
| T. Sompraz D 40mg | 1-0-0 (morning) | Gastroprotection / GERD | PPI + Prokinetic (Pantoprazole + Domperidone) |
| T. Thiamine 100mg | 1-0-1 (BD) | Wernicke's prophylaxis / Vit B1 deficiency | Vitamin supplement |
| T. Dytor 5mg | 1-1-0 (BD) | Ascites / edema management | Loop diuretic (Torasemide) |
| T. UDCA 300mg | 1-0-1 (BD) | Hepatoprotection, cholestasis | Ursodeoxycholic acid |
| T. Evion LC | 1-0-0 | Antioxidant / Vitamin E supplementation | Fat-soluble vitamin |
| T. Shelcal HD | 0-0-1 (night) | Calcium + Vitamin D supplementation | Mineral supplement |
| T. Neurobion Forte | 1-0-0 | B-complex (B1, B6, B12) supplementation | Vitamin supplement |
| T. VSL | 1-0-0 | Gut microbiome support / probiotic | Probiotic |
| T. Baclofen | 1-0-0 | Muscle cramps / spasticity | GABA-B agonist / Muscle relaxant |
| Syp. KCl 5ml TDS | 5ml three times daily | Hypokalemia correction | Potassium supplement |
| Parameter | Frequency | Target |
|---|---|---|
| Serum K+ | Weekly initially | 3.5 - 5.0 mEq/L |
| Serum Na+ | Weekly | 135-145 mEq/L |
| LFT (ALT, AST, Bilirubin, Albumin) | At follow-up (2-4 weeks) | Trending improvement |
| PT/INR | At follow-up | Monitor coagulopathy |
| Body weight | Daily | Stable / decreasing (ascites reduction) |
| Blood pressure & pulse | Daily | Hemodynamic stability |
| Signs of hepatic encephalopathy | Every visit | Absence of flap, orientation intact |
Note: The Sandhya PDF (46 pages) is an image-scanned case sheet containing the original hospital records. It could not be automatically read (OCR not available in this environment). If specific lab values (LFT, CBC, electrolytes, ultrasound reports) from the case sheet are needed to enrich the Objective section, please share a text-readable version of those results or type out key lab values, and I will incorporate them into the SOAP analysis.
Create for this case sheet
Create a professional SOAP Analysis Word document (.docx) for a pharmacy clinical case. Save it to: /tmp/workspace/sandhya_output/Sandhya_SOAP_Analysis.docx Use python-docx to create the document. Here is the full clinical data: PATIENT: 32-year-old male, admitted Nandha Medical College & Hospital on 25/03/2026, discharged 28/03/2026. CASE DETAILS: - Chief Complaints: Muscle cramps in bilateral upper limbs (B/L UL) and right side for 10 days, giddiness, abdominal pain - Past History: Known case of DCLD (Decompensated Chronic Liver Disease) for 6 months, on irregular medication - Personal History: Mixed diet, Alcohol use for 7 years - Vitals: BP 100/70 mmHg, PR 111/min, RR 20/min, Temp 98.6°F - General: Conscious, oriented, afebrile, Icterus present (+), Right eye cataract (L/E) - CVS: S1S2+; RS: BAE+; P/A: Right hypochondrial tenderness, Free fluid (+) [ascites]; CNS: NFND - Final Diagnosis: DCLD (Decompensated Chronic Liver Disease) - Non-Pharm: Fluid restriction <1L/day, Salt restriction <2g/day DISCHARGE MEDICATIONS: 1. T. Sompraz D 40mg - 1-0-0 (Pantoprazole + Domperidone - gastroprotection) 2. T. Thiamine 100mg - 1-0-1 (Vitamin B1 deficiency/Wernicke's prophylaxis) 3. T. Dytor 5mg - 1-1-0 (Torasemide - loop diuretic for ascites/edema) 4. T. UDCA 300mg - 1-0-1 (Ursodeoxycholic acid - hepatoprotection) 5. T. Evion LC - 1-0-0 (Vitamin E) 6. T. Shelcal HD - 0-0-1 (Calcium + Vitamin D) 7. T. Neurobion Forte - 1-0-0 (B-complex vitamins) 8. T. VSL - 1-0-0 (Probiotic) 9. T. Baclofen - 1-0-0 (Muscle relaxant for cramps) 10. Syp. KCl 5ml TDS (Potassium chloride for hypokalemia) SOAP STRUCTURE to include: S - SUBJECTIVE: - Patient demographics - Chief complaints with duration - History of Present Illness - Past Medical History - Personal/Social History O - OBJECTIVE: - Vital signs table - General examination - Systemic examination - Diagnosis A - ASSESSMENT: - Primary diagnosis discussion (DCLD with decompensation) - Identified clinical problems (numbered list with evidence): Hypokalemia (muscle cramps, giddiness → KCl prescribed), Ascites (free fluid, RHC tenderness → fluid/salt restriction, Dytor), Hepatic dysfunction (icterus), Nutritional deficiencies (alcohol-related → multiple supplements), GERD/peptic disease (Sompraz D), Muscle cramps (Baclofen) - Drug-related problems: (1) Domperidone contraindicated in severe hepatic failure - QT risk; (2) Baclofen accumulates in hepatic impairment - monitor encephalopathy; (3) Dytor + KCl appropriate combination - Drug therapy table (all 10 medications with dose, frequency, indication, class) P - PLAN: - Pharmacological plan (6 points) - Non-pharmacological plan (alcohol abstinence, diet, fluid/salt restriction, weight monitoring, ophthalmology referral) - Monitoring parameters table (K+, Na+, LFT, PT/INR, body weight, BP/PR, encephalopathy signs) - Patient counseling points (6 points) FORMATTING REQUIREMENTS: - Use python-docx - Title: "SOAP ANALYSIS - SANDHYA CASE" in bold, centered, dark blue (0,0,128) - Subtitle: "Pharmacotherapeutics-I | Nandha Medical College & Hospital" centered - Date line: "Date of Admission: 25/03/2026 | Date of Discharge: 28/03/2026" centered - Section headers (S, O, A, P) as Heading 1 style, colored dark blue, with full label e.g. "S - SUBJECTIVE" - Sub-section headers as Heading 2 - Tables with header rows shaded light blue (173, 216, 230) - Normal text font: Arial 11pt - Add a horizontal rule line before each SOAP section - Save the file and print "SUCCESS: /tmp/workspace/sandhya_output/Sandhya_SOAP_Analysis.docx" Make sure to create the output directory first with os.makedirs. Return: confirmation the file was created successfully with its path.
Create a professional PowerPoint SOAP Analysis presentation (.pptx) for a pharmacy student clinical case. Save it to: /tmp/workspace/sandhya_output/Sandhya_SOAP_Presentation.pptx Use python-pptx to create the presentation. Here is the full clinical data: PATIENT: 32-year-old male, admitted Nandha Medical College & Hospital on 25/03/2026, discharged 28/03/2026. Presented by: Sindhana M, 2nd Pharm D | Pharmacotherapeutics-I Second Sessional Practical Examination CASE DETAILS: - Chief Complaints: Muscle cramps in bilateral upper limbs (B/L UL) and right side for 10 days, giddiness, abdominal pain - Past History: Known case of DCLD (Decompensated Chronic Liver Disease) for 6 months, on irregular medication - Personal History: Mixed diet, Alcohol use for 7 years - Vitals: BP 100/70 mmHg, PR 111/min, RR 20/min, Temp 98.6°F - General: Conscious, oriented, afebrile, Icterus present (+), Right eye cataract (L/E) - CVS: S1S2+; RS: BAE+; P/A: Right hypochondrial tenderness, Free fluid (+) [ascites]; CNS: NFND - Final Diagnosis: DCLD (Decompensated Chronic Liver Disease) DISCHARGE MEDICATIONS: 1. T. Sompraz D 40mg - 1-0-0 2. T. Thiamine 100mg - 1-0-1 3. T. Dytor 5mg - 1-1-0 4. T. UDCA 300mg - 1-0-1 5. T. Evion LC - 1-0-0 6. T. Shelcal HD - 0-0-1 7. T. Neurobion Forte - 1-0-0 8. T. VSL - 1-0-0 9. T. Baclofen - 1-0-0 10. Syp. KCl 5ml TDS SLIDE STRUCTURE (create exactly these slides): Slide 1 - TITLE SLIDE: Title: "SOAP ANALYSIS" Subtitle: "Sandhya Case | Pharmacotherapeutics-I" Below: "Presented by: Sindhana M | 2nd Pharm D" Below: "Nandha Medical College & Hospital" Background: dark navy blue (#003366), text white Slide 2 - SUBJECTIVE (Patient Profile): Title: "S - SUBJECTIVE" Content: • Patient: 32-year-old male • Admitted: 25/03/2026 | Discharged: 28/03/2026 • Chief Complaints: - Muscle cramps (B/L UL & right side) - 10 days - Giddiness - Abdominal pain • Past History: K/C/O DCLD - 6 months, irregular medication • Personal History: Mixed diet | Alcohol - 7 years Slide 3 - OBJECTIVE (Vitals & Examination): Title: "O - OBJECTIVE" Two columns: Left - Vital Signs: BP: 100/70 mmHg (Low-normal) PR: 111/min (Tachycardia) RR: 20/min (Normal) Temp: 98.6°F (Afebrile) Right - Examination: General: Conscious, Oriented, Icterus (+) CVS: S1S2 heard RS: BAE present P/A: RHC tenderness, Free fluid (+) CNS: NFND L/E: Right eye cataract Slide 4 - DIAGNOSIS: Title: "Final Diagnosis" Large centered text: "DCLD" Below: "Decompensated Chronic Liver Disease" Bullet points: • Alcohol-induced (7 years history) • Presenting with decompensation features • Ascites, Icterus, Tachycardia Slide 5 - ASSESSMENT - Clinical Problems: Title: "A - ASSESSMENT: Clinical Problems" Table with 3 columns: Problem | Evidence | Management Row 1: Hypokalemia | Muscle cramps, Giddiness | Syp. KCl 5ml TDS Row 2: Ascites | Free fluid+, RHC tenderness | Dytor 5mg, Fluid/Salt restriction Row 3: Hepatic dysfunction | Icterus, DCLD | UDCA 300mg Row 4: Nutritional deficiency | Alcohol 7 yrs, DCLD | Thiamine, Neurobion, Shelcal, Evion Row 5: GERD/Peptic disease | Abdominal pain | Sompraz D 40mg Row 6: Muscle cramps | B/L UL cramps | Baclofen Slide 6 - ASSESSMENT - Drug Therapy: Title: "A - ASSESSMENT: Drug Chart" Table with 4 columns: Drug | Dose/Freq | Indication | Class List all 10 medications: Sompraz D 40mg | 1-0-0 | GERD/Gastroprotection | PPI + Prokinetic Thiamine 100mg | 1-0-1 | Vit B1 / Wernicke's | Vitamin Dytor 5mg | 1-1-0 | Ascites/Edema | Loop Diuretic UDCA 300mg | 1-0-1 | Hepatoprotection | Bile acid Evion LC | 1-0-0 | Antioxidant | Vit E Shelcal HD | 0-0-1 | Ca + Vit D | Mineral/Vitamin Neurobion Forte | 1-0-0 | B-complex | Multivitamin VSL | 1-0-0 | Gut flora | Probiotic Baclofen | 1-0-0 | Muscle cramps | Muscle relaxant Syp. KCl 5ml | TDS | Hypokalemia | K+ supplement Slide 7 - ASSESSMENT - Drug Related Problems: Title: "A - ASSESSMENT: Drug Related Problems" Bullet points: • DRP 1: Domperidone (in Sompraz D) - contraindicated in severe hepatic impairment (QT prolongation risk) → Review and consider plain Pantoprazole • DRP 2: Baclofen in DCLD - hepatic metabolism, risk of accumulation → Monitor for encephalopathy, use lowest effective dose • DRP 3: Torasemide (Dytor) causes K+ loss → KCl supplementation appropriate, monitor serum electrolytes • DRP 4: Non-adherence to prior DCLD medication → Patient counseling needed Slide 8 - PLAN - Pharmacological & Non-Pharmacological: Title: "P - PLAN" Left column - Pharmacological: • Continue discharge medications • Monitor serum K+, Na+ weekly • Review Domperidone - consider switch • Titrate Baclofen cautiously • Thiamine/B-complex essential • Repeat LFTs at follow-up Right column - Non-Pharmacological: • Strict alcohol abstinence • High-protein, low-sodium diet • Fluid restriction < 1L/day • Salt restriction < 2g/day • Daily weight monitoring • Ophthalmology referral (cataract) Slide 9 - PLAN - Monitoring: Title: "P - PLAN: Monitoring Parameters" Table with 3 columns: Parameter | Frequency | Target Serum K+ | Weekly | 3.5-5.0 mEq/L Serum Na+ | Weekly | 135-145 mEq/L LFT | At follow-up (2-4 wks) | Trending improvement PT/INR | At follow-up | Monitor coagulopathy Body Weight | Daily | Stable/decreasing BP & PR | Daily | Hemodynamic stability Signs of encephalopathy | Every visit | Absent Slide 10 - PLAN - Patient Counseling: Title: "P - PLAN: Patient Counseling" Bullet points: • Complete alcohol abstinence - primary driver of disease progression • Take all medications regularly (previous non-adherence noted) • Maintain low-salt diet and fluid restriction to control ascites • Warning signs: Increased abdomen swelling, confusion, vomiting blood, fever → ER immediately • KCl syrup: dilute with water before taking • Follow-up: 1-2 weeks for electrolyte check Slide 11 - THANK YOU: Title: "Thank You" Subtitle: "Sindhana M | 2nd Pharm D" "Nandha Medical College & Hospital" "Pharmacotherapeutics-I" DESIGN REQUIREMENTS: - Color scheme: Navy blue (#003366) headers, white/light blue content areas - Title slides: dark background, white text - Content slides: white background, navy header bar - Tables: header row in light blue (#ADD8E6), alternating row shading (#F0F8FF) - Use python-pptx from pptx library - Slide dimensions: standard widescreen (13.33 x 7.5 inches) - Font: Title slides use bold 36pt, section titles 28pt bold, body text 16-18pt - Make it look like a professional medical education presentation Make sure to create the output directory first: os.makedirs("/tmp/workspace/sandhya_output", exist_ok=True) Save to: /tmp/workspace/sandhya_output/Sandhya_SOAP_Presentation.pptx Print "SUCCESS: /path" at the end. Return confirmation the file was created with path.
~/create_soap_docx.py
import os
from docx import Document
from docx.shared import Pt, Inches, RGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_TABLE_ALIGNMENT
from docx.oxml.ns import qn
from docx.oxml import OxmlElement
from io import BytesIO
# ── Output path ─────────────────────────────────────────────────────────────
OUT_DIR = "/tmp/workspace/sandhya_output"
OUT_FILE = os.path.join(OUT_DIR, "Sandhya_SOAP_Analysis.docx")
os.makedirs(OUT_DIR, exist_ok=True)
# ── Colour constants ─────────────────────────────────────────────────────────
DARK_BLUE = RGBColor(0, 0, 128)
LIGHT_BLUE = "ADD8E6" # hex for shade_cell (173,216,230)
WHITE = RGBColor(255, 255, 255)
BLACK = RGBColor(0, 0, 0)
# ── Document setup ───────────────────────────────────────────────────────────
doc = Document()
# Page margins
section = doc.sections[0]
section.page_width = Inches(8.5)
section.page_height = Inches(11)
section.top_margin = Inches(1)
section.bottom_margin = Inches(1)
section.left_margin = Inches(1)
section.right_margin = Inches(1)
# Normal style → Arial 11
normal = doc.styles["Normal"]
normal.font.name = "Arial"
normal.font.size = Pt(11)
# Heading 1 style → dark blue, bold, 14
h1_style = doc.styles["Heading 1"]
h1_style.font.name = "Arial"
h1_style.font.size = Pt(14)
h1_style.font.bold = True
h1_style.font.color.rgb = DARK_BLUE
# Heading 2 style → dark blue, bold, 12
h2_style = doc.styles["Heading 2"]
h2_style.font.name = "Arial"
h2_style.font.size = Pt(12)
h2_style.font.bold = True
h2_style.font.color.rgb = DARK_BLUE
# ── Helper functions ─────────────────────────────────────────────────────────
def shade_cell(cell, hex_fill):
"""Apply background shading to a table cell."""
tcPr = cell._tc.get_or_add_tcPr()
shd = OxmlElement("w:shd")
shd.set(qn("w:val"), "clear")
shd.set(qn("w:color"), "auto")
shd.set(qn("w:fill"), hex_fill)
tcPr.append(shd)
def add_horizontal_rule(doc):
"""Add a horizontal line (border on a paragraph)."""
p = doc.add_paragraph()
pPr = p._p.get_or_add_pPr()
pBdr = OxmlElement("w:pBdr")
bottom = OxmlElement("w:bottom")
bottom.set(qn("w:val"), "single")
bottom.set(qn("w:sz"), "6")
bottom.set(qn("w:space"), "1")
bottom.set(qn("w:color"), "000080") # dark blue
pBdr.append(bottom)
pPr.append(pBdr)
return p
def add_section_heading(doc, text):
"""Add a horizontal rule then a Heading 1 paragraph."""
add_horizontal_rule(doc)
h = doc.add_heading(text, level=1)
h.alignment = WD_ALIGN_PARAGRAPH.LEFT
# Ensure colour is set on the run too
for run in h.runs:
run.font.color.rgb = DARK_BLUE
return h
def add_sub_heading(doc, text):
"""Add a Heading 2 paragraph."""
h = doc.add_heading(text, level=2)
h.alignment = WD_ALIGN_PARAGRAPH.LEFT
for run in h.runs:
run.font.color.rgb = DARK_BLUE
return h
def body_para(doc, text, bold=False, italic=False):
"""Add a normal body paragraph."""
p = doc.add_paragraph()
p.style = doc.styles["Normal"]
run = p.add_run(text)
run.bold = bold
run.italic = italic
return p
def bullet(doc, text, level=1):
"""Add a bulleted list item."""
style = "List Bullet" if level == 1 else f"List Bullet {level}"
p = doc.add_paragraph(text, style=style)
return p
def numbered(doc, text, level=1):
"""Add a numbered list item."""
style = "List Number" if level == 1 else f"List Number {level}"
p = doc.add_paragraph(text, style=style)
return p
def add_table_with_headers(doc, headers, rows_data, col_widths=None):
"""
Create a Table Grid table with shaded header row.
headers : list of str
rows_data : list of list of str
col_widths : list of Inches() values (must sum to <=6.5 in)
"""
n_cols = len(headers)
table = doc.add_table(rows=1 + len(rows_data), cols=n_cols)
table.style = "Table Grid"
table.alignment = WD_TABLE_ALIGNMENT.CENTER
# Default equal widths
if col_widths is None:
w = Inches(6.5 / n_cols)
col_widths = [w] * n_cols
# Header row
hdr_cells = table.rows[0].cells
for i, hdr in enumerate(headers):
cell = hdr_cells[i]
cell.width = col_widths[i]
shade_cell(cell, LIGHT_BLUE)
p = cell.paragraphs[0]
run = p.add_run(hdr)
run.bold = True
run.font.size = Pt(10)
run.font.name = "Arial"
# Data rows
for r_idx, row_data in enumerate(rows_data):
row_cells = table.rows[r_idx + 1].cells
for c_idx, val in enumerate(row_data):
cell = row_cells[c_idx]
cell.width = col_widths[c_idx]
p = cell.paragraphs[0]
run = p.add_run(str(val))
run.font.size = Pt(10)
run.font.name = "Arial"
return table
# ════════════════════════════════════════════════════════════════════════════
# TITLE BLOCK
# ════════════════════════════════════════════════════════════════════════════
# Main title
title_p = doc.add_paragraph()
title_p.alignment = WD_ALIGN_PARAGRAPH.CENTER
run = title_p.add_run("SOAP ANALYSIS - SANDHYA CASE")
run.bold = True
run.font.size = Pt(18)
run.font.name = "Arial"
run.font.color.rgb = DARK_BLUE
# Subtitle
sub_p = doc.add_paragraph()
sub_p.alignment = WD_ALIGN_PARAGRAPH.CENTER
sub_run = sub_p.add_run("Pharmacotherapeutics-I | Nandha Medical College & Hospital")
sub_run.font.size = Pt(12)
sub_run.font.name = "Arial"
sub_run.font.italic = True
# Date line
date_p = doc.add_paragraph()
date_p.alignment = WD_ALIGN_PARAGRAPH.CENTER
date_run = date_p.add_run("Date of Admission: 25/03/2026 | Date of Discharge: 28/03/2026")
date_run.font.size = Pt(11)
date_run.font.name = "Arial"
doc.add_paragraph() # spacer
# ════════════════════════════════════════════════════════════════════════════
# S – SUBJECTIVE
# ════════════════════════════════════════════════════════════════════════════
add_section_heading(doc, "S - SUBJECTIVE")
add_sub_heading(doc, "Patient Demographics")
add_table_with_headers(
doc,
headers=["Parameter", "Details"],
rows_data=[
["Name", "Sandhya (Case Patient)"],
["Age / Sex", "32 years / Male"],
["Hospital", "Nandha Medical College & Hospital"],
["Date of Admission", "25/03/2026"],
["Date of Discharge", "28/03/2026"],
["Duration of Stay", "3 days"],
],
col_widths=[Inches(2.5), Inches(4.0)]
)
doc.add_paragraph()
add_sub_heading(doc, "Chief Complaints")
bullet(doc, "Muscle cramps in bilateral upper limbs (B/L UL) and right side - 10 days duration")
bullet(doc, "Giddiness")
bullet(doc, "Abdominal pain")
add_sub_heading(doc, "History of Present Illness")
body_para(doc,
"A 32-year-old male, known case of Decompensated Chronic Liver Disease (DCLD) for the past "
"6 months, presented with complaints of muscle cramps involving bilateral upper limbs and the "
"right side of the body for 10 days, associated with giddiness and abdominal pain. The patient "
"has been on irregular medication for DCLD. Alcohol use history of 7 years is noted as a "
"significant contributing factor to the liver disease and associated nutritional deficiencies."
)
add_sub_heading(doc, "Past Medical History")
bullet(doc, "Known case of DCLD (Decompensated Chronic Liver Disease) - 6 months")
bullet(doc, "Irregular medication compliance for DCLD")
bullet(doc, "Right eye cataract (L/E - Left Eye, under evaluation)")
add_sub_heading(doc, "Personal / Social History")
bullet(doc, "Diet: Mixed diet")
bullet(doc, "Alcohol use: 7 years (significant risk factor for liver disease and nutritional deficiencies)")
bullet(doc, "Smoking: Not documented")
bullet(doc, "Occupation: Not documented")
# ════════════════════════════════════════════════════════════════════════════
# O – OBJECTIVE
# ════════════════════════════════════════════════════════════════════════════
add_section_heading(doc, "O - OBJECTIVE")
add_sub_heading(doc, "Vital Signs")
add_table_with_headers(
doc,
headers=["Parameter", "Value", "Normal Range", "Interpretation"],
rows_data=[
["Blood Pressure (BP)", "100/70 mmHg", "120/80 mmHg", "Hypotension"],
["Pulse Rate (PR)", "111 /min", "60-100 /min", "Tachycardia"],
["Respiratory Rate (RR)", "20 /min", "12-20 /min", "Normal/Upper limit"],
["Temperature", "98.6 degF", "97-99 degF", "Normal (Afebrile)"],
],
col_widths=[Inches(2.0), Inches(1.5), Inches(1.7), Inches(1.3)]
)
doc.add_paragraph()
add_sub_heading(doc, "General Examination")
add_table_with_headers(
doc,
headers=["Finding", "Status"],
rows_data=[
["Consciousness", "Conscious and oriented"],
["Fever", "Afebrile"],
["Icterus", "Present (+) - indicates hepatic involvement"],
["Pallor", "Not documented"],
["Cyanosis", "Not documented"],
["Clubbing", "Not documented"],
["Lymphadenopathy", "Not documented"],
["Oedema", "Not documented"],
["Right Eye Cataract", "Present (L/E - under evaluation)"],
],
col_widths=[Inches(3.0), Inches(3.5)]
)
doc.add_paragraph()
add_sub_heading(doc, "Systemic Examination")
add_table_with_headers(
doc,
headers=["System", "Finding"],
rows_data=[
["CVS (Cardiovascular)", "S1, S2 heard; no murmurs (S1S2+)"],
["RS (Respiratory System)", "Bilateral air entry present (BAE+)"],
["P/A (Per Abdomen)", "Right hypochondrial tenderness; Free fluid present (+) - Ascites"],
["CNS (Central Nervous System)", "No focal neurological deficit (NFND)"],
],
col_widths=[Inches(2.5), Inches(4.0)]
)
doc.add_paragraph()
add_sub_heading(doc, "Final Diagnosis")
p = doc.add_paragraph()
p.style = doc.styles["Normal"]
run = p.add_run("Decompensated Chronic Liver Disease (DCLD)")
run.bold = True
run.font.size = Pt(11)
run.font.color.rgb = DARK_BLUE
# ════════════════════════════════════════════════════════════════════════════
# A – ASSESSMENT
# ════════════════════════════════════════════════════════════════════════════
add_section_heading(doc, "A - ASSESSMENT")
add_sub_heading(doc, "Primary Diagnosis Discussion")
body_para(doc,
"The patient presents with Decompensated Chronic Liver Disease (DCLD), a stage of chronic "
"liver disease in which the liver is no longer able to compensate for its impaired function. "
"Decompensation is evidenced by the presence of ascites (free fluid in abdomen), icterus "
"(jaundice), haemodynamic instability (hypotension, tachycardia), and electrolyte imbalances "
"(hypokalemia manifesting as muscle cramps and giddiness). Chronic alcohol use for 7 years "
"is the primary etiological factor, contributing to hepatocellular damage, portal hypertension, "
"ascites, and multiple nutritional deficiencies."
)
add_sub_heading(doc, "Identified Clinical Problems")
numbered(doc, "Hypokalemia")
p = doc.add_paragraph(style="List Bullet 2")
p.add_run("Evidence: Muscle cramps in B/L upper limbs, giddiness")
p = doc.add_paragraph(style="List Bullet 2")
p.add_run("Management: Syrup KCl 5 mL TDS prescribed for potassium replacement")
numbered(doc, "Ascites / Fluid Overload")
p = doc.add_paragraph(style="List Bullet 2")
p.add_run("Evidence: Free fluid on abdominal examination, right hypochondrial tenderness")
p = doc.add_paragraph(style="List Bullet 2")
p.add_run("Management: T. Dytor 5 mg (Torasemide - loop diuretic); Fluid restriction <1 L/day; Salt restriction <2 g/day")
numbered(doc, "Hepatic Dysfunction with Jaundice")
p = doc.add_paragraph(style="List Bullet 2")
p.add_run("Evidence: Icterus (+), known DCLD for 6 months, elevated bilirubin expected")
p = doc.add_paragraph(style="List Bullet 2")
p.add_run("Management: T. UDCA 300 mg BD (hepatoprotective, improves bile flow)")
numbered(doc, "Alcohol-related Nutritional Deficiencies")
p = doc.add_paragraph(style="List Bullet 2")
p.add_run("Evidence: 7-year alcohol history - Thiamine (B1), B-complex, Vitamin E, Calcium/Vit D deficiencies expected")
p = doc.add_paragraph(style="List Bullet 2")
p.add_run("Management: T. Thiamine 100 mg BD, T. Neurobion Forte OD, T. Evion LC OD, T. Shelcal HD OD")
numbered(doc, "GERD / Gastric Mucosal Protection")
p = doc.add_paragraph(style="List Bullet 2")
p.add_run("Evidence: Abdominal pain; proton pump inhibitor indicated to protect gastric mucosa in liver disease")
p = doc.add_paragraph(style="List Bullet 2")
p.add_run("Management: T. Sompraz D 40 mg OD (Pantoprazole + Domperidone)")
numbered(doc, "Muscle Cramps")
p = doc.add_paragraph(style="List Bullet 2")
p.add_run("Evidence: Bilateral upper limb cramps - may be multifactorial (hypokalemia, hepatic disease)")
p = doc.add_paragraph(style="List Bullet 2")
p.add_run("Management: T. Baclofen OD (GABA-B agonist, muscle relaxant)")
numbered(doc, "Gut Dysbiosis / Hepatic Encephalopathy Prevention")
p = doc.add_paragraph(style="List Bullet 2")
p.add_run("Evidence: Liver disease associated with gut microbiome disruption and risk of hepatic encephalopathy")
p = doc.add_paragraph(style="List Bullet 2")
p.add_run("Management: T. VSL (probiotic) OD")
doc.add_paragraph()
add_sub_heading(doc, "Drug-Related Problems (DRPs)")
p = doc.add_paragraph()
p.style = doc.styles["Normal"]
run = p.add_run("DRP 1: Domperidone in Severe Hepatic Failure - Contraindication / Risk")
run.bold = True
body_para(doc,
"Domperidone (component of Sompraz D) is contraindicated or requires extreme caution in severe "
"hepatic impairment. It is extensively metabolised by the liver (CYP3A4), and accumulation in "
"hepatic failure can prolong the QTc interval, increasing the risk of ventricular arrhythmias "
"(Torsades de Pointes). Recommendation: Review use; consider switching to plain Pantoprazole "
"monotherapy or an alternative prokinetic such as Metoclopramide at reduced dose with close ECG monitoring."
)
p = doc.add_paragraph()
p.style = doc.styles["Normal"]
run = p.add_run("DRP 2: Baclofen Accumulation in Hepatic Impairment - Encephalopathy Risk")
run.bold = True
body_para(doc,
"Baclofen is primarily renally excreted but CNS depression is potentiated in hepatic impairment. "
"There is an additive risk of precipitating or worsening hepatic encephalopathy. Recommendation: "
"Use at the lowest effective dose, monitor for confusion, asterixis, or altered sensorium "
"suggestive of encephalopathy. Consider alternative non-pharmacological cramp management or "
"quinine sulfate (with caution) once hypokalemia is corrected."
)
p = doc.add_paragraph()
p.style = doc.styles["Normal"]
run = p.add_run("DRP 3: Dytor (Torasemide) + KCl - Appropriate Combination")
run.bold = True
body_para(doc,
"Torasemide (loop diuretic) causes urinary potassium wasting, leading to or worsening "
"hypokalemia. Co-prescribing KCl syrup is clinically appropriate and recommended to correct "
"and maintain potassium levels. Serum electrolytes (K+, Na+) should be monitored regularly "
"(every 48-72 hours during active diuresis). This combination is rational and well-managed."
)
doc.add_paragraph()
add_sub_heading(doc, "Drug Therapy Table")
add_table_with_headers(
doc,
headers=["#", "Drug", "Dose", "Frequency", "Class", "Indication"],
rows_data=[
["1", "T. Sompraz D 40 mg\n(Pantoprazole + Domperidone)", "40 mg", "1-0-0 (OD AM)", "PPI + Prokinetic", "Gastroprotection / GERD"],
["2", "T. Thiamine 100 mg", "100 mg", "1-0-1 (BD)", "Vitamin B1", "Thiamine deficiency / Wernicke's prophylaxis"],
["3", "T. Dytor 5 mg", "5 mg", "1-1-0 (BD AM+Noon)", "Loop Diuretic", "Ascites / Oedema management"],
["4", "T. UDCA 300 mg", "300 mg", "1-0-1 (BD)", "Bile acid / Hepatoprotective","Hepatoprotection, bile flow improvement"],
["5", "T. Evion LC", "Standard","1-0-0 (OD AM)", "Vitamin E", "Antioxidant, nutritional deficiency"],
["6", "T. Shelcal HD", "Standard","0-0-1 (OD PM)", "Calcium + Vitamin D", "Calcium/Vit D deficiency, bone health"],
["7", "T. Neurobion Forte", "Standard","1-0-0 (OD AM)", "B-complex vitamins", "Alcohol-related B-vitamin deficiency"],
["8", "T. VSL", "Standard","1-0-0 (OD AM)", "Probiotic", "Gut dysbiosis, hepatic encephalopathy prevention"],
["9", "T. Baclofen", "Standard","1-0-0 (OD AM)", "GABA-B agonist / Muscle relaxant","Muscle cramps"],
["10", "Syp. KCl 5 mL", "5 mL", "TDS", "Electrolyte supplement", "Hypokalemia correction"],
],
col_widths=[Inches(0.3), Inches(1.6), Inches(0.7), Inches(1.1), Inches(1.4), Inches(1.4)]
)
# ════════════════════════════════════════════════════════════════════════════
# P – PLAN
# ════════════════════════════════════════════════════════════════════════════
add_section_heading(doc, "P - PLAN")
add_sub_heading(doc, "Pharmacological Plan")
numbered(doc, "Electrolyte Correction: Continue Syrup KCl 5 mL TDS until serum potassium normalises (target K+ 3.5-5.0 mEq/L). "
"Repeat serum electrolytes every 48-72 hours during diuresis.")
numbered(doc, "Diuretic Therapy: Maintain T. Dytor 5 mg BD for ascites management. Titrate dose based on clinical response, "
"body weight changes, and renal function. Add spironolactone if ascites is refractory.")
numbered(doc, "Hepatoprotection: Continue T. UDCA 300 mg BD for hepatoprotective effect. Monitor liver function tests (LFT) "
"periodically to assess treatment response.")
numbered(doc, "Nutritional Supplementation: Continue Thiamine 100 mg BD (at least 3 months), Neurobion Forte OD, Evion LC OD, "
"and Shelcal HD OD to correct alcohol-related nutritional deficiencies and prevent Wernicke's encephalopathy.")
numbered(doc, "DRP Review - Domperidone: Review Sompraz D; consider switching to plain Pantoprazole 40 mg OD if prokinetic "
"effect is not clinically essential, to avoid QTc prolongation risk in severe hepatic failure. Obtain ECG.")
numbered(doc, "DRP Monitoring - Baclofen: Use at minimum effective dose; closely monitor for signs of hepatic encephalopathy "
"(asterixis, confusion, altered consciousness). Reassess once hypokalemia is corrected as cramps may resolve.")
doc.add_paragraph()
add_sub_heading(doc, "Non-Pharmacological Plan")
bullet(doc, "Alcohol abstinence: Complete and permanent cessation of alcohol - most critical intervention to slow disease progression")
bullet(doc, "Dietary modification: High-protein (1.2-1.5 g/kg/day), nutritionally balanced diet to counter malnutrition")
bullet(doc, "Fluid restriction: Less than 1 litre per day to prevent worsening of ascites")
bullet(doc, "Salt (sodium) restriction: Less than 2 grams per day to reduce fluid retention")
bullet(doc, "Daily weight monitoring: Weigh at the same time each morning; report weight gain >1 kg/day or >2 kg/week")
bullet(doc, "Ophthalmology referral: Right eye cataract noted; refer to ophthalmology department for evaluation and management")
bullet(doc, "Adequate rest: Avoid strenuous physical activity during active decompensation")
bullet(doc, "Regular follow-up: Scheduled outpatient visits every 4 weeks or earlier if symptoms worsen")
doc.add_paragraph()
add_sub_heading(doc, "Monitoring Parameters")
add_table_with_headers(
doc,
headers=["Parameter", "Frequency", "Target / Action Level"],
rows_data=[
["Serum Potassium (K+)", "Every 48-72 hrs during diuresis, then weekly", "3.5 - 5.0 mEq/L; supplement if low"],
["Serum Sodium (Na+)", "Weekly", "135-145 mEq/L; restrict fluid if low"],
["Liver Function Tests (LFT)", "Every 2-4 weeks", "Monitor bilirubin, ALT, AST, albumin, ALP"],
["PT / INR", "Every 2-4 weeks", "INR >1.5 indicates significant coagulopathy"],
["Body Weight", "Daily (morning, same time)", "Alert if gain >1 kg/day or >2 kg/week"],
["Blood Pressure / Pulse Rate", "Daily during admission; weekly outpatient", "Target BP >90/60 mmHg; HR 60-100/min"],
["Renal Function (BUN, Creatinine)","Weekly", "Monitor for hepatorenal syndrome"],
["Signs of Encephalopathy", "Daily clinical assessment", "Asterixis, altered consciousness - withhold Baclofen if present"],
["ECG (QTc interval)", "Before starting/continuing Domperidone", "QTc <440 ms (male); withhold if prolonged"],
["Abdominal Girth", "Weekly", "Reduction indicates ascites response to therapy"],
],
col_widths=[Inches(2.0), Inches(1.8), Inches(2.7)]
)
doc.add_paragraph()
add_sub_heading(doc, "Patient Counseling Points")
numbered(doc, "Alcohol cessation is the single most important step: Complete abstinence from alcohol is mandatory. "
"Further alcohol intake will rapidly worsen liver disease and can be life-threatening.")
numbered(doc, "Medication compliance: Take all prescribed medications regularly as directed. Do not stop any medicine "
"without consulting your doctor, even if you feel better.")
numbered(doc, "Diet and fluid restrictions: Follow a low-salt diet (less than 2 g/day) and restrict fluid intake to less "
"than 1 litre per day to control abdominal swelling (ascites). Eat a nutritious, protein-rich diet.")
numbered(doc, "Daily weight monitoring: Weigh yourself every morning. If your weight increases by more than 1 kg in one "
"day or 2 kg in one week, contact your doctor immediately as this may indicate fluid accumulation.")
numbered(doc, "Warning signs - seek immediate medical attention: Yellowing of skin/eyes worsening (jaundice), severe "
"confusion or altered behaviour (possible encephalopathy), increased abdominal swelling, vomiting blood, "
"or black tarry stools (GI bleeding).")
numbered(doc, "Eye care: You have been noted to have a cataract in the right eye. Please visit an ophthalmologist "
"for a detailed evaluation and appropriate management.")
# ── Final spacer & end note ──────────────────────────────────────────────────
doc.add_paragraph()
add_horizontal_rule(doc)
p = doc.add_paragraph()
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
run = p.add_run("--- End of SOAP Analysis ---")
run.italic = True
run.font.size = Pt(10)
run.font.name = "Arial"
run.font.color.rgb = RGBColor(100, 100, 100)
# ── Save ─────────────────────────────────────────────────────────────────────
buf = BytesIO()
doc.save(buf)
with open(OUT_FILE, "wb") as f:
f.write(buf.getvalue())
print(f"SUCCESS: {OUT_FILE}")
mkdir -p /tmp/workspace/sandhya_output
~/create_soap_presentation.py
import os
from pptx import Presentation
from pptx.util import Inches, Pt, Emu
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
from pptx.enum.shapes import MSO_SHAPE_TYPE
from pptx.oxml.ns import qn
from lxml import etree
from pptx.oxml import parse_xml
from copy import deepcopy
os.makedirs("/tmp/workspace/sandhya_output", exist_ok=True)
# ── Color Palette ──────────────────────────────────────────────────────────────
NAVY = RGBColor(0x00, 0x33, 0x66) # #003366
DARK_NAVY = RGBColor(0x00, 0x1A, 0x40) # deeper navy for title bg accent
LIGHT_BLUE = RGBColor(0xAD, 0xD8, 0xE6) # #ADD8E6 table headers
ALICE_BLUE = RGBColor(0xF0, 0xF8, 0xFF) # #F0F8FF alt row
WHITE = RGBColor(0xFF, 0xFF, 0xFF)
DARK_TEXT = RGBColor(0x1A, 0x1A, 0x2E)
ACCENT_TEAL = RGBColor(0x00, 0x80, 0x80)
MID_BLUE = RGBColor(0x1E, 0x56, 0x9B) # medium blue for sub-headers
PALE_NAVY = RGBColor(0xE8, 0xF0, 0xFB) # very pale blue for alt rows
GOLD = RGBColor(0xF0, 0xAD, 0x00) # accent gold
RED_ALERT = RGBColor(0xC0, 0x39, 0x2B)
SW = Inches(13.333)
SH = Inches(7.5)
prs = Presentation()
prs.slide_width = SW
prs.slide_height = SH
blank_layout = prs.slide_layouts[6]
# ── Helper Functions ──────────────────────────────────────────────────────────
def set_bg(slide, color: RGBColor):
bg = slide.background
bg.fill.solid()
bg.fill.fore_color.rgb = color
def add_rect(slide, left, top, width, height, fill_color, line_color=None, line_width=None):
from pptx.enum.shapes import MSO_SHAPE
shp = slide.shapes.add_shape(1, left, top, width, height) # 1 = RECTANGLE
shp.fill.solid()
shp.fill.fore_color.rgb = fill_color
if line_color:
shp.line.color.rgb = line_color
if line_width:
shp.line.width = Pt(line_width)
else:
shp.line.fill.background()
shp.shadow.inherit = False
return shp
def add_text(slide, text, left, top, width, height,
font_name="Calibri", font_size=16, bold=False, italic=False,
color=DARK_TEXT, align=PP_ALIGN.LEFT, v_anchor=MSO_ANCHOR.TOP,
word_wrap=True, margin_l=Inches(0.1), margin_r=Inches(0.1),
margin_t=Inches(0.05), margin_b=Inches(0.05)):
tb = slide.shapes.add_textbox(left, top, width, height)
tf = tb.text_frame
tf.word_wrap = word_wrap
tf.vertical_anchor = v_anchor
tf.margin_left = margin_l
tf.margin_right = margin_r
tf.margin_top = margin_t
tf.margin_bottom = margin_b
p = tf.paragraphs[0]
p.alignment = align
r = p.add_run()
r.text = text
r.font.name = font_name
r.font.size = Pt(font_size)
r.font.bold = bold
r.font.italic = italic
r.font.color.rgb = color
return tb, tf
def add_header_bar(slide, title_text, subtitle_tag=None):
"""Navy header bar across top with white title + optional colored tag pill."""
bar = add_rect(slide, 0, 0, SW, Inches(1.0), NAVY)
# Title text in bar
tb = slide.shapes.add_textbox(Inches(0.35), Inches(0.07), Inches(11.0), Inches(0.85))
tf = tb.text_frame
tf.word_wrap = False
tf.vertical_anchor = MSO_ANCHOR.MIDDLE
tf.margin_left = Inches(0.1)
tf.margin_top = 0
tf.margin_bottom = 0
p = tf.paragraphs[0]
p.alignment = PP_ALIGN.LEFT
r = p.add_run()
r.text = title_text
r.font.name = "Calibri"
r.font.size = Pt(28)
r.font.bold = True
r.font.color.rgb = WHITE
if subtitle_tag:
tag_box = slide.shapes.add_textbox(Inches(11.5), Inches(0.25), Inches(1.5), Inches(0.5))
tf2 = tag_box.text_frame
tf2.word_wrap = False
tf2.vertical_anchor = MSO_ANCHOR.MIDDLE
tf2.margin_left = Inches(0.05)
tf2.margin_top = 0
tf2.margin_bottom = 0
p2 = tf2.paragraphs[0]
p2.alignment = PP_ALIGN.CENTER
r2 = p2.add_run()
r2.text = subtitle_tag
r2.font.name = "Calibri"
r2.font.size = Pt(11)
r2.font.bold = True
r2.font.color.rgb = NAVY
def add_bullet_section(slide, lines, left, top, width, height,
font_size=15, indent_char=" "):
"""Add a textbox with bullet lines. Lines starting with ' -' get sub-indent."""
tb = slide.shapes.add_textbox(left, top, width, height)
tf = tb.text_frame
tf.word_wrap = True
tf.margin_left = Inches(0.05)
tf.margin_right = Inches(0.05)
tf.margin_top = Inches(0.04)
tf.margin_bottom = Inches(0.04)
first = True
for line in lines:
if first:
p = tf.paragraphs[0]
first = False
else:
p = tf.add_paragraph()
is_sub = line.startswith(" -") or line.startswith(" ")
r = p.add_run()
r.text = line.strip()
r.font.name = "Calibri"
r.font.size = Pt(font_size - 1 if is_sub else font_size)
r.font.color.rgb = DARK_TEXT
p.level = 1 if is_sub else 0
# Space after paragraph for readability
p.space_after = Pt(3)
return tb, tf
def style_table_cell(cell, text, fill_color=None, font_color=DARK_TEXT,
font_size=13, bold=False, align=PP_ALIGN.LEFT, wrap=True):
cell.text_frame.word_wrap = wrap
tf = cell.text_frame
tf.margin_left = Inches(0.06)
tf.margin_right = Inches(0.06)
tf.margin_top = Inches(0.04)
tf.margin_bottom = Inches(0.04)
p = tf.paragraphs[0]
p.alignment = align
# Clear existing runs
for run in p.runs:
run.text = ""
r = p.add_run()
r.text = text
r.font.name = "Calibri"
r.font.size = Pt(font_size)
r.font.bold = bold
r.font.color.rgb = font_color
if fill_color:
cell.fill.solid()
cell.fill.fore_color.rgb = fill_color
def add_footer(slide, text="Nandha Medical College & Hospital | Pharmacotherapeutics-I | 2nd Pharm D"):
foot = slide.shapes.add_textbox(0, Inches(7.2), SW, Inches(0.3))
tf = foot.text_frame
tf.margin_left = Inches(0.3)
tf.margin_top = 0
tf.margin_bottom = 0
p = tf.paragraphs[0]
p.alignment = PP_ALIGN.CENTER
r = p.add_run()
r.text = text
r.font.name = "Calibri"
r.font.size = Pt(9)
r.font.color.rgb = RGBColor(0x80, 0x80, 0x80)
# ══════════════════════════════════════════════════════════════════════════════
# SLIDE 1 — TITLE SLIDE
# ══════════════════════════════════════════════════════════════════════════════
s1 = prs.slides.add_slide(blank_layout)
set_bg(s1, NAVY)
# Decorative left stripe
add_rect(s1, 0, 0, Inches(0.55), SH, DARK_NAVY)
# Top accent bar (thin gold line)
add_rect(s1, Inches(0.55), 0, SW - Inches(0.55), Inches(0.06), GOLD)
# Rx / medical cross icon area - decorative circle
circ = s1.shapes.add_shape(1, Inches(10.5), Inches(1.0), Inches(2.2), Inches(2.2))
circ.fill.solid(); circ.fill.fore_color.rgb = RGBColor(0x00, 0x4A, 0x8F)
circ.line.fill.background(); circ.shadow.inherit = False
circ2 = s1.shapes.add_shape(1, Inches(10.65), Inches(1.15), Inches(1.9), Inches(1.9))
circ2.fill.solid(); circ2.fill.fore_color.rgb = RGBColor(0x00, 0x5C, 0xAD)
circ2.line.fill.background(); circ2.shadow.inherit = False
# "Rx" label inside circle
tb_rx, tf_rx = add_text(s1, "Rx", Inches(10.9), Inches(1.5), Inches(1.4), Inches(1.0),
font_name="Georgia", font_size=42, bold=True, italic=True,
color=WHITE, align=PP_ALIGN.CENTER, v_anchor=MSO_ANCHOR.MIDDLE)
# Main title "SOAP ANALYSIS"
tb_t, tf_t = add_text(s1, "SOAP ANALYSIS",
Inches(0.8), Inches(1.6), Inches(9.5), Inches(1.3),
font_name="Georgia", font_size=52, bold=True,
color=WHITE, align=PP_ALIGN.LEFT, v_anchor=MSO_ANCHOR.MIDDLE)
# Gold divider line
add_rect(s1, Inches(0.8), Inches(3.0), Inches(7.0), Inches(0.04), GOLD)
# Subtitle
tb_sub, tf_sub = add_text(s1, "Sandhya Case | Pharmacotherapeutics-I",
Inches(0.8), Inches(3.1), Inches(9.5), Inches(0.6),
font_name="Calibri", font_size=22, bold=False, italic=True,
color=LIGHT_BLUE, align=PP_ALIGN.LEFT)
# Presented by
tb_by, tf_by = add_text(s1, "Presented by: Sindhana M | 2nd Pharm D",
Inches(0.8), Inches(3.85), Inches(9.5), Inches(0.55),
font_name="Calibri", font_size=18, bold=True,
color=WHITE, align=PP_ALIGN.LEFT)
# Institution
tb_inst, tf_inst = add_text(s1, "Nandha Medical College & Hospital",
Inches(0.8), Inches(4.5), Inches(9.5), Inches(0.5),
font_name="Calibri", font_size=16,
color=LIGHT_BLUE, align=PP_ALIGN.LEFT)
# Exam label
tb_exam, tf_exam = add_text(s1, "Pharmacotherapeutics-I | Second Sessional Practical Examination",
Inches(0.8), Inches(5.1), Inches(9.5), Inches(0.45),
font_name="Calibri", font_size=13, italic=True,
color=RGBColor(0xB0, 0xC8, 0xE8), align=PP_ALIGN.LEFT)
# Bottom accent bar
add_rect(s1, Inches(0.55), Inches(7.3), SW - Inches(0.55), Inches(0.2), DARK_NAVY)
add_rect(s1, Inches(0.55), Inches(6.95), Inches(4.0), Inches(0.04), GOLD)
# ══════════════════════════════════════════════════════════════════════════════
# SLIDE 2 — SUBJECTIVE
# ══════════════════════════════════════════════════════════════════════════════
s2 = prs.slides.add_slide(blank_layout)
set_bg(s2, WHITE)
add_header_bar(s2, "S — SUBJECTIVE: Patient Profile", "Slide 2")
# Left column box
add_rect(s2, Inches(0.35), Inches(1.15), Inches(5.8), Inches(5.9),
RGBColor(0xF4, 0xF8, 0xFF), RGBColor(0xC0, 0xD8, 0xF0), 0.8)
# Right column box
add_rect(s2, Inches(6.4), Inches(1.15), Inches(6.6), Inches(5.9),
RGBColor(0xF4, 0xF8, 0xFF), RGBColor(0xC0, 0xD8, 0xF0), 0.8)
# LEFT column sub-header
add_text(s2, "Patient Information", Inches(0.5), Inches(1.25), Inches(5.5), Inches(0.4),
font_name="Calibri", font_size=14, bold=True, color=NAVY)
# LEFT column content
left_lines = [
"Patient: 32-year-old Male",
"Admitted: 25/03/2026",
"Discharged: 28/03/2026",
"",
"Chief Complaints:",
" - Muscle cramps (B/L UL & right side) · 10 days",
" - Giddiness",
" - Abdominal pain",
"",
"Past History:",
" - K/C/O DCLD · 6 months",
" - On irregular medication",
]
add_bullet_section(s2, left_lines,
Inches(0.55), Inches(1.7), Inches(5.5), Inches(5.1), font_size=15)
# RIGHT column sub-header
add_text(s2, "Personal & Social History", Inches(6.55), Inches(1.25), Inches(6.3), Inches(0.4),
font_name="Calibri", font_size=14, bold=True, color=NAVY)
# Alcohol icon pill
alc_box = add_rect(s2, Inches(6.55), Inches(1.75), Inches(2.8), Inches(0.45),
RGBColor(0xFF, 0xEE, 0xDD), RGBColor(0xE8, 0x80, 0x30), 1.0)
add_text(s2, "⚠ Alcohol · 7 Years", Inches(6.65), Inches(1.77), Inches(2.6), Inches(0.4),
font_name="Calibri", font_size=13, bold=True, color=RGBColor(0xC0, 0x50, 0x00),
align=PP_ALIGN.CENTER)
right_lines = [
"Diet: Mixed (non-vegetarian)",
"",
"Alcohol Use: 7 years (chronic)",
" - Primary etiology for liver disease",
"",
"Occupation: Not documented",
"",
"Medication Adherence:",
" - Irregular / non-compliant",
" - High-risk for disease progression",
"",
"Allergies: Not documented",
]
add_bullet_section(s2, right_lines,
Inches(6.55), Inches(2.35), Inches(6.3), Inches(4.5), font_size=15)
add_footer(s2)
# ══════════════════════════════════════════════════════════════════════════════
# SLIDE 3 — OBJECTIVE
# ══════════════════════════════════════════════════════════════════════════════
s3 = prs.slides.add_slide(blank_layout)
set_bg(s3, WHITE)
add_header_bar(s3, "O — OBJECTIVE: Vitals & Examination", "Slide 3")
# LEFT panel - Vitals
add_rect(s3, Inches(0.35), Inches(1.12), Inches(6.1), Inches(6.0),
RGBColor(0xF0, 0xF8, 0xFF), RGBColor(0xB0, 0xCE, 0xEC), 0.8)
add_text(s3, "Vital Signs", Inches(0.5), Inches(1.2), Inches(5.8), Inches(0.4),
font_name="Calibri", font_size=15, bold=True, color=NAVY)
# Vitals - card rows
vitals = [
("BP", "100/70 mmHg", "Low-Normal", RGBColor(0xFF, 0xF5, 0xE0), RGBColor(0xE0, 0x80, 0x00)),
("PR", "111 / min", "Tachycardia ⚠", RGBColor(0xFF, 0xED, 0xED), RGBColor(0xC0, 0x20, 0x20)),
("RR", "20 / min", "Normal ✓", RGBColor(0xE8, 0xFF, 0xE8), RGBColor(0x20, 0x80, 0x20)),
("Temp", "98.6 °F", "Afebrile ✓", RGBColor(0xE8, 0xFF, 0xE8), RGBColor(0x20, 0x80, 0x20)),
]
for i, (param, val, interp, bg, fg) in enumerate(vitals):
y = Inches(1.7) + i * Inches(1.22)
# Card bg
add_rect(s3, Inches(0.5), y, Inches(5.7), Inches(1.08), bg, RGBColor(0xCC, 0xCC, 0xCC), 0.5)
# Param label
add_text(s3, param, Inches(0.65), y + Inches(0.08), Inches(1.0), Inches(0.4),
font_name="Calibri", font_size=13, bold=True, color=NAVY)
# Value
add_text(s3, val, Inches(0.65), y + Inches(0.45), Inches(2.5), Inches(0.45),
font_name="Calibri", font_size=20, bold=True, color=DARK_TEXT)
# Interpretation pill
interp_box = add_rect(s3, Inches(3.4), y + Inches(0.28), Inches(2.6), Inches(0.45),
fg, None)
add_text(s3, interp, Inches(3.4), y + Inches(0.3), Inches(2.6), Inches(0.4),
font_name="Calibri", font_size=12, bold=True, color=WHITE,
align=PP_ALIGN.CENTER)
# RIGHT panel - Examination
add_rect(s3, Inches(6.7), Inches(1.12), Inches(6.3), Inches(6.0),
RGBColor(0xF8, 0xF4, 0xFF), RGBColor(0xB0, 0xA0, 0xD8), 0.8)
add_text(s3, "Clinical Examination", Inches(6.85), Inches(1.2), Inches(6.0), Inches(0.4),
font_name="Calibri", font_size=15, bold=True, color=NAVY)
exam_entries = [
("General", "Conscious, Oriented, Afebrile\nIcterus Present (+)"),
("CVS", "S1 S2 heard normally"),
("RS", "BAE (Bilateral Air Entry) present"),
("P/A", "RHC tenderness present\nFree fluid (+) — Ascites"),
("CNS", "NFND (No focal neurological deficit)"),
("L/E", "Right eye cataract (noted)"),
]
for i, (system, finding) in enumerate(exam_entries):
y = Inches(1.7) + i * Inches(0.88)
# System label box
add_rect(s3, Inches(6.85), y, Inches(1.0), Inches(0.75),
NAVY, None)
add_text(s3, system, Inches(6.85), y + Inches(0.05), Inches(1.0), Inches(0.65),
font_name="Calibri", font_size=12, bold=True, color=WHITE,
align=PP_ALIGN.CENTER, v_anchor=MSO_ANCHOR.MIDDLE)
# Finding text
add_text(s3, finding, Inches(8.0), y, Inches(4.8), Inches(0.78),
font_name="Calibri", font_size=13, color=DARK_TEXT,
word_wrap=True, v_anchor=MSO_ANCHOR.MIDDLE)
# Thin separator
if i < len(exam_entries) - 1:
add_rect(s3, Inches(6.85), y + Inches(0.78), Inches(5.9), Inches(0.02),
RGBColor(0xCC, 0xCC, 0xDD))
add_footer(s3)
# ══════════════════════════════════════════════════════════════════════════════
# SLIDE 4 — DIAGNOSIS
# ══════════════════════════════════════════════════════════════════════════════
s4 = prs.slides.add_slide(blank_layout)
set_bg(s4, NAVY)
# Decorative left stripe
add_rect(s4, 0, 0, Inches(0.55), SH, DARK_NAVY)
add_rect(s4, Inches(0.55), 0, SW - Inches(0.55), Inches(0.06), GOLD)
add_text(s4, "Final Diagnosis", Inches(0.8), Inches(0.3), Inches(11.5), Inches(0.7),
font_name="Georgia", font_size=30, bold=True, color=WHITE, align=PP_ALIGN.CENTER)
# Big DCLD acronym
add_text(s4, "DCLD", Inches(2.0), Inches(1.1), Inches(9.3), Inches(2.0),
font_name="Georgia", font_size=90, bold=True, color=GOLD,
align=PP_ALIGN.CENTER, v_anchor=MSO_ANCHOR.MIDDLE)
# Full name
add_text(s4, "Decompensated Chronic Liver Disease",
Inches(1.5), Inches(3.1), Inches(10.3), Inches(0.65),
font_name="Calibri", font_size=26, bold=False, italic=True,
color=LIGHT_BLUE, align=PP_ALIGN.CENTER)
# Gold divider
add_rect(s4, Inches(2.5), Inches(3.85), Inches(8.3), Inches(0.04), GOLD)
# Three feature cards
features = [
("Alcohol-Induced", "7 years of chronic\nalcohol use", RGBColor(0x80, 0x30, 0x00)),
("Decompensation\nFeatures", "Ascites, Icterus,\nTachycardia", RGBColor(0x00, 0x50, 0x80)),
("Irregular\nMedication", "Non-adherence\nto prior therapy", RGBColor(0x40, 0x00, 0x80)),
]
for i, (title, desc, col) in enumerate(features):
x = Inches(1.0) + i * Inches(3.9)
add_rect(s4, x, Inches(4.05), Inches(3.5), Inches(2.5), col, GOLD, 1.0)
add_text(s4, title, x + Inches(0.15), Inches(4.2), Inches(3.2), Inches(0.85),
font_name="Calibri", font_size=15, bold=True, color=GOLD,
align=PP_ALIGN.CENTER)
add_text(s4, desc, x + Inches(0.15), Inches(5.1), Inches(3.2), Inches(1.1),
font_name="Calibri", font_size=13, color=WHITE,
align=PP_ALIGN.CENTER)
add_rect(s4, Inches(0.55), Inches(7.3), SW - Inches(0.55), Inches(0.2), DARK_NAVY)
# ══════════════════════════════════════════════════════════════════════════════
# SLIDE 5 — ASSESSMENT: Clinical Problems (Table)
# ══════════════════════════════════════════════════════════════════════════════
s5 = prs.slides.add_slide(blank_layout)
set_bg(s5, WHITE)
add_header_bar(s5, "A — ASSESSMENT: Clinical Problems", "Slide 5")
rows_data = [
("Hypokalemia", "Muscle cramps, Giddiness", "Syp. KCl 5ml TDS"),
("Ascites", "Free fluid (+), RHC tenderness", "Dytor 5mg, Fluid/Salt restriction"),
("Hepatic dysfunction", "Icterus, DCLD diagnosis", "UDCA 300mg"),
("Nutritional deficiency","Alcohol 7 yrs, DCLD", "Thiamine, Neurobion, Shelcal, Evion LC"),
("GERD / Peptic disease","Abdominal pain", "Sompraz D 40mg"),
("Muscle cramps", "B/L upper limb cramps", "Baclofen"),
]
headers = ["Problem", "Evidence", "Management"]
tbl_shape = s5.shapes.add_table(
len(rows_data) + 1, 3,
Inches(0.35), Inches(1.15),
Inches(12.65), Inches(5.85)
)
tbl = tbl_shape.table
tbl.columns[0].width = Inches(3.2)
tbl.columns[1].width = Inches(4.5)
tbl.columns[2].width = Inches(4.95)
# Header row
for ci, hdr in enumerate(headers):
style_table_cell(tbl.cell(0, ci), hdr,
fill_color=NAVY, font_color=WHITE,
font_size=15, bold=True, align=PP_ALIGN.CENTER)
# Data rows
alt_colors = [WHITE, ALICE_BLUE]
for ri, (prob, evid, mgmt) in enumerate(rows_data):
bg = alt_colors[ri % 2]
style_table_cell(tbl.cell(ri+1, 0), prob, fill_color=bg,
font_size=13, bold=True, color=MID_BLUE if True else DARK_TEXT,
font_color=MID_BLUE)
style_table_cell(tbl.cell(ri+1, 1), evid, fill_color=bg, font_size=13)
style_table_cell(tbl.cell(ri+1, 2), mgmt, fill_color=bg, font_size=13,
font_color=RGBColor(0x00, 0x60, 0x00))
add_footer(s5)
# ══════════════════════════════════════════════════════════════════════════════
# SLIDE 6 — ASSESSMENT: Drug Chart
# ══════════════════════════════════════════════════════════════════════════════
s6 = prs.slides.add_slide(blank_layout)
set_bg(s6, WHITE)
add_header_bar(s6, "A — ASSESSMENT: Drug Chart", "Slide 6")
drug_data = [
("Sompraz D 40mg", "1-0-0", "GERD / Gastroprotection", "PPI + Prokinetic"),
("Thiamine 100mg", "1-0-1", "Vit B1 / Wernicke's prevention", "Vitamin"),
("Dytor 5mg", "1-1-0", "Ascites / Edema", "Loop Diuretic"),
("UDCA 300mg", "1-0-1", "Hepatoprotection", "Bile Acid"),
("Evion LC", "1-0-0", "Antioxidant support", "Vitamin E"),
("Shelcal HD", "0-0-1", "Calcium + Vitamin D", "Mineral / Vitamin"),
("Neurobion Forte", "1-0-0", "B-complex supplementation", "Multivitamin"),
("VSL", "1-0-0", "Gut flora restoration", "Probiotic"),
("Baclofen", "1-0-0", "Muscle cramps", "Muscle Relaxant"),
("Syp. KCl 5ml", "TDS", "Hypokalemia", "K⁺ Supplement"),
]
drug_headers = ["Drug", "Dose / Freq", "Indication", "Drug Class"]
tbl2 = s6.shapes.add_table(
len(drug_data) + 1, 4,
Inches(0.35), Inches(1.12),
Inches(12.65), Inches(6.1)
).table
tbl2.columns[0].width = Inches(2.8)
tbl2.columns[1].width = Inches(1.5)
tbl2.columns[2].width = Inches(4.7)
tbl2.columns[3].width = Inches(3.65)
for ci, hdr in enumerate(drug_headers):
style_table_cell(tbl2.cell(0, ci), hdr,
fill_color=NAVY, font_color=WHITE, font_size=14, bold=True,
align=PP_ALIGN.CENTER)
for ri, (drug, dose, ind, cls) in enumerate(drug_data):
bg = WHITE if ri % 2 == 0 else ALICE_BLUE
style_table_cell(tbl2.cell(ri+1, 0), drug, fill_color=bg, font_size=12, bold=True,
font_color=NAVY)
style_table_cell(tbl2.cell(ri+1, 1), dose, fill_color=bg, font_size=12,
align=PP_ALIGN.CENTER)
style_table_cell(tbl2.cell(ri+1, 2), ind, fill_color=bg, font_size=12)
style_table_cell(tbl2.cell(ri+1, 3), cls, fill_color=bg, font_size=12,
font_color=RGBColor(0x30, 0x60, 0x30))
add_footer(s6)
# ══════════════════════════════════════════════════════════════════════════════
# SLIDE 7 — ASSESSMENT: Drug Related Problems
# ══════════════════════════════════════════════════════════════════════════════
s7 = prs.slides.add_slide(blank_layout)
set_bg(s7, WHITE)
add_header_bar(s7, "A — ASSESSMENT: Drug Related Problems (DRPs)", "Slide 7")
drp_data = [
(
"DRP 1", "Domperidone (in Sompraz D)",
"Contraindicated in severe hepatic impairment — QT prolongation risk.",
"Review and consider switching to plain Pantoprazole 40mg.",
RED_ALERT, RGBColor(0xFF, 0xF0, 0xF0)
),
(
"DRP 2", "Baclofen in DCLD",
"Hepatic metabolism with risk of drug accumulation and encephalopathy.",
"Monitor closely; use lowest effective dose. Watch for confusion.",
RGBColor(0xCC, 0x66, 0x00), RGBColor(0xFF, 0xF5, 0xE0)
),
(
"DRP 3", "Torasemide (Dytor) — K⁺ Loss",
"Loop diuretic causes potassium depletion (hypokalemia risk).",
"KCl supplementation appropriate. Monitor serum electrolytes regularly.",
RGBColor(0x00, 0x70, 0xC0), RGBColor(0xE8, 0xF4, 0xFF)
),
(
"DRP 4", "Non-Adherence to Prior DCLD Medication",
"Patient was on irregular medication — contributed to decompensation.",
"Comprehensive patient counseling essential; simplify regimen if possible.",
RGBColor(0x40, 0x80, 0x00), RGBColor(0xF0, 0xFF, 0xE8)
),
]
for i, (tag, drug, problem, action, accent, bg) in enumerate(drp_data):
y = Inches(1.2) + i * Inches(1.47)
# Card background
add_rect(s7, Inches(0.35), y, Inches(12.65), Inches(1.35), bg,
accent, 1.2)
# DRP tag box
add_rect(s7, Inches(0.35), y, Inches(1.1), Inches(1.35), accent, None)
add_text(s7, tag, Inches(0.35), y + Inches(0.35), Inches(1.1), Inches(0.65),
font_name="Calibri", font_size=14, bold=True, color=WHITE,
align=PP_ALIGN.CENTER, v_anchor=MSO_ANCHOR.MIDDLE)
# Drug name
add_text(s7, drug, Inches(1.6), y + Inches(0.06), Inches(5.5), Inches(0.45),
font_name="Calibri", font_size=14, bold=True, color=accent)
# Problem
add_text(s7, "Issue: " + problem, Inches(1.6), y + Inches(0.5), Inches(5.5), Inches(0.42),
font_name="Calibri", font_size=12, color=DARK_TEXT, word_wrap=True)
# Vertical divider
add_rect(s7, Inches(7.4), y + Inches(0.1), Inches(0.03), Inches(1.15),
RGBColor(0xCC, 0xCC, 0xCC))
# Action label
add_text(s7, "Recommendation:", Inches(7.55), y + Inches(0.06), Inches(5.1), Inches(0.35),
font_name="Calibri", font_size=12, bold=True, color=accent)
add_text(s7, action, Inches(7.55), y + Inches(0.44), Inches(5.1), Inches(0.75),
font_name="Calibri", font_size=12, color=DARK_TEXT, word_wrap=True)
add_footer(s7)
# ══════════════════════════════════════════════════════════════════════════════
# SLIDE 8 — PLAN: Pharmacological & Non-Pharmacological
# ══════════════════════════════════════════════════════════════════════════════
s8 = prs.slides.add_slide(blank_layout)
set_bg(s8, WHITE)
add_header_bar(s8, "P — PLAN: Pharmacological & Non-Pharmacological", "Slide 8")
# LEFT column
add_rect(s8, Inches(0.35), Inches(1.12), Inches(6.05), Inches(6.0),
RGBColor(0xF0, 0xF8, 0xFF), NAVY, 0.8)
add_text(s8, "Pharmacological", Inches(0.5), Inches(1.2), Inches(5.75), Inches(0.45),
font_name="Calibri", font_size=16, bold=True, color=NAVY)
pharm_lines = [
"Continue all discharge medications as prescribed",
"Monitor serum K+, Na+ weekly until stable",
"Review Domperidone — consider switch to plain Pantoprazole",
"Titrate Baclofen cautiously; lowest effective dose",
"Thiamine & B-complex — essential (Wernicke's prevention)",
"Repeat LFTs & coagulation panel at follow-up",
"Electrolytes: K+, Na+, Mg2+ check in 1 week",
]
add_bullet_section(s8, pharm_lines,
Inches(0.5), Inches(1.75), Inches(5.7), Inches(5.1), font_size=14)
# RIGHT column
add_rect(s8, Inches(6.65), Inches(1.12), Inches(6.35), Inches(6.0),
RGBColor(0xF0, 0xFF, 0xF0), RGBColor(0x20, 0x80, 0x20), 0.8)
add_text(s8, "Non-Pharmacological", Inches(6.8), Inches(1.2), Inches(6.05), Inches(0.45),
font_name="Calibri", font_size=16, bold=True, color=RGBColor(0x10, 0x60, 0x10))
non_pharm_lines = [
"STRICT alcohol abstinence (primary driver of progression)",
"High-protein, low-sodium diet",
"Fluid restriction < 1 litre / day",
"Salt restriction < 2 g / day",
"Daily weight monitoring (ascites control)",
"Ophthalmology referral (right eye cataract)",
"Regular follow-up adherence counseling",
]
add_bullet_section(s8, non_pharm_lines,
Inches(6.8), Inches(1.75), Inches(6.0), Inches(5.1), font_size=14)
add_footer(s8)
# ══════════════════════════════════════════════════════════════════════════════
# SLIDE 9 — PLAN: Monitoring Parameters
# ══════════════════════════════════════════════════════════════════════════════
s9 = prs.slides.add_slide(blank_layout)
set_bg(s9, WHITE)
add_header_bar(s9, "P — PLAN: Monitoring Parameters", "Slide 9")
mon_data = [
("Serum K⁺", "Weekly", "3.5 – 5.0 mEq/L"),
("Serum Na⁺", "Weekly", "135 – 145 mEq/L"),
("Liver Function Tests (LFT)","At follow-up (2-4 wks)","Trending improvement"),
("PT / INR", "At follow-up", "Monitor coagulopathy"),
("Body Weight", "Daily", "Stable / decreasing"),
("BP & Pulse Rate", "Daily", "Hemodynamic stability"),
("Signs of Encephalopathy", "Every clinical visit", "Absent — no confusion"),
]
mon_headers = ["Parameter", "Frequency", "Target / Goal"]
mon_tbl = s9.shapes.add_table(
len(mon_data) + 1, 3,
Inches(0.35), Inches(1.15),
Inches(12.65), Inches(5.8)
).table
mon_tbl.columns[0].width = Inches(4.5)
mon_tbl.columns[1].width = Inches(3.2)
mon_tbl.columns[2].width = Inches(4.95)
for ci, hdr in enumerate(mon_headers):
style_table_cell(mon_tbl.cell(0, ci), hdr,
fill_color=NAVY, font_color=WHITE, font_size=15, bold=True,
align=PP_ALIGN.CENTER)
for ri, (param, freq, target) in enumerate(mon_data):
bg = WHITE if ri % 2 == 0 else ALICE_BLUE
style_table_cell(mon_tbl.cell(ri+1, 0), param, fill_color=bg, font_size=13, bold=True,
font_color=NAVY)
style_table_cell(mon_tbl.cell(ri+1, 1), freq, fill_color=bg, font_size=13,
align=PP_ALIGN.CENTER)
style_table_cell(mon_tbl.cell(ri+1, 2), target, fill_color=bg, font_size=13,
font_color=RGBColor(0x10, 0x60, 0x10))
add_footer(s9)
# ══════════════════════════════════════════════════════════════════════════════
# SLIDE 10 — PLAN: Patient Counseling
# ══════════════════════════════════════════════════════════════════════════════
s10 = prs.slides.add_slide(blank_layout)
set_bg(s10, WHITE)
add_header_bar(s10, "P — PLAN: Patient Counseling Points", "Slide 10")
counsel_items = [
(
"01", "Complete Alcohol Abstinence",
"Alcohol is the primary driver of DCLD progression. Even small amounts can worsen liver failure.",
RED_ALERT, RGBColor(0xFF, 0xF2, 0xF2)
),
(
"02", "Medication Adherence",
"Take ALL medications regularly. Previous non-adherence contributed to this hospitalisation.",
RGBColor(0xCC, 0x66, 0x00), RGBColor(0xFF, 0xF8, 0xEE)
),
(
"03", "Diet & Fluid Management",
"Maintain low-salt diet (<2g/day) and fluid restriction (<1L/day) to control ascites.",
RGBColor(0x00, 0x70, 0xC0), RGBColor(0xEE, 0xF6, 0xFF)
),
(
"04", "Warning Signs — ER Immediately",
"Seek emergency care if: increased abdomen swelling, confusion/drowsiness, vomiting blood, high fever.",
RGBColor(0x80, 0x00, 0x00), RGBColor(0xFF, 0xEE, 0xEE)
),
(
"05", "KCl Syrup Administration",
"Always dilute Syp. KCl in water before taking. Do not take undiluted.",
RGBColor(0x40, 0x80, 0x00), RGBColor(0xF2, 0xFF, 0xEC)
),
(
"06", "Follow-Up Schedule",
"Return in 1–2 weeks for electrolyte check. Adhere to all scheduled appointments.",
RGBColor(0x40, 0x00, 0x80), RGBColor(0xF5, 0xEE, 0xFF)
),
]
for i, (num, title, desc, accent, bg) in enumerate(counsel_items):
col = i % 2
row = i // 2
x = Inches(0.35) + col * Inches(6.35)
y = Inches(1.18) + row * Inches(1.95)
w = Inches(6.2)
h = Inches(1.82)
add_rect(s10, x, y, w, h, bg, accent, 1.0)
# Number circle
add_rect(s10, x + Inches(0.1), y + Inches(0.1), Inches(0.6), Inches(0.6), accent, None)
add_text(s10, num, x + Inches(0.1), y + Inches(0.1), Inches(0.6), Inches(0.6),
font_name="Calibri", font_size=14, bold=True, color=WHITE,
align=PP_ALIGN.CENTER, v_anchor=MSO_ANCHOR.MIDDLE)
# Title
add_text(s10, title, x + Inches(0.85), y + Inches(0.1), w - Inches(1.0), Inches(0.45),
font_name="Calibri", font_size=14, bold=True, color=accent)
# Description
add_text(s10, desc, x + Inches(0.15), y + Inches(0.65), w - Inches(0.3), Inches(1.05),
font_name="Calibri", font_size=12, color=DARK_TEXT, word_wrap=True)
add_footer(s10)
# ══════════════════════════════════════════════════════════════════════════════
# SLIDE 11 — THANK YOU
# ══════════════════════════════════════════════════════════════════════════════
s11 = prs.slides.add_slide(blank_layout)
set_bg(s11, NAVY)
# Decorative elements
add_rect(s11, 0, 0, Inches(0.55), SH, DARK_NAVY)
add_rect(s11, Inches(0.55), 0, SW - Inches(0.55), Inches(0.06), GOLD)
# Large center circle
circ_t = s11.shapes.add_shape(1, Inches(5.4), Inches(1.3), Inches(2.55), Inches(2.55))
circ_t.fill.solid(); circ_t.fill.fore_color.rgb = RGBColor(0x00, 0x4A, 0x8F)
circ_t.line.color.rgb = GOLD; circ_t.line.width = Pt(2)
circ_t.shadow.inherit = False
add_text(s11, "✓", Inches(5.4), Inches(1.5), Inches(2.55), Inches(2.1),
font_name="Calibri", font_size=64, bold=True, color=GOLD,
align=PP_ALIGN.CENTER, v_anchor=MSO_ANCHOR.MIDDLE)
# Thank You
add_text(s11, "Thank You", Inches(1.0), Inches(3.95), Inches(11.35), Inches(1.1),
font_name="Georgia", font_size=52, bold=True, color=WHITE,
align=PP_ALIGN.CENTER, v_anchor=MSO_ANCHOR.MIDDLE)
# Gold line
add_rect(s11, Inches(2.5), Inches(5.15), Inches(8.35), Inches(0.04), GOLD)
# Presenter
add_text(s11, "Sindhana M | 2nd Pharm D",
Inches(1.0), Inches(5.3), Inches(11.35), Inches(0.55),
font_name="Calibri", font_size=20, bold=True, color=WHITE, align=PP_ALIGN.CENTER)
add_text(s11, "Nandha Medical College & Hospital",
Inches(1.0), Inches(5.95), Inches(11.35), Inches(0.45),
font_name="Calibri", font_size=16, color=LIGHT_BLUE, align=PP_ALIGN.CENTER)
add_text(s11, "Pharmacotherapeutics-I | Second Sessional Practical Examination",
Inches(1.0), Inches(6.48), Inches(11.35), Inches(0.4),
font_name="Calibri", font_size=13, italic=True,
color=RGBColor(0xB0, 0xC8, 0xE8), align=PP_ALIGN.CENTER)
add_rect(s11, Inches(0.55), Inches(7.3), SW - Inches(0.55), Inches(0.2), DARK_NAVY)
# ══════════════════════════════════════════════════════════════════════════════
# SAVE
# ══════════════════════════════════════════════════════════════════════════════
output_path = "/tmp/workspace/sandhya_output/Sandhya_SOAP_Presentation.pptx"
from io import BytesIO
buf = BytesIO()
prs.save(buf)
with open(output_path, "wb") as f:
f.write(buf.getvalue())
print(f"SUCCESS: {output_path}")
print(f"File size: {os.path.getsize(output_path):,} bytes")
print(f"Slide count: {len(prs.slides)}")
python3 /tmp/workspace/create_soap_docx.py
python3 /tmp/skills/docx/scripts/office/validate.py /tmp/workspace/sandhya_output/Sandhya_SOAP_Analysis.docx
# The settings.xml zoom issue is a benign python-docx default issue. # Let's unpack and fix it, then repack. python3 /tmp/skills/docx/scripts/office/unpack.py /tmp/workspace/sandhya_output/Sandhya_SOAP_Analysis.docx /tmp/workspace/sandhya_unpacked/
"zoom" in ~/sandhya_unpacked/word
"zoom" in ~/sandhya_unpacked/word/settings.xml
~/sandhya_unpacked/word/settings.xml
Successfully replaced 1 instance(s) of the string in '/tmp/workspace/sandhya_unpacked/word/settings.xml'
python3 /tmp/skills/docx/scripts/office/pack.py /tmp/workspace/sandhya_unpacked/ /tmp/workspace/sandhya_output/Sandhya_SOAP_Analysis.docx --original /tmp/workspace/sandhya_output/Sandhya_SOAP_Analysis.docx
python3 -c " from docx import Document doc = Document('/tmp/workspace/sandhya_output/Sandhya_SOAP_Analysis.docx') # Print headings and first paragraph of each section for p in doc.paragraphs: style = p.style.name if p.style else 'None' if p.text.strip(): if 'Heading' in style: print(f'[{style}] {p.text}') print() print('Total paragraphs:', len(doc.paragraphs)) print('Total tables:', len(doc.tables)) # Print table headers for i, t in enumerate(doc.tables): print(f' Table {i+1} cols={len(t.columns)}, rows={len(t.rows)}, header: {[c.text[:30] for c in t.rows[0].cells]}') "
ls -lh /tmp/workspace/sandhya_output/Sandhya_SOAP_Analysis.docx
cd /tmp/workspace && python create_soap_presentation.py 2>&1
~/create_soap_presentation.py
Successfully replaced 1 instance(s) of the string in '/tmp/workspace/create_soap_presentation.py'
cd /tmp/workspace && python create_soap_presentation.py 2>&1
python3 - <<'EOF' from pptx import Presentation from pptx.util import Emu, Inches path = "/tmp/workspace/sandhya_output/Sandhya_SOAP_Presentation.pptx" prs = Presentation(path) sw, sh = prs.slide_width, prs.slide_height print(f"=== CONTENT QA ===") print(f"Slide dims: {Emu(sw).inches:.3f}\" x {Emu(sh).inches:.3f}\"") print(f"Total slides: {len(prs.slides)}\n") for i, slide in enumerate(prs.slides, 1): print(f"--- Slide {i} ---") for shape in slide.shapes: if shape.has_text_frame: text = shape.text_frame.text.strip() if text: # Show first 120 chars display = text[:120].replace('\n', ' | ') print(f" TEXT: {display}") if hasattr(shape, "table"): tbl = shape.table print(f" TABLE: {tbl._tbl.tr_lst.__len__() if hasattr(tbl._tbl, 'tr_lst') else '?'} rows") print() EOF
python3 - <<'EOF' from pptx import Presentation from pptx.util import Emu, Inches path = "/tmp/workspace/sandhya_output/Sandhya_SOAP_Presentation.pptx" prs = Presentation(path) sw, sh = prs.slide_width, prs.slide_height print("=== GEOMETRY QA ===") issues = [] for i, slide in enumerate(prs.slides, 1): for shape in slide.shapes: if shape.left is None or shape.top is None: continue left = shape.left top = shape.top width = shape.width or 0 height = shape.height or 0 right = left + width bottom = top + height if left < 0 or top < 0: issues.append(f"Slide {i}: shape starts off-slide (left={Emu(left).inches:.2f}\", top={Emu(top).inches:.2f}\")") if right > sw + 10000: # allow tiny rounding issues.append(f"Slide {i}: shape extends right ({Emu(right).inches:.2f}\" > {Emu(sw).inches:.2f}\")") if bottom > sh + 10000: issues.append(f"Slide {i}: shape extends below ({Emu(bottom).inches:.2f}\" > {Emu(sh).inches:.2f}\")") if issues: for iss in issues: print(" ISSUE:", iss) else: print(" No geometry issues found!") # Check table content print("\n=== TABLE CONTENT QA ===") for i, slide in enumerate(prs.slides, 1): for shape in slide.shapes: if hasattr(shape, "table"): tbl = shape.table print(f"Slide {i} table:") for ri in range(len(tbl.rows)): row_texts = [] for ci in range(len(tbl.columns)): cell_text = tbl.cell(ri, ci).text_frame.text.strip() row_texts.append(cell_text[:30] if cell_text else "(empty)") print(f" Row {ri}: {row_texts}") print() EOF
ls -lh /tmp/workspace/sandhya_output/Sandhya_SOAP_Presentation.pptx && python3 -c " from pptx import Presentation prs = Presentation('/tmp/workspace/sandhya_output/Sandhya_SOAP_Presentation.pptx') from pptx.util import Emu print('Slides:', len(prs.slides)) print('Dims:', Emu(prs.slide_width).inches, 'x', Emu(prs.slide_height).inches) slide_titles = [] for slide in prs.slides: for shape in slide.shapes: if shape.has_text_frame: t = shape.text_frame.text.strip() if t and len(t) > 3: slide_titles.append(t.split('\n')[0][:60]) break for j, t in enumerate(slide_titles, 1): print(f' Slide {j:2d}: {t}') print() print('SUCCESS: /tmp/workspace/sandhya_output/Sandhya_SOAP_Presentation.pptx') "
python3 << 'EOF' import os from docx import Document from docx.shared import Pt, Inches, RGBColor from docx.enum.text import WD_ALIGN_PARAGRAPH from docx.oxml.ns import qn from docx.oxml import OxmlElement os.makedirs("/tmp/workspace/sandhya_output", exist_ok=True) doc = Document() # Page margins from docx.shared import Inches as In sections = doc.sections for section in sections: section.top_margin = In(1) section.bottom_margin = In(1) section.left_margin = In(1.2) section.right_margin = In(1.2) style = doc.styles["Normal"] style.font.name = "Arial" style.font.size = Pt(11) NAVY = RGBColor(0, 51, 102) DARK_BLUE = RGBColor(0, 0, 139) GRAY = RGBColor(80, 80, 80) def set_font(run, bold=False, size=11, color=None): run.bold = bold run.font.size = Pt(size) if color: run.font.color.rgb = color def add_para(text, align=None, bold=False, size=11, color=None, space_before=0, space_after=6): p = doc.add_paragraph() if align: p.alignment = align p.paragraph_format.space_before = Pt(space_before) p.paragraph_format.space_after = Pt(space_after) run = p.add_run(text) set_font(run, bold=bold, size=size, color=color) return p def add_horizontal_rule(): p = doc.add_paragraph() p.paragraph_format.space_before = Pt(2) p.paragraph_format.space_after = Pt(2) pPr = p._p.get_or_add_pPr() pBdr = OxmlElement('w:pBdr') bottom = OxmlElement('w:bottom') bottom.set(qn('w:val'), 'single') bottom.set(qn('w:sz'), '6') bottom.set(qn('w:space'), '1') bottom.set(qn('w:color'), '003366') pBdr.append(bottom) pPr.append(pBdr) # ─── LETTERHEAD ─── p = doc.add_paragraph() p.alignment = WD_ALIGN_PARAGRAPH.CENTER r = p.add_run("NANDHA MEDICAL COLLEGE AND HOSPITAL") r.bold = True r.font.size = Pt(16) r.font.color.rgb = NAVY p2 = doc.add_paragraph() p2.alignment = WD_ALIGN_PARAGRAPH.CENTER r2 = p2.add_run("Department of Pharmacy Practice") r2.font.size = Pt(12) r2.font.color.rgb = DARK_BLUE p3 = doc.add_paragraph() p3.alignment = WD_ALIGN_PARAGRAPH.CENTER r3 = p3.add_run("Erode, Tamil Nadu, India") r3.font.size = Pt(11) r3.font.color.rgb = GRAY add_horizontal_rule() # Title add_para("MEDICAL REFERRAL LETTER", WD_ALIGN_PARAGRAPH.CENTER, bold=True, size=14, color=NAVY, space_before=6, space_after=4) add_horizontal_rule() # Date / Ref doc.add_paragraph() p_date = doc.add_paragraph() r_lbl = p_date.add_run("Date: ") r_lbl.bold = True r_lbl.font.size = Pt(11) p_date.add_run("25th March 2026") p_ref = doc.add_paragraph() r2 = p_ref.add_run("Reference No: ") r2.bold = True r2.font.size = Pt(11) p_ref.add_run("NMCH/PP/2026/0325") doc.add_paragraph() # To block add_para("To,", bold=False, size=11) add_para("The Consultant Ophthalmologist", bold=True, size=11, color=NAVY, space_after=0) add_para("Department of Ophthalmology", size=11, space_after=0) add_para("Nandha Medical College and Hospital", size=11, space_after=0) add_para("Erode, Tamil Nadu", size=11, space_after=10) add_para("Subject: Referral for Ophthalmological Evaluation – Right Eye Cataract", bold=True, size=11, color=DARK_BLUE, space_after=8) add_para("Dear Doctor,", size=11, space_after=6) # Body paragraphs body1 = ("We are referring Mr. [Patient Name], a 32-year-old male patient, currently under the care " "of the Department of Medicine at Nandha Medical College and Hospital (IP No: ________), " "for evaluation and management of an incidentally detected right eye cataract.") add_para(body1, size=11, space_after=8) # Patient summary box add_para("PATIENT SUMMARY", bold=True, size=11, color=NAVY, space_after=2) tbl = doc.add_table(rows=7, cols=2) tbl.style = "Table Grid" data = [ ("Age / Sex", "32 years / Male"), ("Admission Date", "25/03/2026"), ("Discharge Date", "28/03/2026"), ("Primary Diagnosis", "Decompensated Chronic Liver Disease (DCLD) – Alcohol-induced"), ("Co-morbidities", "Hypokalemia, Ascites, Icterus"), ("Current Medications", "Dytor 5mg, UDCA 300mg, Thiamine 100mg, KCl syrup, Baclofen, Sompraz D, Neurobion, Shelcal HD, Evion LC, VSL"), ("Ocular Finding", "Right eye cataract detected on L/E examination"), ] for i, (label, value) in enumerate(data): row = tbl.rows[i] c0, c1 = row.cells[0], row.cells[1] c0.paragraphs[0].clear() c1.paragraphs[0].clear() r0 = c0.paragraphs[0].add_run(label) r0.bold = True r0.font.size = Pt(10) r1 = c1.paragraphs[0].add_run(value) r1.font.size = Pt(10) # Header row shading from docx.oxml.ns import qn from docx.oxml import OxmlElement if i == 0: for cell in [c0, c1]: tc = cell._tc tcPr = tc.get_or_add_tcPr() shd = OxmlElement('w:shd') shd.set(qn('w:fill'), 'ADD8E6') shd.set(qn('w:val'), 'clear') tcPr.append(shd) # Set column widths from docx.shared import Inches as In2 tbl.columns[0].width = In2(1.8) tbl.columns[1].width = In2(4.0) doc.add_paragraph() reason = ("During the admission workup, a cataract was noted in the right eye on local examination. " "Given that the patient has a significant history of 7 years of chronic alcohol use and is on " "multiple long-term medications (including corticosteroids if any), ophthalmic assessment is " "warranted to determine the nature, grade, and visual impact of the cataract, and to advise " "on appropriate management or surgical intervention.") add_para(reason, size=11, space_after=8) add_para("Specific Request:", bold=True, size=11, space_after=4) bullets = [ "Complete slit-lamp examination and visual acuity assessment", "Grading of cataract (nuclear, cortical, posterior subcapsular) in the right eye", "Intraocular pressure measurement (rule out glaucoma given DCLD/medication history)", "Fundus examination (evaluate for hepatic retinopathy if applicable)", "Advice regarding timing and feasibility of surgical intervention given systemic disease (DCLD, coagulopathy risk)", "Recommendations for any ocular medications to be added to current drug regimen", ] for b in bullets: p = doc.add_paragraph(style="List Bullet") p.paragraph_format.space_after = Pt(3) r = p.add_run(b) r.font.size = Pt(11) doc.add_paragraph() note = ("Please note that the patient has Decompensated Chronic Liver Disease with coagulopathy risk. " "Pre-operative assessment including PT/INR should be reviewed before any surgical planning. " "We request your valued opinion and will be grateful for your expert management.") add_para(note, size=11, space_after=10) add_para("Thanking you,", size=11, space_after=16) # Signature block add_para("Dr. ______________________________", bold=True, size=11, space_after=0) add_para("Consultant Physician / Pharmacist-in-Charge", size=11, space_after=0) add_para("Department of Medicine / Pharmacy Practice", size=11, space_after=0) add_para("Nandha Medical College and Hospital", size=11, space_after=0) add_para("Contact: ____________________", size=11, space_after=12) add_horizontal_rule() footer_p = doc.add_paragraph() footer_p.alignment = WD_ALIGN_PARAGRAPH.CENTER r_f = footer_p.add_run("SOAP Analysis prepared by: Sindhana M, 2nd Pharm D | Pharmacotherapeutics-I | Second Sessional Practical Examination") r_f.font.size = Pt(9) r_f.font.color.rgb = GRAY r_f.italic = True out_path = "/tmp/workspace/sandhya_output/Sandhya_Referral_Letter.docx" doc.save(out_path) print(f"SUCCESS: {out_path}") EOF
ls -lh /tmp/workspace/sandhya_output/
| File | Description | Download |
|---|---|---|
| SOAP Analysis (Word) | Full clinical SOAP with tables, DRP analysis, monitoring plan | Sandhya SOAP Analysis Word Document · DOCX |
| SOAP Presentation (PowerPoint) | 11-slide professional presentation | Sandhya SOAP Presentation Presentation · PPTX |
| Referral Letter (Word) | Formal ophthalmology referral for right eye cataract | Sandhya Referral Letter Word Document · DOCX |