Doctor clinical sheets of case asthma
Realife feeling doctor sheets
mkdir -p /tmp/workspace/asthma-clinical-sheet && ls /tmp/workspace/asthma-clinical-sheet/
~/asthma-clinical-sheet/create_sheet.py
from docx import Document
from docx.shared import Pt, Inches, RGBColor, Cm
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_TABLE_ALIGNMENT, WD_ALIGN_VERTICAL
from docx.oxml.ns import qn
from docx.oxml import OxmlElement
import copy
doc = Document()
# ── Page margins ──
section = doc.sections[0]
section.page_width = Cm(21)
section.page_height = Cm(29.7)
section.top_margin = Cm(1.5)
section.bottom_margin = Cm(1.5)
section.left_margin = Cm(2.0)
section.right_margin = Cm(2.0)
# ── Helper: set cell background ──
def set_cell_bg(cell, hex_color):
tc = cell._tc
tcPr = 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_color)
tcPr.append(shd)
# ── Helper: set cell borders ──
def set_cell_border(cell, top=None, bottom=None, left=None, right=None):
tc = cell._tc
tcPr = tc.get_or_add_tcPr()
tcBorders = OxmlElement('w:tcBorders')
for side, val in [('top', top), ('bottom', bottom), ('left', left), ('right', right)]:
if val:
el = OxmlElement(f'w:{side}')
el.set(qn('w:val'), val.get('val', 'single'))
el.set(qn('w:sz'), val.get('sz', '4'))
el.set(qn('w:space'), val.get('space', '0'))
el.set(qn('w:color'), val.get('color', '000000'))
tcBorders.append(el)
tcPr.append(tcBorders)
# ── Helper: paragraph spacing ──
def para_space(p, before=0, after=0, line=None):
pPr = p._p.get_or_add_pPr()
spng = OxmlElement('w:spacing')
spng.set(qn('w:before'), str(before))
spng.set(qn('w:after'), str(after))
if line:
spng.set(qn('w:line'), str(line))
spng.set(qn('w:lineRule'), 'auto')
pPr.append(spng)
def add_run(para, text, bold=False, italic=False, size=10, color=None, underline=False):
run = para.add_run(text)
run.bold = bold
run.italic = italic
run.underline = underline
run.font.size = Pt(size)
run.font.name = 'Arial'
if color:
run.font.color.rgb = RGBColor(*color)
return run
# ══════════════════════════════════════════════
# HOSPITAL HEADER
# ══════════════════════════════════════════════
hosp_table = doc.add_table(rows=1, cols=3)
hosp_table.style = 'Table Grid'
hosp_table.alignment = WD_TABLE_ALIGNMENT.CENTER
# Left cell – Hospital name
lc = hosp_table.cell(0, 0)
set_cell_bg(lc, '1A3A5C')
lc.width = Inches(2.8)
lp = lc.paragraphs[0]
lp.alignment = WD_ALIGN_PARAGRAPH.CENTER
add_run(lp, 'CITY GENERAL HOSPITAL', bold=True, size=13, color=(255,255,255))
lp.add_run('\n')
add_run(lp, 'Department of Respiratory Medicine', bold=False, size=8.5, color=(200,220,255))
para_space(lp, before=80, after=80)
# Centre cell – Red Cross / logo placeholder
cc = hosp_table.cell(0, 1)
set_cell_bg(cc, '1A3A5C')
cc.width = Inches(0.9)
cp = cc.paragraphs[0]
cp.alignment = WD_ALIGN_PARAGRAPH.CENTER
add_run(cp, '✚', bold=True, size=22, color=(220, 50, 50))
para_space(cp, before=60, after=60)
# Right cell – form title
rc = hosp_table.cell(0, 2)
set_cell_bg(rc, '0E2540')
rc.width = Inches(2.8)
rp = rc.paragraphs[0]
rp.alignment = WD_ALIGN_PARAGRAPH.CENTER
add_run(rp, 'ASTHMA CLINICAL CASE SHEET', bold=True, size=11, color=(255, 220, 50))
rp.add_run('\n')
add_run(rp, 'Form No: RES-AST-001 | Rev: 3.0', size=7.5, color=(180, 200, 230))
para_space(rp, before=80, after=80)
doc.add_paragraph()
# ══════════════════════════════════════════════
# SECTION helper
# ══════════════════════════════════════════════
def section_heading(doc, title, icon=''):
p = doc.add_paragraph()
p.alignment = WD_ALIGN_PARAGRAPH.LEFT
para_space(p, before=60, after=40)
add_run(p, f' {icon} {title} ', bold=True, size=10, color=(255,255,255))
# Blue bar background via paragraph shading
pPr = p._p.get_or_add_pPr()
shd = OxmlElement('w:shd')
shd.set(qn('w:val'), 'clear')
shd.set(qn('w:color'), 'auto')
shd.set(qn('w:fill'), '1A3A5C')
pPr.append(shd)
return p
def field_row(table, row_idx, label, value='', label_bg='EBF1F8', value_bg='FFFFFF'):
r = table.rows[row_idx]
lc_cell = r.cells[0]
vc_cell = r.cells[1]
set_cell_bg(lc_cell, label_bg)
set_cell_bg(vc_cell, value_bg)
lp = lc_cell.paragraphs[0]
add_run(lp, label, bold=True, size=9, color=(30,60,100))
para_space(lp, before=40, after=40)
vp = vc_cell.paragraphs[0]
add_run(vp, value, size=9)
para_space(vp, before=40, after=40)
# ══════════════════════════════════════════════
# 1. PATIENT DEMOGRAPHICS
# ══════════════════════════════════════════════
section_heading(doc, 'SECTION 1 — PATIENT DEMOGRAPHICS', '👤')
dem = doc.add_table(rows=6, cols=4)
dem.style = 'Table Grid'
dem.alignment = WD_TABLE_ALIGNMENT.CENTER
labels = [
['Patient Name:', 'Ahmed Al-Rashidi', 'MR Number:', 'MR-2026-08847'],
['Date of Birth:', '14 / 03 / 1989', 'Age:', '37 years'],
['Gender:', 'Male ☑ Female ☐', 'Nationality:', 'Saudi Arabian'],
['Contact:', '+966 55 234 7890', 'Emergency Contact:', 'Fatima Al-Rashidi (wife)'],
['Address:', 'Villa 12, Al-Rawdah District, Riyadh', 'Occupation:', 'School Teacher'],
['Insurance:', 'Bupa Arabia — Policy #BP-94512', 'Attending Physician:', 'Dr. Khalid Al-Otaibi, MD'],
]
for ri, row in enumerate(labels):
for ci in range(0, 4, 2):
cell_l = dem.cell(ri, ci)
cell_v = dem.cell(ri, ci+1)
set_cell_bg(cell_l, 'D6E4F0')
set_cell_bg(cell_v, 'FFFFFF')
pl = cell_l.paragraphs[0]
pv = cell_v.paragraphs[0]
add_run(pl, row[ci], bold=True, size=8.5, color=(20,50,100))
add_run(pv, row[ci+1], size=8.5)
para_space(pl, before=40, after=40)
para_space(pv, before=40, after=40)
doc.add_paragraph()
# ══════════════════════════════════════════════
# 2. VISIT INFORMATION
# ══════════════════════════════════════════════
section_heading(doc, 'SECTION 2 — VISIT INFORMATION', '📋')
vis = doc.add_table(rows=3, cols=4)
vis.style = 'Table Grid'
vis.alignment = WD_TABLE_ALIGNMENT.CENTER
vis_data = [
['Date of Visit:', '19 / 07 / 2026', 'Visit Type:', 'Acute Follow-Up'],
['Time:', '09:15 AM', 'Referred By:', 'Emergency Dept.'],
['Previous Visits:', '3 (last: 14/04/2026)', 'Allergies Documented:', 'Yes — see Allergy section'],
]
for ri, row in enumerate(vis_data):
for ci in range(0, 4, 2):
cl = vis.cell(ri, ci); cv = vis.cell(ri, ci+1)
set_cell_bg(cl, 'D6E4F0'); set_cell_bg(cv, 'FFFFFF')
pl = cl.paragraphs[0]; pv = cv.paragraphs[0]
add_run(pl, row[ci], bold=True, size=8.5, color=(20,50,100))
add_run(pv, row[ci+1], size=8.5)
para_space(pl, before=40, after=40); para_space(pv, before=40, after=40)
doc.add_paragraph()
# ══════════════════════════════════════════════
# 3. CHIEF COMPLAINT & HISTORY OF PRESENT ILLNESS
# ══════════════════════════════════════════════
section_heading(doc, 'SECTION 3 — CHIEF COMPLAINT & HISTORY OF PRESENT ILLNESS', '🩺')
cc_table = doc.add_table(rows=5, cols=2)
cc_table.style = 'Table Grid'
cc_table.alignment = WD_TABLE_ALIGNMENT.CENTER
cc_table.columns[0].width = Cm(4.5)
cc_table.columns[1].width = Cm(12)
cc_data = [
('Chief Complaint:', 'Progressive dyspnoea, wheezing, and chest tightness × 3 days'),
('Onset & Duration:', 'Gradual onset 3 days ago; worsening over past 24 hours'),
('Precipitating Factors:', 'Exposure to dust (classroom renovation) + recent URTI (5 days ago)'),
('Associated Symptoms:', 'Dry cough (worse at night), mild rhinorrhoea, no fever, no haemoptysis, no chest pain'),
('Relieving Factors:', 'Partial relief with salbutamol puffer (4 puffs used in last 12 h)'),
]
for ri, (lbl, val) in enumerate(cc_data):
cl = cc_table.cell(ri, 0); cv = cc_table.cell(ri, 1)
set_cell_bg(cl, 'D6E4F0'); set_cell_bg(cv, 'FFFFFF')
pl = cl.paragraphs[0]; pv = cv.paragraphs[0]
add_run(pl, lbl, bold=True, size=8.5, color=(20,50,100))
add_run(pv, val, size=8.5)
para_space(pl, before=40, after=40); para_space(pv, before=40, after=40)
doc.add_paragraph()
# ══════════════════════════════════════════════
# 4. PAST MEDICAL & FAMILY HISTORY
# ══════════════════════════════════════════════
section_heading(doc, 'SECTION 4 — PAST MEDICAL, SURGICAL & FAMILY HISTORY', '📜')
hx_table = doc.add_table(rows=4, cols=2)
hx_table.style = 'Table Grid'
hx_table.columns[0].width = Cm(4.5)
hx_table.columns[1].width = Cm(12)
hx_data = [
('Past Medical Hx:', 'Bronchial asthma (diagnosed 2014) | Allergic rhinitis (2016) | No T2DM | No HTN'),
('Surgical Hx:', 'Appendicectomy (2008) — uneventful | No thoracic surgeries'),
('Family Hx:', 'Mother: asthma + eczema | Father: allergic rhinitis | No TB, no malignancy'),
('Social Hx:', 'Non-smoker | No alcohol | Lives with wife + 2 children | No pets | Teaches indoor classes'),
]
for ri, (lbl, val) in enumerate(hx_data):
cl = hx_table.cell(ri, 0); cv = hx_table.cell(ri, 1)
set_cell_bg(cl, 'D6E4F0'); set_cell_bg(cv, 'FFFFFF')
pl = cl.paragraphs[0]; pv = cv.paragraphs[0]
add_run(pl, lbl, bold=True, size=8.5, color=(20,50,100))
add_run(pv, val, size=8.5)
para_space(pl, before=40, after=40); para_space(pv, before=40, after=40)
doc.add_paragraph()
# ══════════════════════════════════════════════
# 5. CURRENT MEDICATIONS & ALLERGIES
# ══════════════════════════════════════════════
section_heading(doc, 'SECTION 5 — CURRENT MEDICATIONS & ALLERGIES', '💊')
med_tbl = doc.add_table(rows=1, cols=5)
med_tbl.style = 'Table Grid'
headers = ['Drug Name', 'Dose', 'Route', 'Frequency', 'Duration']
for ci, h in enumerate(headers):
cell = med_tbl.cell(0, ci)
set_cell_bg(cell, '1A3A5C')
p = cell.paragraphs[0]
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
add_run(p, h, bold=True, size=9, color=(255,255,255))
para_space(p, before=40, after=40)
meds = [
['Budesonide / Formoterol (Symbicort 200/6)', '1 puff', 'Inhaled', 'BD', 'Ongoing'],
['Salbutamol (Ventolin) — Reliever', '100 mcg / puff', 'Inhaled', 'PRN', 'As needed'],
['Montelukast (Singulair)', '10 mg', 'Oral', 'Nocte', 'Ongoing'],
['Normal Saline nasal spray', '2 sprays each nostril', 'Intranasal', 'BD', 'PRN rhinitis'],
]
for med in meds:
row = med_tbl.add_row()
for ci, val in enumerate(med):
cell = row.cells[ci]
set_cell_bg(cell, 'FFFFFF')
p = cell.paragraphs[0]
add_run(p, val, size=8.5)
para_space(p, before=30, after=30)
# Allergies
doc.add_paragraph()
alg_tbl = doc.add_table(rows=2, cols=4)
alg_tbl.style = 'Table Grid'
alg_headers = ['Allergen', 'Type', 'Reaction', 'Severity']
for ci, h in enumerate(alg_headers):
cell = alg_tbl.cell(0, ci)
set_cell_bg(cell, 'C0392B')
p = cell.paragraphs[0]
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
add_run(p, h, bold=True, size=9, color=(255,255,255))
para_space(p, before=40, after=40)
alg_data = ['Aspirin / NSAIDs', 'Drug', 'Bronchospasm, urticaria', '⚠ HIGH — Avoid']
for ci, val in enumerate(alg_data):
cell = alg_tbl.cell(1, ci)
set_cell_bg(cell, 'FDECEA')
p = cell.paragraphs[0]
add_run(p, val, size=8.5, bold=(ci==3), color=(180,0,0) if ci==3 else (0,0,0))
para_space(p, before=30, after=30)
doc.add_paragraph()
# ══════════════════════════════════════════════
# 6. PHYSICAL EXAMINATION
# ══════════════════════════════════════════════
section_heading(doc, 'SECTION 6 — PHYSICAL EXAMINATION', '🔬')
# Vitals
vitals_p = doc.add_paragraph()
add_run(vitals_p, 'VITAL SIGNS', bold=True, size=9, color=(20,50,100))
para_space(vitals_p, before=40, after=20)
vit_tbl = doc.add_table(rows=2, cols=7)
vit_tbl.style = 'Table Grid'
vit_headers = ['Temp (°C)', 'HR (bpm)', 'RR (/min)', 'BP (mmHg)', 'SpO₂ (%)', 'Peak Flow (L/min)', 'Weight (kg)']
vit_vals = ['37.1', '102', '24', '124/78', '93% (room air)', '240 ← 60% predicted', '74']
for ci, h in enumerate(vit_headers):
cell = vit_tbl.cell(0, ci); set_cell_bg(cell, '2471A3')
p = cell.paragraphs[0]; p.alignment = WD_ALIGN_PARAGRAPH.CENTER
add_run(p, h, bold=True, size=8, color=(255,255,255)); para_space(p, before=30, after=30)
for ci, v in enumerate(vit_vals):
cell = vit_tbl.cell(1, ci)
color = 'FFD7D7' if ci in [1,2,4,5] else 'FFFFFF'
set_cell_bg(cell, color)
p = cell.paragraphs[0]; p.alignment = WD_ALIGN_PARAGRAPH.CENTER
is_abn = ci in [1,2,4,5]
add_run(p, v, size=8.5, bold=is_abn, color=(180,0,0) if is_abn else (0,0,0))
para_space(p, before=30, after=30)
doc.add_paragraph()
# Systems exam
sys_tbl = doc.add_table(rows=6, cols=2)
sys_tbl.style = 'Table Grid'
sys_tbl.columns[0].width = Cm(4)
sys_tbl.columns[1].width = Cm(12.5)
sys_data = [
('General:', 'Alert, conscious, oriented. In mild-moderate respiratory distress. Sitting upright. Using accessory muscles. Speaking in full sentences.'),
('Respiratory:', 'Chest: hyperinflated. Bilateral diffuse expiratory wheeze on auscultation. Prolonged expiratory phase. No crepitations. Percussion: resonant bilaterally. Trachea midline.'),
('Cardiovascular:', 'Regular rate and rhythm. S1 S2 heard. No murmurs. No JVP elevation. Capillary refill < 2 sec.'),
('Abdomen:', 'Soft, non-tender. No organomegaly. Bowel sounds normal.'),
('ENT / Head-Neck:', 'Nasal mucosa mildly congested. No nasal polyps. Pharynx mildly erythematous. Tonsils normal. No cervical lymphadenopathy.'),
('Skin:', 'No eczema, no urticaria. No cyanosis. No clubbing.'),
]
for ri, (lbl, val) in enumerate(sys_data):
cl = sys_tbl.cell(ri, 0); cv = sys_tbl.cell(ri, 1)
set_cell_bg(cl, 'D6E4F0'); set_cell_bg(cv, 'FFFFFF')
pl = cl.paragraphs[0]; pv = cv.paragraphs[0]
add_run(pl, lbl, bold=True, size=8.5, color=(20,50,100))
add_run(pv, val, size=8.5)
para_space(pl, before=40, after=40); para_space(pv, before=40, after=40)
doc.add_paragraph()
# ══════════════════════════════════════════════
# 7. SEVERITY ASSESSMENT (GINA)
# ══════════════════════════════════════════════
section_heading(doc, 'SECTION 7 — ASTHMA SEVERITY ASSESSMENT (GINA 2024)', '⚠')
sev_tbl = doc.add_table(rows=2, cols=5)
sev_tbl.style = 'Table Grid'
sev_cols = ['Criterion', 'Mild', 'Moderate', 'Severe', 'Life-Threatening']
sev_colors = ['D6E4F0', 'D4EFDF', 'FCF3CF', 'FDECEA', 'F9EBEA']
for ci, (h, col) in enumerate(zip(sev_cols, sev_colors)):
cell = sev_tbl.cell(0, ci); set_cell_bg(cell, '1A3A5C')
p = cell.paragraphs[0]; p.alignment = WD_ALIGN_PARAGRAPH.CENTER
add_run(p, h, bold=True, size=8.5, color=(255,255,255)); para_space(p, before=30, after=30)
checks = ['GINA Parameters', '—', '✔ THIS PATIENT', '—', '—']
row_data = [
'SpO₂ > 95% / 92-95% / <92% / <90%',
'PEF > 80%',
'PEF 60–80% predicted',
'PEF <60%',
'Pulsus paradoxus'
]
for ci, val in enumerate(checks):
cell = sev_tbl.cell(1, ci)
bg = 'FCF3CF' if ci == 2 else 'FFFFFF'
set_cell_bg(cell, bg)
p = cell.paragraphs[0]; p.alignment = WD_ALIGN_PARAGRAPH.CENTER
bold = ci == 2
color = (180,0,0) if ci == 2 else (0,0,0)
add_run(p, val, bold=bold, size=9, color=color)
para_space(p, before=30, after=30)
verdict_p = doc.add_paragraph()
para_space(verdict_p, before=40, after=40)
add_run(verdict_p, ' ASSESSMENT: ', bold=True, size=10, color=(255,255,255))
add_run(verdict_p, ' MODERATE ACUTE ASTHMA EXACERBATION ', bold=True, size=10, color=(180,0,0))
pPr = verdict_p._p.get_or_add_pPr()
shd = OxmlElement('w:shd'); shd.set(qn('w:val'),'clear'); shd.set(qn('w:color'),'auto'); shd.set(qn('w:fill'),'FFF9C4')
pPr.append(shd)
doc.add_paragraph()
# ══════════════════════════════════════════════
# 8. INVESTIGATIONS
# ══════════════════════════════════════════════
section_heading(doc, 'SECTION 8 — INVESTIGATIONS ORDERED / RESULTS', '🔭')
inv_tbl = doc.add_table(rows=1, cols=4)
inv_tbl.style = 'Table Grid'
inv_headers = ['Investigation', 'Result', 'Normal Range', 'Interpretation']
for ci, h in enumerate(inv_headers):
cell = inv_tbl.cell(0, ci); set_cell_bg(cell, '1A3A5C')
p = cell.paragraphs[0]; p.alignment = WD_ALIGN_PARAGRAPH.CENTER
add_run(p, h, bold=True, size=9, color=(255,255,255)); para_space(p, before=30, after=30)
inv_data = [
['SpO₂ (pulse oximetry)', '93%', '95–100%', '⬇ Low — supplemental O₂ initiated'],
['Peak Expiratory Flow (PEF)', '240 L/min', '400 L/min predicted', '⬇ 60% of predicted — Moderate'],
['ABG (Arterial Blood Gas)', 'pH 7.44 | PaO₂ 72 | PaCO₂ 34', '7.35–7.45 | 80–100 | 35–45', 'Mild hypoxaemia; resp alkalosis'],
['CXR (Chest X-ray)', 'Hyperinflation; no consolidation; no pneumothorax', '—', 'Consistent with acute asthma'],
['FBC (Full Blood Count)', 'WBC 9.8 × 10⁹/L; Eos 0.6 (6%)', 'WBC 4–11 × 10⁹/L; Eos <5%', '⬆ Eosinophilia — allergic component'],
['Serum IgE', '480 IU/mL', '< 100 IU/mL', '⬆ Elevated — atopic asthma'],
['ECG', 'Sinus tachycardia — rate 102/min', 'Normal sinus rhythm', 'Tachycardia (response to hypoxia/SABA)'],
['Spirometry (pre-bronchodilator)', 'FEV₁ 62% predicted; FEV₁/FVC 0.64', '> 80% predicted; > 0.70', '⬇ Obstructive pattern'],
['Spirometry (post-bronchodilator)', 'FEV₁ 78% predicted; +16% change', '< 12% change = irreversible', '✔ Reversible obstruction confirmed'],
]
for row in inv_data:
trow = inv_tbl.add_row()
for ci, val in enumerate(row):
cell = trow.cells[ci]
abn = '⬇' in val or '⬆' in val
set_cell_bg(cell, 'FFF5F5' if abn else 'FFFFFF')
p = cell.paragraphs[0]
col = (160,0,0) if abn else (0,0,0)
add_run(p, val, size=8, color=col)
para_space(p, before=25, after=25)
doc.add_paragraph()
# ══════════════════════════════════════════════
# 9. DIAGNOSIS
# ══════════════════════════════════════════════
section_heading(doc, 'SECTION 9 — DIAGNOSIS', '✅')
dx_tbl = doc.add_table(rows=3, cols=2)
dx_tbl.style = 'Table Grid'
dx_tbl.columns[0].width = Cm(4)
dx_tbl.columns[1].width = Cm(12.5)
dx_data = [
('Primary Diagnosis:', 'Moderate acute exacerbation of bronchial asthma (ICD-10: J45.41)'),
('Secondary Diagnosis:', 'Allergic rhinitis (J30.1) | Viral-triggered exacerbation (URTI)'),
('Differential Diagnoses:', 'COPD exacerbation (less likely — age, non-smoker) | Vocal cord dysfunction | Pulmonary embolism (excluded by clinical picture)'),
]
for ri, (lbl, val) in enumerate(dx_data):
cl = dx_tbl.cell(ri, 0); cv = dx_tbl.cell(ri, 1)
set_cell_bg(cl, 'D6E4F0'); set_cell_bg(cv, 'FFFFFF')
pl = cl.paragraphs[0]; pv = cv.paragraphs[0]
add_run(pl, lbl, bold=True, size=8.5, color=(20,50,100))
add_run(pv, val, size=8.5)
para_space(pl, before=40, after=40); para_space(pv, before=40, after=40)
doc.add_paragraph()
# ══════════════════════════════════════════════
# 10. TREATMENT PLAN
# ══════════════════════════════════════════════
section_heading(doc, 'SECTION 10 — MANAGEMENT & TREATMENT PLAN', '💉')
# Immediate management
imm_p = doc.add_paragraph()
add_run(imm_p, 'A. IMMEDIATE MANAGEMENT (Emergency Bay)', bold=True, size=9, color=(20,50,100))
para_space(imm_p, before=40, after=20)
imm_tbl = doc.add_table(rows=1, cols=4)
imm_tbl.style = 'Table Grid'
for ci, h in enumerate(['Intervention', 'Dose / Detail', 'Route', 'Time Given']):
cell = imm_tbl.cell(0, ci); set_cell_bg(cell, '2E86C1')
p = cell.paragraphs[0]; p.alignment = WD_ALIGN_PARAGRAPH.CENTER
add_run(p, h, bold=True, size=8.5, color=(255,255,255)); para_space(p, before=30, after=30)
imm_rows = [
['Oxygen therapy', '4 L/min via nasal cannula — target SpO₂ > 95%', 'Inhalation', '09:20 AM'],
['Salbutamol nebulisation', '5 mg in 3 mL normal saline — repeat × 3 q20min', 'Nebulised', '09:22 AM'],
['Ipratropium bromide', '500 mcg — mixed with salbutamol (first 3 doses)', 'Nebulised', '09:22 AM'],
['Prednisolone (oral)', '50 mg stat — then 40 mg OD × 5 days', 'Oral', '09:30 AM'],
['Magnesium sulphate IV', '2 g in 100 mL NS over 20 min (if poor response)', 'IV infusion', 'PRN / 10:00 AM'],
]
for row in imm_rows:
trow = imm_tbl.add_row()
for ci, val in enumerate(row):
cell = trow.cells[ci]; set_cell_bg(cell, 'FFFFFF')
p = cell.paragraphs[0]
add_run(p, val, size=8.5); para_space(p, before=25, after=25)
doc.add_paragraph()
# Step-up therapy
stu_p = doc.add_paragraph()
add_run(stu_p, 'B. STEP-UP MAINTENANCE THERAPY (GINA Step 3 → Step 4)', bold=True, size=9, color=(20,50,100))
para_space(stu_p, before=40, after=20)
step_tbl = doc.add_table(rows=1, cols=4)
step_tbl.style = 'Table Grid'
for ci, h in enumerate(['Drug', 'Dose', 'Frequency', 'Notes']):
cell = step_tbl.cell(0, ci); set_cell_bg(cell, '2E86C1')
p = cell.paragraphs[0]; p.alignment = WD_ALIGN_PARAGRAPH.CENTER
add_run(p, h, bold=True, size=8.5, color=(255,255,255)); para_space(p, before=30, after=30)
step_rows = [
['Budesonide/Formoterol 400/12 (upgraded)', '1 puff BD', 'Twice daily', 'Step-up from 200/6 — reassess in 4 weeks'],
['Montelukast (continue)', '10 mg nocte', 'Once daily at night', 'Leukotriene receptor antagonist'],
['Salbutamol (reliever)', 'PRN (max 4 puffs/day)', 'As needed', 'If using > 2×/week → reassess control'],
['Chlorpheniramine (antihistamine)', '4 mg TDS PRN', 'PRN for rhinitis', 'Short-term for allergic symptoms'],
]
for row in step_rows:
trow = step_tbl.add_row()
for ci, val in enumerate(row):
cell = trow.cells[ci]; set_cell_bg(cell, 'FFFFFF')
p = cell.paragraphs[0]
add_run(p, val, size=8.5); para_space(p, before=25, after=25)
doc.add_paragraph()
# ══════════════════════════════════════════════
# 11. PATIENT EDUCATION & ACTION PLAN
# ══════════════════════════════════════════════
section_heading(doc, 'SECTION 11 — PATIENT EDUCATION & ASTHMA ACTION PLAN', '📚')
edu_tbl = doc.add_table(rows=4, cols=2)
edu_tbl.style = 'Table Grid'
edu_tbl.columns[0].width = Cm(4.5)
edu_tbl.columns[1].width = Cm(12)
edu_data = [
('GREEN Zone\n(PEF > 80%)', 'Continue regular preventer. Reliever only if needed. Maintain normal activities.'),
('YELLOW Zone\n(PEF 60–80%)', 'Increase reliever to 2–4 puffs q4h. Start prednisolone 40 mg/day. Contact clinic within 24h.'),
('RED Zone\n(PEF < 60%)', '4–8 puffs salbutamol immediately. Call family. Go to Emergency NOW. Do not drive alone.'),
('Education Given:', 'Inhaler technique demonstrated (spacer use). Avoidance of triggers. Importance of preventer compliance. Written action plan provided. Asthma diary issued.'),
]
edu_bg = ['D4EFDF','FCF3CF','FDECEA','E8F5E9']
for ri, ((lbl, val), bg) in enumerate(zip(edu_data, edu_bg)):
cl = edu_tbl.cell(ri, 0); cv = edu_tbl.cell(ri, 1)
set_cell_bg(cl, bg); set_cell_bg(cv, 'FFFFFF')
pl = cl.paragraphs[0]; pv = cv.paragraphs[0]
add_run(pl, lbl, bold=True, size=8.5, color=(20,50,100))
add_run(pv, val, size=8.5)
para_space(pl, before=50, after=50); para_space(pv, before=50, after=50)
doc.add_paragraph()
# ══════════════════════════════════════════════
# 12. FOLLOW-UP & MONITORING
# ══════════════════════════════════════════════
section_heading(doc, 'SECTION 12 — FOLLOW-UP, MONITORING & DISPOSITION', '📅')
fup_tbl = doc.add_table(rows=4, cols=2)
fup_tbl.style = 'Table Grid'
fup_tbl.columns[0].width = Cm(4.5)
fup_tbl.columns[1].width = Cm(12)
fup_data = [
('Disposition:', 'Admit to Short Stay Unit (SSU) for 6–12 h observation — reassess at 2h and 4h post-nebulisation'),
('Discharge Criteria:', 'SpO₂ ≥ 95% on room air | PEF ≥ 70% predicted | Respiratory rate < 20/min | No accessory muscle use'),
('Follow-up:', 'Respiratory clinic in 1 week (26/07/2026). GP review in 48 h after discharge. Spirometry + FeNO test in 4 weeks.'),
('Referrals:', 'Allergy & Immunology referral for allergen skin testing. Consider biologics (dupilumab/mepolizumab) if Step 4 fails.'),
]
for ri, (lbl, val) in enumerate(fup_data):
cl = fup_tbl.cell(ri, 0); cv = fup_tbl.cell(ri, 1)
set_cell_bg(cl, 'D6E4F0'); set_cell_bg(cv, 'FFFFFF')
pl = cl.paragraphs[0]; pv = cv.paragraphs[0]
add_run(pl, lbl, bold=True, size=8.5, color=(20,50,100))
add_run(pv, val, size=8.5)
para_space(pl, before=40, after=40); para_space(pv, before=40, after=40)
doc.add_paragraph()
# ══════════════════════════════════════════════
# 13. PHYSICIAN SIGNATURE
# ══════════════════════════════════════════════
section_heading(doc, 'SECTION 13 — PHYSICIAN SIGN-OFF', '✍')
sig_tbl = doc.add_table(rows=2, cols=3)
sig_tbl.style = 'Table Grid'
sig_headers = ['Attending Physician', 'Resident / Intern', 'Nurse In-Charge']
for ci, h in enumerate(sig_headers):
cell = sig_tbl.cell(0, ci); set_cell_bg(cell, 'D6E4F0')
p = cell.paragraphs[0]; p.alignment = WD_ALIGN_PARAGRAPH.CENTER
add_run(p, h, bold=True, size=9, color=(20,50,100)); para_space(p, before=40, after=40)
sig_vals = [
'Dr. Khalid Al-Otaibi, MD\nConsultant Pulmonologist\nSign: _______________\nDate: 19/07/2026',
'Dr. Sara Mahmoud, MBBCh\nPGY-2 Internal Medicine\nSign: _______________\nDate: 19/07/2026',
'Nurse Hana Al-Zahrani, RN\nRespiratory Ward\nSign: _______________\nDate: 19/07/2026',
]
for ci, val in enumerate(sig_vals):
cell = sig_tbl.cell(1, ci); set_cell_bg(cell, 'FFFFFF')
p = cell.paragraphs[0]; p.alignment = WD_ALIGN_PARAGRAPH.CENTER
add_run(p, val, size=8.5); para_space(p, before=60, after=60)
# ── Footer note ──
doc.add_paragraph()
ft = doc.add_paragraph()
ft.alignment = WD_ALIGN_PARAGRAPH.CENTER
add_run(ft, 'CONFIDENTIAL — For authorized medical personnel only | City General Hospital — Respiratory Medicine | Form RES-AST-001 | GINA 2024 Guidelines', size=7, color=(120,120,120), italic=True)
# ── Save ──
out = '/tmp/workspace/asthma-clinical-sheet/Asthma_Clinical_Case_Sheet.docx'
doc.save(out)
print(f'Saved: {out}')
python3 /tmp/workspace/asthma-clinical-sheet/create_sheet.py
Asthma Clinical Case Sheet
Word Document · DOCX
| Section | Contents |
|---|---|
| Header | Hospital name, department, form number, red cross logo |
| 1. Patient Demographics | Name, MR#, DOB, gender, insurance, attending physician |
| 2. Visit Information | Date, time, visit type, referral source |
| 3. Chief Complaint & HPI | Dyspnoea, wheezing, precipitating factors, timeline |
| 4. Past Medical / Family Hx | Asthma Dx 2014, allergic rhinitis, family atopy |
| 5. Medications & Allergies | Symbicort, Ventolin, Montelukast + Aspirin allergy (red-flagged) |
| 6. Physical Examination | Vital signs table (abnormals highlighted in red), full systems exam |
| 7. Severity Assessment | GINA 2024 grid - classified as Moderate Acute Exacerbation |
| 8. Investigations | SpO₂, PEF, ABG, CXR, FBC, IgE, ECG, spirometry with results & interpretation |
| 9. Diagnosis | Primary + secondary + differentials with ICD-10 codes |
| 10. Treatment Plan | Immediate ER management (O₂, nebulisers, steroids, MgSO₄) + step-up maintenance |
| 11. Action Plan | Green / Yellow / Red zone instructions + patient education checklist |
| 12. Follow-up | Discharge criteria, clinic dates, specialist referrals |
| 13. Sign-off | Attending consultant, resident, and nurse signature blocks |
Which app this application open
| App | Platform | Free? |
|---|---|---|
| Microsoft Word | Android & iPhone | Free (basic) |
| WPS Office | Android & iPhone | Free |
| Google Docs | Android & iPhone | Free |
| OnlyOffice | Android & iPhone | Free |
| App | Platform | Free? |
|---|---|---|
| Microsoft Word | Windows & Mac | Paid (Office 365) |
| Google Docs | Any browser | Free |
| LibreOffice Writer | Windows, Mac, Linux | Free |
| WPS Office | Windows & Mac | Free |
| Pages | Mac / iPhone | Free |
.docx file.docx format is the most universal document format in the world - almost any office app on any device will open it correctly.