Clinical history taking assessment,test,patient communication and documentation of finding made a pdf

Asking for Preferences
Reading File
Running Command

mkdir -p /tmp/workspace/clinical-history-guide && ls /tmp/workspace/

Writing File

~/clinical-history-guide/generate_pdf.py

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

OUTPUT_PATH = "/tmp/workspace/clinical-history-guide/Clinical_History_Taking_Guide.pdf"

# ── Color Palette ──────────────────────────────────────────────────────────────
NAVY      = colors.HexColor("#1A3A5C")
TEAL      = colors.HexColor("#0D7377")
LIGHT_TEAL= colors.HexColor("#E8F6F7")
AMBER     = colors.HexColor("#E8A838")
LIGHT_AMBER=colors.HexColor("#FEF9EC")
LIGHT_GREY= colors.HexColor("#F4F6F9")
MID_GREY  = colors.HexColor("#B0B8C4")
WHITE     = colors.white
DARK_TEXT = colors.HexColor("#1C2533")
RED_ACCENT= colors.HexColor("#C0392B")

# ── Document ───────────────────────────────────────────────────────────────────
doc = SimpleDocTemplate(
    OUTPUT_PATH,
    pagesize=A4,
    leftMargin=2*cm, rightMargin=2*cm,
    topMargin=2.2*cm, bottomMargin=2.2*cm,
    title="Clinical History Taking – Medical Student Study Guide",
    author="Orris Medical Education"
)

W, H = A4
styles = getSampleStyleSheet()

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

sTitle = S("sTitle", fontSize=28, textColor=WHITE, fontName="Helvetica-Bold",
           alignment=TA_CENTER, spaceAfter=6)
sSubtitle = S("sSubtitle", fontSize=13, textColor=LIGHT_TEAL, fontName="Helvetica",
              alignment=TA_CENTER, spaceAfter=4)
sMeta = S("sMeta", fontSize=9, textColor=MID_GREY, fontName="Helvetica",
          alignment=TA_CENTER)

sH1 = S("sH1", fontSize=16, textColor=WHITE, fontName="Helvetica-Bold",
        alignment=TA_LEFT, spaceAfter=4, spaceBefore=2,
        leftIndent=0, backColor=NAVY,
        borderPad=6)
sH2 = S("sH2", fontSize=12, textColor=NAVY, fontName="Helvetica-Bold",
        spaceAfter=4, spaceBefore=8, leftIndent=0)
sH3 = S("sH3", fontSize=10.5, textColor=TEAL, fontName="Helvetica-Bold",
        spaceAfter=3, spaceBefore=5)

sBody = S("sBody", fontSize=9.5, textColor=DARK_TEXT, fontName="Helvetica",
          leading=15, spaceAfter=4, alignment=TA_JUSTIFY)
sBullet = S("sBullet", fontSize=9.5, textColor=DARK_TEXT, fontName="Helvetica",
            leading=14, leftIndent=14, spaceAfter=2)
sNote = S("sNote", fontSize=8.8, textColor=colors.HexColor("#5A6370"),
          fontName="Helvetica-Oblique", leading=13, leftIndent=10, spaceAfter=2)
sBox  = S("sBox", fontSize=9.5, textColor=DARK_TEXT, fontName="Helvetica",
          leading=14, spaceAfter=2, leftIndent=6)
sBoxBold = S("sBoxBold", fontSize=9.5, textColor=NAVY, fontName="Helvetica-Bold",
             leading=14, spaceAfter=1, leftIndent=6)
sWarn = S("sWarn", fontSize=9.5, textColor=RED_ACCENT, fontName="Helvetica-Bold",
          leading=14, leftIndent=6, spaceAfter=2)
sTableHead = S("sTableHead", fontSize=9, textColor=WHITE, fontName="Helvetica-Bold",
               alignment=TA_CENTER)
sTableCell = S("sTableCell", fontSize=8.5, textColor=DARK_TEXT, fontName="Helvetica",
               leading=12, alignment=TA_LEFT)

def hr(): return HRFlowable(width="100%", thickness=0.6, color=MID_GREY, spaceAfter=6, spaceBefore=4)
def sp(h=0.3): return Spacer(1, h*cm)

# ── Helper: Section Header ─────────────────────────────────────────────────────
def section_header(text, num):
    data = [[Paragraph(f"  {num}   {text}", sH1)]]
    t = Table(data, colWidths=[W - 4*cm])
    t.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), NAVY),
        ("ROUNDEDCORNERS", [4]),
        ("TOPPADDING",   (0,0), (-1,-1), 7),
        ("BOTTOMPADDING",(0,0), (-1,-1), 7),
        ("LEFTPADDING",  (0,0), (-1,-1), 0),
        ("RIGHTPADDING", (0,0), (-1,-1), 4),
    ]))
    return t

# ── Helper: Teal Info Box ──────────────────────────────────────────────────────
def info_box(title, items, color=LIGHT_TEAL, border=TEAL):
    rows = [[Paragraph(f"<b>{title}</b>", sH3)]]
    for item in items:
        rows.append([Paragraph(f"• {item}", sBox)])
    t = Table(rows, colWidths=[W - 4*cm])
    t.setStyle(TableStyle([
        ("BACKGROUND",   (0,0), (-1,-1), color),
        ("BOX",          (0,0), (-1,-1), 1.2, border),
        ("TOPPADDING",   (0,0), (-1,-1), 5),
        ("BOTTOMPADDING",(0,0), (-1,-1), 3),
        ("LEFTPADDING",  (0,0), (-1,-1), 10),
        ("RIGHTPADDING", (0,0), (-1,-1), 10),
    ]))
    return t

# ── Helper: Two-column key-value table ────────────────────────────────────────
def kv_table(rows_data, col_w=(6.5*cm, 10*cm)):
    header = rows_data[0]
    body   = rows_data[1:]
    table_data = [[Paragraph(header[0], sTableHead), Paragraph(header[1], sTableHead)]]
    for r in body:
        table_data.append([Paragraph(r[0], sTableCell), Paragraph(r[1], sTableCell)])
    t = Table(table_data, colWidths=list(col_w))
    style = [
        ("BACKGROUND",    (0,0), (-1,0),  NAVY),
        ("BACKGROUND",    (0,1), (-1,-1), LIGHT_GREY),
        ("ROWBACKGROUNDS",(0,1), (-1,-1), [WHITE, LIGHT_GREY]),
        ("BOX",           (0,0), (-1,-1), 0.8, MID_GREY),
        ("INNERGRID",     (0,0), (-1,-1), 0.4, MID_GREY),
        ("TOPPADDING",    (0,0), (-1,-1), 5),
        ("BOTTOMPADDING", (0,0), (-1,-1), 5),
        ("LEFTPADDING",   (0,0), (-1,-1), 8),
        ("RIGHTPADDING",  (0,0), (-1,-1), 8),
        ("VALIGN",        (0,0), (-1,-1), "TOP"),
    ]
    t.setStyle(TableStyle(style))
    return t

# ── Helper: Amber alert/tip box ───────────────────────────────────────────────
def tip_box(label, text):
    rows = [
        [Paragraph(f"<b>{label}</b>", S("tmp", fontSize=9.5, textColor=AMBER, fontName="Helvetica-Bold"))],
        [Paragraph(text, sBox)],
    ]
    t = Table(rows, colWidths=[W - 4*cm])
    t.setStyle(TableStyle([
        ("BACKGROUND",   (0,0), (-1,-1), LIGHT_AMBER),
        ("BOX",          (0,0), (-1,-1), 1.5, AMBER),
        ("LEFTPADDING",  (0,0), (-1,-1), 10),
        ("RIGHTPADDING", (0,0), (-1,-1), 10),
        ("TOPPADDING",   (0,0), (-1,-1), 4),
        ("BOTTOMPADDING",(0,0), (-1,-1), 4),
    ]))
    return t

# ── Helper: Checklist table ────────────────────────────────────────────────────
def checklist(title, items):
    rows = [[Paragraph(f"<b>{title}</b>", sH3), Paragraph("<b>Done</b>", sTableHead)]]
    for item in items:
        rows.append([Paragraph(item, sTableCell), Paragraph("☐", sTableCell)])
    t = Table(rows, colWidths=[W - 5.5*cm, 1.5*cm])
    t.setStyle(TableStyle([
        ("BACKGROUND",   (0,0), (-1,0),  TEAL),
        ("TEXTCOLOR",    (0,0), (-1,0),  WHITE),
        ("ROWBACKGROUNDS",(0,1), (-1,-1), [WHITE, LIGHT_GREY]),
        ("BOX",          (0,0), (-1,-1), 0.8, MID_GREY),
        ("INNERGRID",    (0,0), (-1,-1), 0.4, MID_GREY),
        ("TOPPADDING",   (0,0), (-1,-1), 5),
        ("BOTTOMPADDING",(0,0), (-1,-1), 5),
        ("LEFTPADDING",  (0,0), (-1,-1), 8),
        ("ALIGN",        (1,0), (1,-1),  "CENTER"),
    ]))
    return t

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

# ── COVER PAGE ─────────────────────────────────────────────────────────────────
cover_data = [[Paragraph("Clinical History Taking", sTitle)]]
cover_t = Table(cover_data, colWidths=[W - 4*cm])
cover_t.setStyle(TableStyle([
    ("BACKGROUND",    (0,0), (-1,-1), NAVY),
    ("ROUNDEDCORNERS",[8]),
    ("TOPPADDING",    (0,0), (-1,-1), 24),
    ("BOTTOMPADDING", (0,0), (-1,-1), 8),
    ("LEFTPADDING",   (0,0), (-1,-1), 10),
]))
story.append(sp(1.5))
story.append(cover_t)

sub_data = [[Paragraph("Assessment · Patient Communication · Documentation of Findings", sSubtitle)]]
sub_t = Table(sub_data, colWidths=[W - 4*cm])
sub_t.setStyle(TableStyle([
    ("BACKGROUND",   (0,0), (-1,-1), TEAL),
    ("TOPPADDING",   (0,0), (-1,-1), 8),
    ("BOTTOMPADDING",(0,0), (-1,-1), 10),
]))
story.append(sub_t)
story.append(sp(0.4))
story.append(Paragraph("Medical Student Study Guide", sMeta))
story.append(Paragraph(f"Prepared July 2026", sMeta))
story.append(sp(1))
story.append(hr())

# Intro paragraph
story.append(sp(0.3))
story.append(Paragraph(
    "Clinical history taking is the systematic process of gathering information from a patient to understand their health problem. "
    "It is one of the most fundamental skills in clinical medicine. A well-taken history directs the physical examination, "
    "guides investigations, and underpins clinical reasoning. This guide covers the structured approach to history taking, "
    "communication techniques, assessment frameworks, and documentation standards.",
    sBody))
story.append(sp(0.5))

# ── TABLE OF CONTENTS ─────────────────────────────────────────────────────────
story.append(Paragraph("Contents", sH2))
toc_items = [
    ("1", "Structure of a Clinical History"),
    ("2", "Presenting Complaint and History of Presenting Complaint"),
    ("3", "Systematic Components of the History"),
    ("4", "Patient Communication Skills"),
    ("5", "History-Taking Assessment Frameworks"),
    ("6", "Clinical Assessment – Worked Example"),
    ("7", "Documentation of Findings"),
    ("8", "Common Pitfalls and Quality Checklist"),
]
toc_data = [["#", "Section"]]
for num, title in toc_items:
    toc_data.append([num, title])
toc_t = Table(toc_data, colWidths=[1*cm, W - 5*cm])
toc_t.setStyle(TableStyle([
    ("BACKGROUND",    (0,0), (-1,0),  NAVY),
    ("TEXTCOLOR",     (0,0), (-1,0),  WHITE),
    ("FONTNAME",      (0,0), (-1,0),  "Helvetica-Bold"),
    ("FONTSIZE",      (0,0), (-1,-1), 9.5),
    ("ROWBACKGROUNDS",(0,1), (-1,-1), [WHITE, LIGHT_GREY]),
    ("BOX",           (0,0), (-1,-1), 0.8, MID_GREY),
    ("INNERGRID",     (0,0), (-1,-1), 0.4, MID_GREY),
    ("TOPPADDING",    (0,0), (-1,-1), 5),
    ("BOTTOMPADDING", (0,0), (-1,-1), 5),
    ("LEFTPADDING",   (0,0), (-1,-1), 8),
    ("ALIGN",         (0,0), (0,-1),  "CENTER"),
]))
story.append(toc_t)
story.append(PageBreak())

# =============================================================================
# SECTION 1 – STRUCTURE OF A CLINICAL HISTORY
# =============================================================================
story.append(section_header("Structure of a Clinical History", "1"))
story.append(sp(0.4))
story.append(Paragraph(
    "A complete clinical history follows a standard structure that ensures no important information is omitted. "
    "While the order may vary with the clinical context, the components below are universally expected.",
    sBody))
story.append(sp(0.3))

struct_data = [
    ["Component", "Description / Key Information Sought"],
    ["Demographic Details", "Patient name, age, sex, occupation, marital status, referring source"],
    ["Presenting Complaint (PC)", "Chief symptom(s) in the patient's own words; duration in brief"],
    ["History of Presenting\nComplaint (HPC)", "Onset, site, character, radiation, timing, severity, exacerbating/relieving factors, associated symptoms"],
    ["Past Medical History\n(PMH)", "Previous illnesses, hospitalizations, surgeries, known diagnoses (DM, HTN, asthma, etc.)"],
    ["Drug History (DH)", "Current medications (name, dose, frequency), OTC drugs, herbal/supplements, allergies with reaction type"],
    ["Family History (FH)", "First-degree relatives' health status; heritable conditions, early cardiac disease, cancers"],
    ["Social History (SH)", "Smoking (pack-years), alcohol (units/week), recreational drugs, occupation, housing, travel, diet, exercise"],
    ["Systems Review (SR)", "Structured screening questions across all body systems to detect symptoms not yet disclosed"],
]
story.append(kv_table(struct_data, col_w=(5*cm, 11.5*cm)))
story.append(sp(0.5))

story.append(tip_box("OSCE Tip",
    "In an OSCE station, always introduce yourself, confirm the patient's name and date of birth, "
    "and obtain consent to take a history. These three steps alone earn marks and set the professional tone."))
story.append(PageBreak())

# =============================================================================
# SECTION 2 – PRESENTING COMPLAINT AND HPC
# =============================================================================
story.append(section_header("Presenting Complaint and History of Presenting Complaint", "2"))
story.append(sp(0.4))

story.append(Paragraph("2.1  Presenting Complaint (PC)", sH2))
story.append(Paragraph(
    "The presenting complaint is the primary reason the patient is seeking medical attention, recorded in their own words. "
    "Avoid medical jargon when documenting it. Write exactly what the patient said, e.g., <i>'chest tightness for 2 days'</i> "
    "rather than <i>'angina.'</i>", sBody))
story.append(sp(0.3))

story.append(Paragraph("2.2  History of Presenting Complaint – The SOCRATES Framework", sH2))
story.append(Paragraph(
    "SOCRATES is the gold-standard mnemonic for exploring any symptom, particularly pain. Each element should be "
    "addressed for every significant symptom the patient reports.", sBody))
story.append(sp(0.2))

socrates_data = [
    ["Letter", "Stands For", "Sample Questions"],
    ["S", "Site", "Where exactly is it? Can you point to it?"],
    ["O", "Onset", "When did it start? Was it sudden or gradual?"],
    ["C", "Character", "What does it feel like? Sharp, dull, burning, crushing?"],
    ["R", "Radiation", "Does it spread anywhere? To the arm, jaw, back?"],
    ["A", "Associated\nSymptoms", "Anything else at the same time? Nausea, sweating, breathlessness?"],
    ["T", "Timing", "Is it constant or comes and goes? How long does each episode last?"],
    ["E", "Exacerbating /\nRelieving Factors", "What makes it worse or better? Exercise, food, rest, medication?"],
    ["S", "Severity", "On a scale of 0-10, how bad is it now? At worst?"],
]
t_soc = Table(
    [[Paragraph(r[0], sTableHead), Paragraph(r[1], sTableHead), Paragraph(r[2], sTableHead)]] +
    [[Paragraph(row[0], S("c", fontSize=11, fontName="Helvetica-Bold", textColor=TEAL, alignment=TA_CENTER)),
      Paragraph(row[1], sTableCell),
      Paragraph(row[2], sTableCell)] for row in socrates_data[1:]],
    colWidths=[1*cm, 3.5*cm, 12*cm]
)
t_soc.setStyle(TableStyle([
    ("BACKGROUND",    (0,0), (-1,0),  NAVY),
    ("ROWBACKGROUNDS",(0,1), (-1,-1), [WHITE, LIGHT_TEAL]),
    ("BOX",           (0,0), (-1,-1), 0.8, MID_GREY),
    ("INNERGRID",     (0,0), (-1,-1), 0.4, MID_GREY),
    ("TOPPADDING",    (0,0), (-1,-1), 5),
    ("BOTTOMPADDING", (0,0), (-1,-1), 5),
    ("LEFTPADDING",   (0,0), (-1,-1), 8),
    ("VALIGN",        (0,0), (-1,-1), "TOP"),
    ("ALIGN",         (0,1), (0,-1),  "CENTER"),
]))
story.append(t_soc)
story.append(sp(0.4))

story.append(info_box("Additional HPC Exploration", [
    "Ask about functional impact: 'How has this affected your daily life or work?'",
    "Timeline: construct a chronological narrative of the symptom's evolution",
    "Previous episodes: 'Have you had anything like this before? How was it managed?'",
    "Patient's own ideas: 'What do you think might be causing this?' (ICE framework – see Section 4)",
    "Red flag symptoms: always screen specifically for relevant warning signs"
]))
story.append(PageBreak())

# =============================================================================
# SECTION 3 – SYSTEMATIC COMPONENTS
# =============================================================================
story.append(section_header("Systematic Components of the History", "3"))
story.append(sp(0.4))

story.append(Paragraph("3.1  Past Medical History", sH2))
story.append(Paragraph(
    "Use the mnemonic <b>MJ THREADS</b> to avoid missing common chronic conditions:", sBody))
story.append(sp(0.1))
threads = [
    "M – Myocardial infarction / cardiac disease",
    "J – Jaundice / liver disease",
    "T – Tuberculosis",
    "H – Hypertension",
    "R – Rheumatic fever / rheumatic heart disease",
    "E – Epilepsy",
    "A – Asthma / respiratory disease",
    "D – Diabetes mellitus",
    "S – Stroke / neurological disease",
]
for item in threads:
    story.append(Paragraph(f"• {item}", sBullet))
story.append(sp(0.3))
story.append(Paragraph(
    "Also ask about: childhood illnesses, immunizations, previous surgeries and dates, blood transfusions, "
    "significant injuries, psychiatric history, and obstetric/gynaecological history where relevant.", sBody))
story.append(sp(0.3))

story.append(Paragraph("3.2  Drug History", sH2))
dh_items = [
    "Name, dose, route, and frequency of each current medication",
    "How long they have been taking it and whether they are compliant",
    "Over-the-counter (OTC) medications and herbal/alternative remedies",
    "Contraceptive pills or hormone therapy (often omitted by patients)",
    "Allergies: name of substance, type of reaction (e.g., rash vs. anaphylaxis), and date",
]
story.append(info_box("Drug History Essentials", dh_items))
story.append(sp(0.3))

story.append(Paragraph("3.3  Family History", sH2))
story.append(Paragraph(
    "Ask about the health of parents, siblings, and children. If deceased, note age and cause of death. "
    "Specifically enquire about conditions with a genetic component: ischaemic heart disease, hypertension, "
    "diabetes, cancers (colon, breast, ovarian), hyperlipidaemia, and mental health conditions.", sBody))
story.append(sp(0.3))

story.append(Paragraph("3.4  Social History", sH2))
social_data = [
    ["Domain", "What to Ask"],
    ["Smoking", "Current/ex/never; pack-years = (packs/day) x (years smoked); type (cigarettes, cigars, pipe, e-cigarette)"],
    ["Alcohol", "Units per week; CAGE questionnaire if dependence suspected; binge-drinking patterns"],
    ["Recreational Drugs", "IV drug use (risk of BBV, endocarditis), cocaine, cannabis, others; non-judgemental framing"],
    ["Occupation", "Current and past jobs; occupational exposures (dust, chemicals, radiation, shift work)"],
    ["Housing", "Type, who lives with them, stairs, adaptations, support workers"],
    ["Functional Status", "Activities of daily living (ADLs): washing, dressing, cooking, mobility, continence"],
    ["Travel History", "Recent international travel; endemic disease exposure, vaccinations"],
    ["Diet & Exercise", "Relevant to metabolic, cardiovascular, and GI conditions"],
]
story.append(kv_table(social_data, col_w=(4.5*cm, 12*cm)))
story.append(sp(0.3))

story.append(Paragraph("3.5  Systems Review", sH2))
story.append(Paragraph(
    "A brief screening review of all major organ systems identifies problems the patient may not have volunteered. "
    "This is typically performed at the end of the history. Ask 1-2 targeted questions per system:", sBody))
story.append(sp(0.2))

sr_data = [
    ["System", "Key Screening Questions"],
    ["Cardiovascular", "Chest pain, palpitations, dyspnoea, ankle swelling, orthopnoea, PND"],
    ["Respiratory", "Cough, breathlessness, wheeze, haemoptysis, sputum production"],
    ["Gastrointestinal", "Appetite, weight change, nausea/vomiting, dysphagia, bowel habit, rectal bleeding"],
    ["Genitourinary", "Dysuria, frequency, haematuria, nocturia; menstrual/sexual history where relevant"],
    ["Neurological", "Headache, visual changes, dizziness, weakness, sensory disturbance, seizures, memory"],
    ["Musculoskeletal", "Joint pain/swelling/stiffness, back pain, muscle weakness or cramps"],
    ["Endocrine", "Heat/cold intolerance, weight change, thirst/polyuria, skin/hair changes"],
    ["Psychiatric", "Mood, sleep, appetite, concentration, anxiety, self-harm thoughts"],
    ["Dermatological", "Rashes, lesions, pruritus, colour changes"],
]
story.append(kv_table(sr_data, col_w=(4*cm, 12.5*cm)))
story.append(PageBreak())

# =============================================================================
# SECTION 4 – PATIENT COMMUNICATION
# =============================================================================
story.append(section_header("Patient Communication Skills", "4"))
story.append(sp(0.4))

story.append(Paragraph("4.1  Core Communication Principles", sH2))
comm_items = [
    ("Introduction", "State your name, role, and purpose. Confirm patient identity (name + DOB)."),
    ("Open Questions First", "Start with 'Tell me about what's been troubling you.' Allow 1-2 minutes uninterrupted."),
    ("Active Listening", "Eye contact, nodding, minimal encouragers ('I see', 'go on'), avoid interrupting."),
    ("Clarifying", "Paraphrase to confirm: 'So if I've understood correctly, the pain started suddenly yesterday?'"),
    ("Empathy", "Acknowledge emotion: 'That sounds very worrying. How have you been coping?'"),
    ("Signposting", "Warn before topic changes: 'I'd like to move on and ask about your past health history now.'"),
    ("Closed Questions", "Use only to clarify specific details after open exploration is complete."),
    ("Avoiding Jargon", "Use lay language: say 'heart attack' not 'myocardial infarction' when speaking to the patient."),
    ("Summarising", "Briefly recap key points and invite correction: 'Have I missed anything important?'"),
    ("Closing", "Explain next steps clearly. Ask if the patient has any questions."),
]
comm_table_data = [["Skill", "Application"]] + [[Paragraph(k, sTableCell), Paragraph(v, sTableCell)] for k, v in comm_items]
comm_t = Table(
    [[Paragraph("Skill", sTableHead), Paragraph("Application", sTableHead)]] +
    [[Paragraph(k, sTableCell), Paragraph(v, sTableCell)] for k, v in comm_items],
    colWidths=[4*cm, 12.5*cm]
)
comm_t.setStyle(TableStyle([
    ("BACKGROUND",    (0,0), (-1,0),  NAVY),
    ("ROWBACKGROUNDS",(0,1), (-1,-1), [WHITE, LIGHT_GREY]),
    ("BOX",           (0,0), (-1,-1), 0.8, MID_GREY),
    ("INNERGRID",     (0,0), (-1,-1), 0.4, MID_GREY),
    ("TOPPADDING",    (0,0), (-1,-1), 5),
    ("BOTTOMPADDING", (0,0), (-1,-1), 5),
    ("LEFTPADDING",   (0,0), (-1,-1), 8),
    ("VALIGN",        (0,0), (-1,-1), "TOP"),
]))
story.append(comm_t)
story.append(sp(0.4))

story.append(Paragraph("4.2  The ICE Framework", sH2))
story.append(Paragraph(
    "ICE stands for <b>Ideas, Concerns, and Expectations</b>. Exploring these three domains is central "
    "to patient-centred care and is specifically assessed in OSCEs.", sBody))
story.append(sp(0.2))
ice_data = [
    ["ICE Element", "Purpose", "Example Phrasing"],
    ["Ideas", "Understand what the patient thinks is wrong", "'What do you think might be causing this?'"],
    ["Concerns", "Identify their specific worries or fears", "'Is there anything in particular you're worried it might be?'"],
    ["Expectations", "Clarify what they hope will happen today", "'What were you hoping we might do for you today?'"],
]
ice_t = Table(
    [[Paragraph(h, sTableHead) for h in ice_data[0]]] +
    [[Paragraph(cell, sTableCell) for cell in row] for row in ice_data[1:]],
    colWidths=[3*cm, 5.5*cm, 8*cm]
)
ice_t.setStyle(TableStyle([
    ("BACKGROUND",    (0,0), (-1,0),  TEAL),
    ("ROWBACKGROUNDS",(0,1), (-1,-1), [LIGHT_TEAL, WHITE]),
    ("BOX",           (0,0), (-1,-1), 0.8, TEAL),
    ("INNERGRID",     (0,0), (-1,-1), 0.4, MID_GREY),
    ("TOPPADDING",    (0,0), (-1,-1), 6),
    ("BOTTOMPADDING", (0,0), (-1,-1), 6),
    ("LEFTPADDING",   (0,0), (-1,-1), 8),
    ("VALIGN",        (0,0), (-1,-1), "TOP"),
]))
story.append(ice_t)
story.append(sp(0.4))

story.append(Paragraph("4.3  Challenging Communication Scenarios", sH2))
challenges = [
    ("<b>Angry patient:</b> Acknowledge anger first ('I can see you're frustrated and I'm sorry about that.'), "
     "don't become defensive, explore the cause of the anger, and stay calm."),
    ("<b>Quiet/withdrawn patient:</b> Use open questions, allow silence, consider asking gently if they "
     "are comfortable continuing. Check for anxiety or low mood."),
    ("<b>Distressed or crying patient:</b> Pause, offer tissues, acknowledge distress non-verbally and verbally. "
     "Do not rush to fill silence. Proceed only when the patient is ready."),
    ("<b>Collateral history:</b> Obtained from relatives, carers, or GP when the patient cannot give a full history "
     "(e.g., dementia, unconscious, children). Always note the source."),
    ("<b>Interpreter needed:</b> Use a professional medical interpreter, not a family member where possible. "
     "Speak to the patient, not the interpreter. Use short sentences."),
]
for c in challenges:
    story.append(Paragraph(f"• {c}", sBullet))
story.append(PageBreak())

# =============================================================================
# SECTION 5 – ASSESSMENT FRAMEWORKS
# =============================================================================
story.append(section_header("History-Taking Assessment Frameworks", "5"))
story.append(sp(0.4))

story.append(Paragraph("5.1  OSCE Assessment Domains", sH2))
story.append(Paragraph(
    "In Objective Structured Clinical Examinations (OSCEs), history-taking stations are marked across "
    "several domains. Understanding these helps you prioritise where marks are gained and lost.", sBody))
story.append(sp(0.2))

osce_data = [
    ["Domain", "Marks Focus", "Common Errors That Lose Marks"],
    ["Introduction & Rapport", "Intro, consent, body language", "Jumping straight to questions without introducing self"],
    ["Content – PC & HPC", "SOCRATES, red flags, timeline", "Missing associated symptoms; not asking about red flags"],
    ["Content – Background\nHistory", "PMH, DH, FH, SH", "Forgetting to ask about allergies or drug compliance"],
    ["Systems Review", "Targeted screen per case", "Skipping entirely or asking irrelevant systems only"],
    ["Communication\nTechnique", "Open Q's, ICE, empathy", "Only using closed questions; not acknowledging concerns"],
    ["Professionalism", "Non-judgemental, accurate", "Appearing rushed, dismissive, or using jargon"],
    ["Summarising &\nClosing", "Accurate recap, next steps", "Abrupt closure with no summary or explanation"],
]
osce_t = Table(
    [[Paragraph(h, sTableHead) for h in osce_data[0]]] +
    [[Paragraph(cell, sTableCell) for cell in row] for row in osce_data[1:]],
    colWidths=[3.5*cm, 4.5*cm, 8.5*cm]
)
osce_t.setStyle(TableStyle([
    ("BACKGROUND",    (0,0), (-1,0),  NAVY),
    ("ROWBACKGROUNDS",(0,1), (-1,-1), [WHITE, LIGHT_GREY]),
    ("BOX",           (0,0), (-1,-1), 0.8, MID_GREY),
    ("INNERGRID",     (0,0), (-1,-1), 0.4, MID_GREY),
    ("TOPPADDING",    (0,0), (-1,-1), 5),
    ("BOTTOMPADDING", (0,0), (-1,-1), 5),
    ("LEFTPADDING",   (0,0), (-1,-1), 8),
    ("VALIGN",        (0,0), (-1,-1), "TOP"),
]))
story.append(osce_t)
story.append(sp(0.4))

story.append(Paragraph("5.2  Calgary-Cambridge Model", sH2))
story.append(Paragraph(
    "The Calgary-Cambridge communication model provides a framework used in many medical schools to teach and assess "
    "clinical consultations. It describes five sequential tasks:", sBody))
story.append(sp(0.2))
cc_steps = [
    ("1. Initiating the Session", "Establish initial rapport, identify the reason for the consultation, set an agenda"),
    ("2. Gathering Information", "Explore the problem (disease framework + illness framework), understand the patient's perspective"),
    ("3. Physical Examination", "Perform targeted examination after history; explain to patient throughout"),
    ("4. Explanation & Planning", "Provide correct amount of information, aid recall and understanding, achieve shared understanding"),
    ("5. Closing the Session", "Forward planning, ensure appropriate point of closure, safety netting"),
]
cc_t = Table(
    [[Paragraph("Step", sTableHead), Paragraph("Key Tasks", sTableHead)]] +
    [[Paragraph(s[0], sTableCell), Paragraph(s[1], sTableCell)] for s in cc_steps],
    colWidths=[5*cm, 11.5*cm]
)
cc_t.setStyle(TableStyle([
    ("BACKGROUND",    (0,0), (-1,0),  TEAL),
    ("ROWBACKGROUNDS",(0,1), (-1,-1), [LIGHT_TEAL, WHITE]),
    ("BOX",           (0,0), (-1,-1), 0.8, MID_GREY),
    ("INNERGRID",     (0,0), (-1,-1), 0.4, MID_GREY),
    ("TOPPADDING",    (0,0), (-1,-1), 5),
    ("BOTTOMPADDING", (0,0), (-1,-1), 5),
    ("LEFTPADDING",   (0,0), (-1,-1), 8),
    ("VALIGN",        (0,0), (-1,-1), "TOP"),
]))
story.append(cc_t)
story.append(PageBreak())

# =============================================================================
# SECTION 6 – WORKED EXAMPLE
# =============================================================================
story.append(section_header("Clinical Assessment – Worked Example", "6"))
story.append(sp(0.4))

story.append(Paragraph(
    "The following is an example of a well-documented clinical history for a patient presenting with chest pain. "
    "This serves as a model for both documentation and the structure you should follow in real or simulated consultations.",
    sBody))
story.append(sp(0.3))

example_sections = [
    ("Demographics", "Mr. John Smith, 58-year-old male, retired lorry driver. Referred by GP."),
    ("Presenting Complaint", "Chest tightness and breathlessness on exertion for 3 weeks."),
    ("HPC",
     "Mr. Smith describes a central, tight sensation in his chest, 6/10 severity at worst, radiating to the left arm. "
     "Onset is gradual, occurring on moderate exertion (walking up stairs or hills). Relieved by rest within 5 minutes. "
     "No relief with antacids. Associated with mild breathlessness and diaphoresis during episodes. "
     "Denies rest pain, syncope, palpitations, or cough. No previous similar episodes. "
     "He is concerned this might be 'heart-related' as his father had a heart attack at 62."),
    ("Past Medical History",
     "Hypertension (diagnosed 2015). Hypercholesterolaemia (diagnosed 2018). Appendicectomy 1993. No known diabetes, asthma, or previous cardiac events."),
    ("Drug History",
     "Amlodipine 5 mg OD. Atorvastatin 40 mg ON. No OTC medications. NKDA (No Known Drug Allergies)."),
    ("Family History",
     "Father: MI aged 62, died 70 (CVA). Mother: Type 2 DM, alive. Brother: Hypertension."),
    ("Social History",
     "Ex-smoker – quit 2018 (15 pack-year history). Alcohol: 12 units/week (beer, 3-4 evenings). No recreational drugs. "
     "Retired; previously drove HGVs. Lives with wife in 2-storey house. Independent in all ADLs. No recent travel."),
    ("Systems Review",
     "CVS: as above. Resp: mild exertional dyspnoea only. GI: no change in appetite, no weight loss, normal bowels. "
     "GU: nocturia x1. Neuro: no headache, no focal deficit. MSK: mild knee OA. No other significant symptoms."),
]

for heading, content in example_sections:
    ex_row = [[Paragraph(heading, sBoxBold)], [Paragraph(content, sBox)]]
    ex_t = Table(ex_row, colWidths=[W - 4*cm])
    ex_t.setStyle(TableStyle([
        ("BACKGROUND",   (0,0), (-1,0), LIGHT_AMBER),
        ("BACKGROUND",   (0,1), (-1,1), WHITE),
        ("BOX",          (0,0), (-1,-1), 0.8, AMBER),
        ("TOPPADDING",   (0,0), (-1,-1), 5),
        ("BOTTOMPADDING",(0,0), (-1,-1), 5),
        ("LEFTPADDING",  (0,0), (-1,-1), 10),
    ]))
    story.append(ex_t)
    story.append(sp(0.2))

story.append(sp(0.3))
story.append(tip_box("Clinical Impression",
    "Based on this history the most likely diagnosis is stable angina pectoris. "
    "Key risk factors include male sex, age, hypertension, hypercholesterolaemia, and a significant smoking history. "
    "Red flags for ACS (rest pain, prolonged pain >20 min, haemodynamic compromise) are absent. "
    "Initial investigation plan: resting ECG, fasting lipids, HbA1c, FBC, U&E, exercise tolerance test or CT coronary angiogram."))
story.append(PageBreak())

# =============================================================================
# SECTION 7 – DOCUMENTATION
# =============================================================================
story.append(section_header("Documentation of Findings", "7"))
story.append(sp(0.4))

story.append(Paragraph("7.1  Principles of Clinical Documentation", sH2))
doc_principles = [
    "Write clearly and legibly (for handwritten notes). Use black ink.",
    "Date, time, and sign every entry. Print your name and role beneath your signature.",
    "Use objective language – document what the patient said and what you observed, not your interpretation alone.",
    "Record negative findings that are clinically significant (e.g., 'No chest pain at rest').",
    "Be accurate and complete – incomplete notes are a medico-legal risk.",
    "Avoid abbreviations unless universally accepted in your institution.",
    "Correct errors by drawing a single line through the error, writing 'error', signing, and dating – never use correction fluid.",
    "Maintain patient confidentiality – do not include unnecessary personal details.",
]
for p in doc_principles:
    story.append(Paragraph(f"• {p}", sBullet))
story.append(sp(0.3))

story.append(Paragraph("7.2  Standard Documentation Format (SOAP / Clerking)", sH2))
story.append(Paragraph(
    "Most clinical documentation follows either the <b>SOAP</b> format or the structured <b>Clerking Note</b>.", sBody))
story.append(sp(0.2))

soap_data = [
    ["Format", "Component", "Content"],
    ["S – Subjective", "History", "Patient's story: PC, HPC, PMH, DH, FH, SH, SR in patient's own words"],
    ["O – Objective", "Examination\n& Investigations", "Vital signs, physical examination findings, investigations (ordered/results)"],
    ["A – Assessment", "Diagnosis /\nDifferential", "Working diagnosis and differential diagnoses with brief justification"],
    ["P – Plan", "Management", "Investigations ordered, treatments initiated, referrals, safety netting, follow-up"],
]
soap_t = Table(
    [[Paragraph(h, sTableHead) for h in soap_data[0]]] +
    [[Paragraph(cell, sTableCell) for cell in row] for row in soap_data[1:]],
    colWidths=[3.5*cm, 3.5*cm, 9.5*cm]
)
soap_t.setStyle(TableStyle([
    ("BACKGROUND",    (0,0), (-1,0),  NAVY),
    ("ROWBACKGROUNDS",(0,1), (-1,-1), [LIGHT_TEAL, WHITE, LIGHT_TEAL, WHITE]),
    ("BOX",           (0,0), (-1,-1), 0.8, MID_GREY),
    ("INNERGRID",     (0,0), (-1,-1), 0.4, MID_GREY),
    ("TOPPADDING",    (0,0), (-1,-1), 5),
    ("BOTTOMPADDING", (0,0), (-1,-1), 5),
    ("LEFTPADDING",   (0,0), (-1,-1), 8),
    ("VALIGN",        (0,0), (-1,-1), "TOP"),
]))
story.append(soap_t)
story.append(sp(0.4))

story.append(Paragraph("7.3  Electronic Health Records (EHR)", sH2))
ehr_items = [
    "Always log in with your own credentials – never use a colleague's login.",
    "Structured templates improve completeness but avoid copy-pasting previous entries without review.",
    "Flag allergy alerts and ensure the medication list is up to date at every encounter.",
    "Use free-text fields for narrative history; use structured fields for coded diagnoses and vitals.",
    "Document who was present (e.g., interpreter, relative) and whether consent was obtained.",
]
story.append(info_box("EHR Best Practice", ehr_items))
story.append(sp(0.3))

story.append(Paragraph("7.4  Referral Letter / Handover", sH2))
story.append(Paragraph(
    "When referring a patient or handing over care, a good written summary should include:", sBody))
referral_items = [
    "Patient identifier (name, DOB, hospital number)",
    "Reason for referral / handover in one clear sentence",
    "Brief relevant history and key findings",
    "Investigations already performed and results",
    "Current medications and allergies",
    "Your working diagnosis and the specific question you need answered",
    "Urgency and proposed timeframe",
    "Your name, contact details, and bleep/phone number",
]
for item in referral_items:
    story.append(Paragraph(f"• {item}", sBullet))
story.append(PageBreak())

# =============================================================================
# SECTION 8 – CHECKLIST & PITFALLS
# =============================================================================
story.append(section_header("Common Pitfalls and Quality Checklist", "8"))
story.append(sp(0.4))

story.append(Paragraph("8.1  Common History-Taking Errors", sH2))
pitfalls = [
    "Starting with closed questions before allowing the patient to open freely",
    "Interrupting the patient within the first 30 seconds (average time before interruption in studies: 18 seconds)",
    "Failing to explore ICE – leaving concerns unaddressed",
    "Omitting a drug history or allergies (a patient safety risk)",
    "Forgetting to ask about smoking, alcohol, and occupation",
    "Not performing a systems review, missing unrelated but significant pathology",
    "Using medical jargon the patient does not understand",
    "Appearing disinterested – poor eye contact, typing throughout the consultation",
    "Failing to summarise and invite correction at the end of the history",
    "Inadequate documentation: missing time, date, or signature on notes",
]
for p in pitfalls:
    story.append(Paragraph(f"⚠  {p}", sWarn))
story.append(sp(0.3))

story.append(Paragraph("8.2  Pre-OSCE / Pre-Clinic Checklist", sH2))
cl_items = [
    "Introduce myself, confirm patient identity, state purpose",
    "Open question to establish the presenting complaint",
    "Full SOCRATES for the main symptom",
    "Associated symptoms screened",
    "Red flags for the relevant system addressed",
    "Past medical history using MJ THREADS",
    "Complete drug history including OTC, herbal, and allergies (with reaction type)",
    "Family history with first-degree relatives covered",
    "Social history: smoking, alcohol, drugs, occupation, housing, ADLs",
    "ICE explored (Ideas, Concerns, Expectations)",
    "Systems review completed",
    "Summarised and invited correction",
    "Explained next steps and checked for patient questions",
    "Documentation: date, time, name, role, signature, legible and complete",
]
story.append(checklist("History Taking Checklist", cl_items))
story.append(sp(0.4))

story.append(tip_box("Key Principle",
    "The best history takers listen more than they speak. Give the patient space to tell their story first. "
    "Your job is to structure and clarify it, not to lead it. A thorough history frequently reveals the "
    "diagnosis before you even examine the patient."))
story.append(sp(0.5))

# ── FOOTER NOTE ───────────────────────────────────────────────────────────────
story.append(hr())
story.append(Paragraph(
    "This guide is intended as a structured study reference for medical students. Clinical practice should "
    "always follow local guidelines, senior supervision, and institutional protocols.",
    sNote))
story.append(Paragraph(
    f"Prepared by Orris Medical Education | July 2026",
    sMeta))

# =============================================================================
# BUILD PDF
# =============================================================================

def add_page_number(canvas, doc):
    canvas.saveState()
    canvas.setFont("Helvetica", 8)
    canvas.setFillColor(MID_GREY)
    page_num = canvas.getPageNumber()
    canvas.drawRightString(W - 2*cm, 1.2*cm, f"Page {page_num}")
    canvas.drawString(2*cm, 1.2*cm, "Clinical History Taking | Medical Student Study Guide")
    canvas.restoreState()

doc.build(story, onFirstPage=add_page_number, onLaterPages=add_page_number)
print(f"PDF created: {OUTPUT_PATH}")
Running Command

cd /tmp/workspace/clinical-history-guide && python generate_pdf.py 2>&1

Editing File

~/clinical-history-guide/generate_pdf.py

Successfully replaced 1 instance(s) of the string in '/tmp/workspace/clinical-history-guide/generate_pdf.py'
Running Command

cd /tmp/workspace/clinical-history-guide && python generate_pdf.py 2>&1

Running Command

ls -lh /tmp/workspace/clinical-history-guide/Clinical_History_Taking_Guide.pdf

Your PDF is ready! Here is your download link:

Clinical History Taking Guide

PDF Document · PDF


What's inside the guide (10+ pages, A4, colour-formatted):
SectionContent
1. Structure of a Clinical HistoryFull table of all 8 components: demographics, PC, HPC, PMH, DH, FH, SH, systems review
2. PC and HPCSOCRATES framework in full colour table with example questions
3. Systematic ComponentsMJ THREADS mnemonic, drug history checklist, family/social history domains, full systems review table
4. Patient Communication10 core communication skills, ICE framework (Ideas, Concerns, Expectations), challenging scenarios
5. Assessment FrameworksOSCE marking domains with common errors, Calgary-Cambridge model
6. Worked ExampleComplete model history (chest pain case) with clinical impression and investigation plan
7. DocumentationSOAP format, medico-legal documentation principles, EHR best practice, referral letter structure
8. Checklist & Pitfalls10 common errors, 14-item pre-OSCE/pre-clinic checklist
The guide uses a professional Navy/Teal colour scheme with highlighted tip boxes, colour-coded tables, and page numbers throughout. It is designed to be printable as well as viewed on screen.

Clinical history taking assessment,test,patient communication and documentation of finding made a ppt with more details

Reading File
Reading File
Writing File

~/clinical-history-guide/generate_pptx.py

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.oxml.ns import qn
from pptx.util import Inches, Pt
import copy
from lxml import etree

OUTPUT = "/tmp/workspace/clinical-history-guide/Clinical_History_Taking.pptx"

# ── Palette ────────────────────────────────────────────────────────────────────
NAVY        = RGBColor(0x1A, 0x3A, 0x5C)
TEAL        = RGBColor(0x0D, 0x73, 0x77)
TEAL_LIGHT  = RGBColor(0xD0, 0xEE, 0xF0)
AMBER       = RGBColor(0xE8, 0xA8, 0x38)
AMBER_LIGHT = RGBColor(0xFE, 0xF5, 0xDC)
WHITE       = RGBColor(0xFF, 0xFF, 0xFF)
LIGHT_GREY  = RGBColor(0xF4, 0xF6, 0xF9)
MID_GREY    = RGBColor(0xB0, 0xB8, 0xC4)
DARK_TEXT   = RGBColor(0x1C, 0x25, 0x33)
RED         = RGBColor(0xC0, 0x39, 0x2B)
GREEN       = RGBColor(0x1A, 0x7A, 0x4A)
PURPLE      = RGBColor(0x5B, 0x2D, 0x8E)
ORANGE      = RGBColor(0xD4, 0x5A, 0x1A)

prs = Presentation()
prs.slide_width  = Inches(13.333)
prs.slide_height = Inches(7.5)
BLANK = prs.slide_layouts[6]

W = prs.slide_width
H = prs.slide_height

# ── Low-level helpers ──────────────────────────────────────────────────────────
def rgb_hex(r): return f"{r[0]:02X}{r[1]:02X}{r[2]:02X}"

def add_rect(slide, x, y, w, h, fill_rgb, alpha=None):
    shape = slide.shapes.add_shape(1, Inches(x), Inches(y), Inches(w), Inches(h))
    shape.line.fill.background()
    shape.fill.solid()
    shape.fill.fore_color.rgb = fill_rgb
    return shape

def add_tb(slide, x, y, w, h, text, size, bold=False, color=DARK_TEXT,
           align=PP_ALIGN.LEFT, italic=False, font="Calibri", wrap=True):
    tb = slide.shapes.add_textbox(Inches(x), Inches(y), Inches(w), Inches(h))
    tf = tb.text_frame
    tf.word_wrap = wrap
    tf.margin_left = tf.margin_right = tf.margin_top = tf.margin_bottom = 0
    p = tf.paragraphs[0]
    p.alignment = align
    run = p.add_run()
    run.text = text
    run.font.name = font
    run.font.size = Pt(size)
    run.font.bold = bold
    run.font.italic = italic
    run.font.color.rgb = color
    return tb

def add_para(tf, text, size, bold=False, color=DARK_TEXT,
             align=PP_ALIGN.LEFT, italic=False, space_before=0, bullet=False):
    p = tf.add_paragraph()
    p.alignment = align
    if space_before:
        p.space_before = Pt(space_before)
    if bullet:
        pPr = p._p.get_or_add_pPr()
        buChar = etree.SubElement(pPr, qn('a:buChar'))
        buChar.set('char', '•')
    run = p.add_run()
    run.text = text
    run.font.name = "Calibri"
    run.font.size = Pt(size)
    run.font.bold = bold
    run.font.italic = italic
    run.font.color.rgb = color
    return p

def no_bullet(p):
    pPr = p._p.get_or_add_pPr()
    buNone = etree.SubElement(pPr, qn('a:buNone'))

def add_slide_header(slide, title, subtitle=None):
    """Navy top bar with title and optional right-side subtitle tag"""
    add_rect(slide, 0, 0, 13.333, 1.15, NAVY)
    add_tb(slide, 0.35, 0.12, 9.5, 0.9, title, 26, bold=True,
           color=WHITE, align=PP_ALIGN.LEFT)
    if subtitle:
        add_rect(slide, 10.3, 0.2, 2.7, 0.72, TEAL)
        add_tb(slide, 10.35, 0.25, 2.6, 0.62, subtitle, 11,
               bold=True, color=WHITE, align=PP_ALIGN.CENTER)
    # Bottom accent line
    add_rect(slide, 0, 1.15, 13.333, 0.06, AMBER)

def slide_bg(slide):
    add_rect(slide, 0, 0, 13.333, 7.5, LIGHT_GREY)

def add_section_divider(slide, number, title, subtitle):
    """Full-screen section divider slide"""
    add_rect(slide, 0, 0, 13.333, 7.5, NAVY)
    add_rect(slide, 0, 0, 0.6, 7.5, TEAL)
    add_rect(slide, 0.6, 3.1, 12.733, 0.08, AMBER)
    # Large section number
    add_tb(slide, 1.2, 1.2, 3, 2.2, str(number), 110, bold=True, color=AMBER,
           align=PP_ALIGN.LEFT)
    add_tb(slide, 4.2, 2.0, 8.5, 1.3, title, 34, bold=True,
           color=WHITE, align=PP_ALIGN.LEFT)
    add_tb(slide, 4.2, 3.4, 8.5, 0.8, subtitle, 15, bold=False,
           color=TEAL_LIGHT, align=PP_ALIGN.LEFT)

def card(slide, x, y, w, h, header_text, body_lines, hdr_color=TEAL, hdr_txt_color=WHITE):
    """Rounded card with coloured header and white body"""
    add_rect(slide, x, y, w, 0.42, hdr_color)
    add_tb(slide, x+0.12, y+0.04, w-0.2, 0.36, header_text, 11,
           bold=True, color=hdr_txt_color)
    add_rect(slide, x, y+0.42, w, h-0.42, WHITE)
    tb = slide.shapes.add_textbox(Inches(x+0.12), Inches(y+0.5),
                                   Inches(w-0.24), Inches(h-0.6))
    tf = tb.text_frame
    tf.word_wrap = True
    tf.margin_left = tf.margin_right = tf.margin_top = tf.margin_bottom = 0
    first = True
    for line in body_lines:
        if first:
            p = tf.paragraphs[0]
            first = False
        else:
            p = tf.add_paragraph()
        p.alignment = PP_ALIGN.LEFT
        run = p.add_run()
        run.text = line
        run.font.name = "Calibri"
        run.font.size = Pt(10)
        run.font.color.rgb = DARK_TEXT

def table_shape(slide, x, y, w, rows_data, col_widths, header_color=NAVY):
    """Build a proper pptx table"""
    num_rows = len(rows_data)
    num_cols = len(rows_data[0])
    col_w_emu = [Inches(cw) for cw in col_widths]
    total_w = sum(col_w_emu)
    tbl = slide.shapes.add_table(num_rows, num_cols, Inches(x), Inches(y),
                                  total_w, Inches(0.4 * num_rows)).table
    # Column widths
    for ci, cw in enumerate(col_w_emu):
        tbl.columns[ci].width = cw

    for ri, row in enumerate(rows_data):
        for ci, cell_text in enumerate(row):
            cell = tbl.cell(ri, ci)
            cell.text = str(cell_text)
            tf = cell.text_frame
            tf.word_wrap = True
            p = tf.paragraphs[0]
            p.alignment = PP_ALIGN.LEFT
            run = p.runs[0] if p.runs else p.add_run()
            run.text = str(cell_text)
            run.font.name = "Calibri"
            run.font.size = Pt(10 if ri > 0 else 11)
            run.font.bold = (ri == 0)
            run.font.color.rgb = WHITE if ri == 0 else DARK_TEXT
            # Fill
            tc = cell._tc
            tcPr = tc.get_or_add_tcPr()
            solidFill = etree.SubElement(tcPr, qn('a:solidFill'))
            srgbClr  = etree.SubElement(solidFill, qn('a:srgbClr'))
            if ri == 0:
                srgbClr.set('val', f"{header_color[0]:02X}{header_color[1]:02X}{header_color[2]:02X}")
            else:
                clr = WHITE if ri % 2 == 1 else LIGHT_GREY
                srgbClr.set('val', f"{clr[0]:02X}{clr[1]:02X}{clr[2]:02X}")
    return tbl

def note_box(slide, x, y, w, h, icon, label, text, box_color=TEAL_LIGHT, border_color=TEAL):
    add_rect(slide, x, y, w, h, box_color)
    # Border via outline shape
    shape = slide.shapes.add_shape(1, Inches(x), Inches(y), Inches(w), Inches(h))
    shape.fill.background()
    shape.line.color.rgb = border_color
    shape.line.width = Pt(1.5)
    add_tb(slide, x+0.12, y+0.08, w-0.3, 0.32, f"{icon}  {label}", 11,
           bold=True, color=border_color)
    add_tb(slide, x+0.12, y+0.42, w-0.3, h-0.5, text, 9.5,
           color=DARK_TEXT, wrap=True)

# =============================================================================
# SLIDE 1 – TITLE
# =============================================================================
slide = prs.slides.add_slide(BLANK)
add_rect(slide, 0, 0, 13.333, 7.5, NAVY)
# Decorative teal bars
add_rect(slide, 0, 0, 0.8, 7.5, TEAL)
add_rect(slide, 0, 6.6, 13.333, 0.9, RGBColor(0x0A, 0x55, 0x58))
add_rect(slide, 0.8, 3.28, 12.533, 0.07, AMBER)

add_tb(slide, 1.3, 1.0, 11, 1.1,
       "Clinical History Taking", 48, bold=True, color=WHITE, align=PP_ALIGN.LEFT)
add_tb(slide, 1.3, 2.2, 11, 0.7,
       "Assessment  ·  Patient Communication  ·  Documentation of Findings",
       18, bold=False, color=TEAL_LIGHT, align=PP_ALIGN.LEFT)
add_rect(slide, 1.3, 3.05, 6, 0.06, AMBER)
add_tb(slide, 1.3, 3.3, 9, 0.55,
       "A Comprehensive Guide for Medical Students", 15,
       italic=True, color=MID_GREY, align=PP_ALIGN.LEFT)
# Tag boxes bottom
for i, (label, clr) in enumerate([
    ("History Taking", TEAL), ("OSCE Skills", AMBER), ("SOCRATES", RGBColor(0x5B,0x2D,0x8E)),
    ("ICE Framework", RGBColor(0x1A,0x7A,0x4A)), ("Documentation", ORANGE)
]):
    add_rect(slide, 1.3 + i*2.2, 4.3, 2.0, 0.42, clr)
    add_tb(slide, 1.3 + i*2.2 + 0.05, 4.35, 1.9, 0.32, label, 10,
           bold=True, color=WHITE, align=PP_ALIGN.CENTER)
add_tb(slide, 1.3, 6.65, 10, 0.5,
       "Medical Education  |  July 2026", 11, color=MID_GREY)

# =============================================================================
# SLIDE 2 – TABLE OF CONTENTS
# =============================================================================
slide = prs.slides.add_slide(BLANK)
slide_bg(slide)
add_slide_header(slide, "Table of Contents", "Overview")
add_rect(slide, 0, 1.21, 13.333, 6.29, LIGHT_GREY)

sections = [
    ("01", "Structure of a Clinical History",        "The 8 essential components"),
    ("02", "Presenting Complaint & HPC",             "SOCRATES framework in detail"),
    ("03", "Past Medical, Drug & Social History",    "MJ THREADS, drug safety, ICE"),
    ("04", "Systems Review",                         "Screening across all body systems"),
    ("05", "Patient Communication Skills",           "Techniques, models & challenging scenarios"),
    ("06", "History-Taking Assessment",              "OSCE domains & Calgary-Cambridge"),
    ("07", "Clinical Documentation",                 "SOAP notes, EHR, referrals"),
    ("08", "Red Flags & Clinical Reasoning",         "Alarm features per system"),
    ("09", "Worked Clinical Example",                "Chest pain history end-to-end"),
    ("10", "Quality Checklist & Common Pitfalls",    "OSCE-ready self-assessment"),
]
col1 = sections[:5]
col2 = sections[5:]

for i, (num, title, sub) in enumerate(col1):
    yy = 1.45 + i * 1.0
    add_rect(slide, 0.4, yy, 0.55, 0.55, NAVY)
    add_tb(slide, 0.4, yy+0.04, 0.55, 0.47, num, 14, bold=True,
           color=AMBER, align=PP_ALIGN.CENTER)
    add_tb(slide, 1.1, yy+0.02, 5.4, 0.28, title, 12, bold=True, color=NAVY)
    add_tb(slide, 1.1, yy+0.3, 5.4, 0.25, sub, 9.5, italic=True, color=MID_GREY)

for i, (num, title, sub) in enumerate(col2):
    yy = 1.45 + i * 1.0
    add_rect(slide, 7.0, yy, 0.55, 0.55, TEAL)
    add_tb(slide, 7.0, yy+0.04, 0.55, 0.47, num, 14, bold=True,
           color=WHITE, align=PP_ALIGN.CENTER)
    add_tb(slide, 7.7, yy+0.02, 5.4, 0.28, title, 12, bold=True, color=NAVY)
    add_tb(slide, 7.7, yy+0.3, 5.4, 0.25, sub, 9.5, italic=True, color=MID_GREY)

# vertical divider
add_rect(slide, 6.7, 1.3, 0.05, 5.9, MID_GREY)

# =============================================================================
# SECTION DIVIDER 1
# =============================================================================
slide = prs.slides.add_slide(BLANK)
add_section_divider(slide, "01", "Structure of a Clinical History",
                    "The 8 essential components every clinician must cover")

# =============================================================================
# SLIDE 3 – The 8 Components
# =============================================================================
slide = prs.slides.add_slide(BLANK)
slide_bg(slide)
add_slide_header(slide, "The 8 Components of a Clinical History", "Section 01")

components = [
    ("1", "Demographic\nDetails",     "Name · Age · Sex · Occupation\nMarital status · Referring source", NAVY),
    ("2", "Presenting\nComplaint",    "Chief symptom in patient's own\nwords; brief duration", TEAL),
    ("3", "History of PC\n(HPC)",     "SOCRATES: Onset, Site, Character,\nRadiation, Timing, Severity…", PURPLE),
    ("4", "Past Medical\nHistory",    "Previous illnesses, operations,\nhospitalisations, known diagnoses", ORANGE),
    ("5", "Drug History",             "Current meds, doses, compliance,\nOTC/herbal, allergies + reaction", RED),
    ("6", "Family History",           "First-degree relatives; heritable\nconditions, early cardiac disease", GREEN),
    ("7", "Social History",           "Smoking, alcohol, drugs,\noccupation, housing, ADLs, travel", AMBER),
    ("8", "Systems Review",           "Screening Q's across all body\nsystems for unvoiced symptoms", RGBColor(0x2E,0x86,0xAB)),
]
cols = 4
for idx, (num, title, body, clr) in enumerate(components):
    col = idx % cols
    row = idx // cols
    x = 0.35 + col * 3.22
    y = 1.5 + row * 2.75
    w, h = 3.0, 2.5
    add_rect(slide, x, y, w, 0.52, clr)
    add_tb(slide, x+0.1, y+0.07, 0.38, 0.38, num, 16, bold=True,
           color=WHITE, align=PP_ALIGN.CENTER)
    add_tb(slide, x+0.55, y+0.07, w-0.65, 0.38, title, 11,
           bold=True, color=WHITE)
    add_rect(slide, x, y+0.52, w, h-0.52, WHITE)
    add_tb(slide, x+0.12, y+0.62, w-0.24, h-0.72, body, 10.5,
           color=DARK_TEXT, wrap=True)

# =============================================================================
# SECTION DIVIDER 2
# =============================================================================
slide = prs.slides.add_slide(BLANK)
add_section_divider(slide, "02", "Presenting Complaint & HPC",
                    "Exploring symptoms with SOCRATES – the gold-standard framework")

# =============================================================================
# SLIDE 4 – SOCRATES Framework
# =============================================================================
slide = prs.slides.add_slide(BLANK)
slide_bg(slide)
add_slide_header(slide, "SOCRATES – Exploring Any Symptom", "Section 02")

socrates = [
    ("S", "Site",                   "Where exactly is it? Ask the patient to point with one finger. "
                                    "Is it localised or diffuse?",           NAVY),
    ("O", "Onset",                  "When did it start? Sudden vs gradual? Was there a trigger (exertion, food, stress)?",
                                                                              TEAL),
    ("C", "Character",              "What does it feel like? Sharp, dull, burning, crushing, cramping, throbbing, tight?",
                                                                              PURPLE),
    ("R", "Radiation",              "Does it spread anywhere? Arm, jaw, back, groin? Ask: 'Does it go anywhere else?'",
                                                                              ORANGE),
    ("A", "Associated Symptoms",    "What else did you notice? Nausea, vomiting, sweating, breathlessness, fever?",
                                                                              RED),
    ("T", "Timing",                 "Constant or intermittent? How long does each episode last? Any pattern (day/night)?",
                                                                              GREEN),
    ("E", "Exacerbating/Relieving", "What makes it worse or better? Activity, food, rest, posture, medications?",
                                                                              AMBER),
    ("S", "Severity",               "Score 0-10 now and at worst. How does it compare to previous pain episodes?",
                                                                              RGBColor(0x2E,0x86,0xAB)),
]

for i, (letter, label, desc, clr) in enumerate(socrates):
    col = i % 4
    row = i // 4
    x = 0.3 + col * 3.26
    y = 1.4 + row * 2.7
    w, h = 3.1, 2.55
    # Circle for letter
    circle = slide.shapes.add_shape(9, Inches(x+0.08), Inches(y+0.1),
                                     Inches(0.5), Inches(0.5))
    circle.fill.solid()
    circle.fill.fore_color.rgb = clr
    circle.line.fill.background()
    add_tb(slide, x+0.08, y+0.12, 0.5, 0.38, letter, 16,
           bold=True, color=WHITE, align=PP_ALIGN.CENTER)
    add_tb(slide, x+0.65, y+0.14, w-0.75, 0.35, label, 11,
           bold=True, color=clr)
    add_rect(slide, x, y+0.65, w, 0.04, clr)
    add_rect(slide, x, y+0.69, w, h-0.69, WHITE)
    add_tb(slide, x+0.1, y+0.78, w-0.2, h-0.88, desc, 9.5,
           color=DARK_TEXT, wrap=True)

# =============================================================================
# SLIDE 5 – HPC Deep Dive
# =============================================================================
slide = prs.slides.add_slide(BLANK)
slide_bg(slide)
add_slide_header(slide, "History of Presenting Complaint – Deep Dive", "Section 02")

add_tb(slide, 0.4, 1.32, 12.5, 0.42,
       "Beyond SOCRATES: additional dimensions to explore in every HPC", 13,
       bold=False, color=DARK_TEXT, italic=True)

hpc_items = [
    ("📅  Timeline",            "Construct a chronological narrative. When did symptoms first start? Any events preceding onset? "
                                "How have they evolved? Any previous similar episodes and how were they managed?"),
    ("🔢  Functional Impact",   "How has this affected the patient's daily life, work, sleep, relationships? "
                                "Can they walk to the shops? Climb stairs? The Functional Inquiry Score helps quantify disability."),
    ("🔴  Red Flag Screen",     "Always specifically screen for system-relevant red flags: unexplained weight loss, night sweats, "
                                "haemoptysis, haematuria, dysphagia, unexplained anaemia, new onset headache in >50 yrs."),
    ("💊  Previous Treatment",  "Has the patient seen any other healthcare provider? Tried any medications (including OTC)? "
                                "Attended A&E or been admitted for this problem before?"),
    ("🧑‍🤝‍🧑  Patient Perspective",  "Ask 'What do you think might be causing this?' and 'Is there anything you are worried "
                                "it might be?' – ICE framework (covered in Section 05)."),
]

for i, (title, body) in enumerate(hpc_items):
    y = 1.85 + i * 1.05
    add_rect(slide, 0.4, y, 4.0, 0.38, NAVY)
    add_tb(slide, 0.52, y+0.05, 3.88, 0.32, title, 10.5, bold=True, color=WHITE)
    add_rect(slide, 4.4, y, 8.5, 0.38+0.52, TEAL_LIGHT)
    add_tb(slide, 4.55, y+0.05, 8.2, 0.72, body, 9.5, color=DARK_TEXT, wrap=True)

# =============================================================================
# SECTION DIVIDER 3
# =============================================================================
slide = prs.slides.add_slide(BLANK)
add_section_divider(slide, "03", "Past Medical, Drug & Social History",
                    "MJ THREADS · Drug safety · Family & Social context")

# =============================================================================
# SLIDE 6 – PMH & MJ THREADS
# =============================================================================
slide = prs.slides.add_slide(BLANK)
slide_bg(slide)
add_slide_header(slide, "Past Medical History – MJ THREADS Mnemonic", "Section 03")

add_tb(slide, 0.4, 1.28, 12.5, 0.38,
       "Use this mnemonic to screen for the most common chronic conditions before exploring further:", 12,
       italic=True, color=DARK_TEXT)

threads_data = [
    ("M", "Myocardial Infarction / Cardiac Disease",
     "Any previous heart attacks, angina, heart failure, arrhythmias, cardiac procedures (CABG, PCI)?"),
    ("J", "Jaundice / Liver Disease",
     "Previous hepatitis, liver disease, gallstones? Any jaundice in the past?"),
    ("T", "Tuberculosis",
     "Previous TB diagnosis or contact? BCG vaccination? Relevant if immunocompromised or immigrant population."),
    ("H", "Hypertension",
     "Diagnosed hypertension? On antihypertensives? Any hypertensive complications (stroke, CKD)?"),
    ("R", "Rheumatic Fever / Rheumatic Heart Disease",
     "Childhood rheumatic fever? Valvular heart disease? Joint problems in childhood?"),
    ("E", "Epilepsy / Neurological Disease",
     "Seizures, strokes, TIAs, migraines, Parkinson's, MS? Neurological operations?"),
    ("A", "Asthma / Respiratory Disease",
     "Asthma, COPD, bronchiectasis, previous pneumonias, pleural effusion, PE, obstructive sleep apnoea?"),
    ("D", "Diabetes Mellitus",
     "Type 1 or Type 2? Duration, control (HbA1c), complications (eyes, kidneys, feet, cardiovascular)?"),
    ("S", "Stroke / Neurological",
     "Strokes, TIAs, dementia, peripheral neuropathy? Any residual neurological deficits?"),
]

for i, (letter, condition, detail) in enumerate(threads_data):
    col = i % 3
    row = i // 3
    x = 0.35 + col * 4.32
    y = 1.75 + row * 1.7
    clr = [NAVY, TEAL, PURPLE][i % 3]
    add_rect(slide, x, y, 0.45, 1.42, clr)
    add_tb(slide, x, y+0.35, 0.45, 0.65, letter, 20, bold=True,
           color=AMBER if clr == NAVY else WHITE, align=PP_ALIGN.CENTER)
    add_rect(slide, x+0.45, y, 3.72, 1.42, WHITE)
    add_tb(slide, x+0.55, y+0.08, 3.55, 0.35, condition, 10, bold=True, color=clr)
    add_tb(slide, x+0.55, y+0.48, 3.55, 0.85, detail, 8.8, color=DARK_TEXT, wrap=True)

# =============================================================================
# SLIDE 7 – Drug History & Allergies
# =============================================================================
slide = prs.slides.add_slide(BLANK)
slide_bg(slide)
add_slide_header(slide, "Drug History – A Patient Safety Critical Step", "Section 03")

# Left panel
add_rect(slide, 0.35, 1.35, 6.2, 5.85, WHITE)
add_rect(slide, 0.35, 1.35, 6.2, 0.48, TEAL)
add_tb(slide, 0.5, 1.4, 6.0, 0.38, "What to Ask About Medications", 12, bold=True, color=WHITE)

dh_points = [
    "Name of each medication (generic and brand)",
    "Dose, route (oral/inhaled/IV), and frequency",
    "Duration – how long have they been taking it?",
    "Compliance – do they take it as prescribed?",
    "Recent changes – anything started, stopped, or dose adjusted?",
    "Over-the-counter (OTC) medications (NSAIDs, antacids, vitamins)",
    "Herbal / alternative remedies (St John's Wort, garlic, etc.)",
    "Contraceptive pill or hormone replacement therapy",
    "Recreational drugs (document non-judgementally)",
]
for i, pt in enumerate(dh_points):
    y = 1.98 + i * 0.54
    add_rect(slide, 0.48, y+0.06, 0.18, 0.18, TEAL)
    add_tb(slide, 0.75, y, 5.6, 0.5, pt, 10, color=DARK_TEXT, wrap=True)

# Right panel
add_rect(slide, 6.9, 1.35, 6.1, 2.7, RGBColor(0xFF,0xEE,0xEE))
add_rect(slide, 6.9, 1.35, 6.1, 0.48, RED)
add_tb(slide, 7.05, 1.4, 5.9, 0.38, "⚠  Allergy Documentation", 12, bold=True, color=WHITE)
allergy_text = ("For EVERY reported allergy, document:\n\n"
                "1.  Name of the substance\n"
                "2.  Exact type of reaction\n      (e.g., anaphylaxis, rash, nausea – NOT just 'allergy')\n"
                "3.  Severity of reaction\n"
                "4.  Year it occurred\n"
                "5.  Whether it was confirmed by an allergist\n\n"
                "NKDA = No Known Drug Allergies\n"
                "Never assume NKA if not confirmed!")
add_tb(slide, 7.05, 1.92, 5.8, 2.02, allergy_text, 9.5, color=DARK_TEXT, wrap=True)

add_rect(slide, 6.9, 4.2, 6.1, 3.0, WHITE)
add_rect(slide, 6.9, 4.2, 6.1, 0.48, AMBER)
add_tb(slide, 7.05, 4.25, 5.9, 0.38, "💡  OSCE Drug History Tips", 12, bold=True, color=DARK_TEXT)
osce_tips = [
    "Allergies are commonly the first mark lost in OSCEs",
    "Always ask about recent antibiotic use if suspecting infection",
    "Check for medications that cause the presenting symptom (e.g., ACEi cough, beta-blocker wheeze)",
    "Ask about Warfarin/DOAC/antiplatelet use before any procedure discussion",
    "NSAIDs: common cause of GI bleeds, renal impairment, hypertension",
]
for i, tip in enumerate(osce_tips):
    y = 4.8 + i * 0.46
    add_tb(slide, 7.05, y, 5.8, 0.44, f"• {tip}", 9.5, color=DARK_TEXT, wrap=True)

# =============================================================================
# SLIDE 8 – Family & Social History
# =============================================================================
slide = prs.slides.add_slide(BLANK)
slide_bg(slide)
add_slide_header(slide, "Family History & Social History", "Section 03")

# Family History left
add_rect(slide, 0.35, 1.35, 6.2, 5.85, WHITE)
add_rect(slide, 0.35, 1.35, 6.2, 0.48, NAVY)
add_tb(slide, 0.5, 1.4, 6.0, 0.38, "Family History", 13, bold=True, color=WHITE)

fh_items = [
    ("Parents", "Health status and age; if deceased, cause of death and age"),
    ("Siblings", "Any significant illnesses; same genetic risk"),
    ("Children", "Any diagnosed conditions"),
    ("Heritable Conditions\nto Ask About", "IHD / MI (especially <60 yrs) · Hypertension\n"
      "Hyperlipidaemia · Diabetes mellitus\nCancers: colorectal, breast, ovarian, prostate\n"
      "Mental health: depression, bipolar, schizophrenia\nNeurological: Huntington's, epilepsy"),
    ("Pedigree Chart", "Draw a family tree for complex hereditary conditions. "
     "Use squares for males, circles for females, filled = affected."),
]
y = 1.95
for title, body in fh_items:
    add_rect(slide, 0.48, y, 1.9, len(body.split('\n'))*0.3+0.25, TEAL_LIGHT)
    add_tb(slide, 0.55, y+0.05, 1.82, 0.4, title, 9.5, bold=True, color=TEAL)
    add_tb(slide, 2.5, y, 3.9, len(body.split('\n'))*0.3+0.25, body, 9.5,
           color=DARK_TEXT, wrap=True)
    y += max(len(body.split('\n'))*0.34 + 0.28, 0.65)

# Social History right
add_rect(slide, 6.9, 1.35, 6.1, 5.85, WHITE)
add_rect(slide, 6.9, 1.35, 6.1, 0.48, PURPLE)
add_tb(slide, 7.05, 1.4, 5.9, 0.38, "Social History", 13, bold=True, color=WHITE)

sh_items = [
    ("🚬 Smoking",       "Current / ex / never. Pack-years = packs/day × years smoked. Type: cigarettes, "
                         "cigars, pipe, shisha, e-cigarette."),
    ("🍺 Alcohol",       "Units per week (1 unit = 10ml pure alcohol). CAGE questionnaire if dependence suspected. "
                         "Binge drinking patterns."),
    ("💊 Recreational",  "IV drug use (BBV, endocarditis risk), cocaine, cannabis, amphetamines. "
                         "Non-judgemental tone is essential."),
    ("🏗️ Occupation",    "Current and past jobs. Specific exposures: dust, asbestos, fumes, radiation, shift work, "
                         "stress, heavy lifting."),
    ("🏠 Housing",       "House/flat, owner/renter, stairs, heating, dampness. Who lives with them? "
                         "Carer support? Safeguarding concerns?"),
    ("🚶 ADLs",          "Activities of Daily Living: washing, dressing, cooking, shopping, mobility, continence, "
                         "finances. Barthel Index score if relevant."),
    ("✈️ Travel",        "Recent international travel, countries visited, vaccinations, malaria prophylaxis, "
                         "food/water exposure."),
    ("🥗 Diet/Exercise", "Relevant to metabolic, cardiovascular, and eating disorder presentations."),
]
y = 1.95
for icon_title, body in sh_items:
    add_tb(slide, 7.05, y, 1.6, 0.52, icon_title, 10, bold=True, color=PURPLE)
    add_tb(slide, 8.75, y, 4.1, 0.55, body, 9.5, color=DARK_TEXT, wrap=True)
    y += 0.68

# =============================================================================
# SECTION DIVIDER 4
# =============================================================================
slide = prs.slides.add_slide(BLANK)
add_section_divider(slide, "04", "Systems Review",
                    "Systematic screening across all body systems")

# =============================================================================
# SLIDE 9 – Systems Review Table
# =============================================================================
slide = prs.slides.add_slide(BLANK)
slide_bg(slide)
add_slide_header(slide, "Systems Review – Screening Questions by System", "Section 04")

add_tb(slide, 0.4, 1.28, 12.5, 0.32,
       "Ask 1-3 targeted questions per system to catch symptoms the patient has not volunteered:", 11,
       italic=True, color=DARK_TEXT)

sr_data = [
    ["System", "Key Screening Questions", "Red Flags to Probe"],
    ["Cardiovascular", "Chest pain/tightness, palpitations, breathlessness, ankle swelling, orthopnoea, PND",
     "Exertional chest pain, syncope, severe breathlessness"],
    ["Respiratory", "Cough (dry/productive), breathlessness (at rest/exertion), wheeze, haemoptysis, stridor",
     "Haemoptysis, weight loss, night sweats, hoarse voice"],
    ["Gastrointestinal", "Appetite, weight change, nausea/vomiting, heartburn, dysphagia, bowel habit, rectal bleeding, jaundice",
     "Dysphagia, unexplained weight loss, rectal bleeding, change in bowel habit >6 wks"],
    ["Genitourinary", "Dysuria, frequency, haematuria, nocturia, incontinence; menstrual cycle, sexual history",
     "Painless haematuria, loin pain, post-menopausal bleeding"],
    ["Neurological", "Headache, visual changes, dizziness, vertigo, weakness (focal), sensory disturbance, seizures, memory",
     "Thunderclap headache, progressive weakness, sudden visual loss, first seizure"],
    ["Musculoskeletal", "Joint pain, swelling, stiffness, back pain, muscle weakness, cramps, functional limitation",
     "Bone pain at rest/night, joint swelling with systemic symptoms"],
    ["Endocrine", "Weight change, thirst/polyuria, heat/cold intolerance, sweating, skin/hair changes, menstrual irregularity",
     "Rapid unexplained weight loss or gain, exophthalmos"],
    ["Haematological", "Fatigue, pallor, bruising, bleeding tendency, lymph node swelling, recurrent infections",
     "Painless lymphadenopathy, recurrent infections, purpura"],
    ["Psychiatric", "Mood (low/elevated), energy, sleep, appetite, concentration, anxiety, hallucinations, self-harm ideation",
     "Suicidal ideation, psychotic symptoms, severe functional decline"],
]

table_shape(slide, 0.35, 1.65, 0, sr_data,
            col_widths=[2.2, 5.8, 4.85], header_color=NAVY)

# =============================================================================
# SECTION DIVIDER 5
# =============================================================================
slide = prs.slides.add_slide(BLANK)
add_section_divider(slide, "05", "Patient Communication Skills",
                    "Evidence-based techniques for effective clinical encounters")

# =============================================================================
# SLIDE 10 – Core Communication Skills
# =============================================================================
slide = prs.slides.add_slide(BLANK)
slide_bg(slide)
add_slide_header(slide, "Core Communication Skills", "Section 05")

comm_items = [
    ("Introduction", "State your full name, role, and purpose. Confirm patient identity (name + DOB). "
     "This earns OSCE marks AND is a legal/safety requirement.", NAVY),
    ("Opening – Open Questions", "'Tell me about what has been troubling you.' Allow 1-2 minutes uninterrupted. "
     "Resist the urge to jump in. Studies show GPs interrupt in under 18 seconds.", TEAL),
    ("Active Listening", "Maintain appropriate eye contact. Use nodding, minimal encouragers ('I see', 'mm-hmm', 'go on'). "
     "Lean slightly forward. Avoid typing or writing during this phase.", PURPLE),
    ("Reflection & Clarification", "Mirror back: 'So if I've understood correctly, the pain started suddenly yesterday?' "
     "Paraphrase, don't just repeat verbatim. Invite correction.", ORANGE),
    ("Empathy & Validation", "Acknowledge emotions: 'That sounds really worrying' or 'I can imagine that's been very difficult.' "
     "Name the emotion. Do not dismiss or minimise.", RED),
    ("Signposting", "Warn before moving topic: 'I'd like to move on and ask about your past health history now – is that OK?' "
     "This prevents the patient feeling interrogated.", GREEN),
    ("Avoiding Jargon", "Translate clinical language: 'heart attack' not 'MI', 'water works' not 'urinary tract', "
     "'blood test' not 'FBC and U&Es'. Check understanding frequently.", AMBER),
    ("Summarising", "Briefly recap at the end: 'Let me just check I've got everything right…' Invite correction: "
     "'Have I missed anything important you wanted to mention?'", RGBColor(0x2E,0x86,0xAB)),
]

for i, (title, body, clr) in enumerate(comm_items):
    col = i % 2
    row = i // 2
    x = 0.35 + col * 6.52
    y = 1.4 + row * 1.45
    w = 6.2
    add_rect(slide, x, y, 0.08, 1.28, clr)
    add_rect(slide, x+0.08, y, w-0.08, 1.28, WHITE)
    add_tb(slide, x+0.22, y+0.08, w-0.3, 0.3, title, 10.5, bold=True, color=clr)
    add_tb(slide, x+0.22, y+0.42, w-0.3, 0.78, body, 9.5, color=DARK_TEXT, wrap=True)

# =============================================================================
# SLIDE 11 – ICE Framework
# =============================================================================
slide = prs.slides.add_slide(BLANK)
slide_bg(slide)
add_slide_header(slide, "The ICE Framework – Patient-Centred Communication", "Section 05")

add_tb(slide, 0.4, 1.28, 12.5, 0.35,
       "ICE stands for Ideas, Concerns, and Expectations. It is central to patient-centred care and is specifically assessed in OSCEs.",
       12, italic=True, color=DARK_TEXT)

ice_items = [
    ("I", "Ideas",        "What the patient\nthinks is wrong",
     "What do you think might be causing your symptoms?\n"
     "Do you have any thoughts about what it could be?\n"
     "Have you looked anything up about your symptoms?",
     "Uncovers misconceptions that need addressing\n"
     "Reveals health literacy and cultural beliefs\n"
     "Informs how you frame your explanation",   NAVY),
    ("C", "Concerns",     "Their specific\nworries or fears",
     "Is there anything particular you're worried it might be?\n"
     "What is your biggest concern about this?\n"
     "Is there anything frightening you about these symptoms?",
     "Addresses the reason behind the visit\n"
     "Often reveals the 'hidden agenda' (e.g., fear of cancer)\n"
     "Directly impacts patient satisfaction and compliance",  RED),
    ("E", "Expectations", "What they hope\nwill happen today",
     "What were you hoping I might do for you today?\n"
     "What would you most like to get from this appointment?\n"
     "Is there something specific you were hoping for?",
     "Prevents mismatched consultations\n"
     "Identifies when patient expectations need managing\n"
     "Helps tailor your explanation and management plan",     TEAL),
]

for i, (letter, title, tag, questions, importance, clr) in enumerate(ice_items):
    x = 0.35 + i * 4.3
    y = 1.78
    w = 4.1
    # Header
    add_rect(slide, x, y, w, 0.6, clr)
    circle = slide.shapes.add_shape(9, Inches(x+0.12), Inches(y+0.08),
                                     Inches(0.44), Inches(0.44))
    circle.fill.solid(); circle.fill.fore_color.rgb = WHITE
    circle.line.fill.background()
    add_tb(slide, x+0.12, y+0.1, 0.44, 0.35, letter, 18,
           bold=True, color=clr, align=PP_ALIGN.CENTER)
    add_tb(slide, x+0.65, y+0.08, w-0.75, 0.22, title, 12, bold=True, color=WHITE)
    add_tb(slide, x+0.65, y+0.32, w-0.75, 0.24, tag, 9, italic=True, color=TEAL_LIGHT)

    # Questions box
    add_rect(slide, x, y+0.6, w, 1.85, TEAL_LIGHT)
    add_tb(slide, x+0.1, y+0.65, 0.8, 0.28, "Sample", 8, bold=True, color=TEAL)
    add_tb(slide, x+0.1, y+0.88, w-0.2, 1.52, questions, 9.5,
           color=DARK_TEXT, wrap=True)

    # Why it matters box
    add_rect(slide, x, y+2.45, w, 1.8, WHITE)
    add_tb(slide, x+0.1, y+2.52, w-0.2, 0.28, "Why it Matters", 9, bold=True, color=clr)
    add_tb(slide, x+0.1, y+2.82, w-0.2, 1.35, importance, 9.5,
           color=DARK_TEXT, wrap=True)

# =============================================================================
# SLIDE 12 – Challenging Communication Scenarios
# =============================================================================
slide = prs.slides.add_slide(BLANK)
slide_bg(slide)
add_slide_header(slide, "Challenging Communication Scenarios", "Section 05")

scenarios = [
    ("😠  Angry Patient", RED,
     "Do NOT become defensive or argumentative.",
     "• Acknowledge anger first: 'I can see you're really frustrated and I'm sorry about that.'\n"
     "• Stay calm and use a quiet, steady voice\n"
     "• Explore the source: 'Can you tell me what has upset you?'\n"
     "• Avoid blaming colleagues or the system\n"
     "• If personal safety is threatened, disengage professionally"),
    ("😶  Quiet/Withdrawn Patient", NAVY,
     "Silence may reflect depression, anxiety, cultural norms, or cognitive impairment.",
     "• Use open questions and allow adequate silence\n"
     "• Normalise: 'Some people find it hard to talk about this – that's completely understandable.'\n"
     "• Consider screening for depression with PHQ-2 or GAD-2\n"
     "• Check if an interpreter or support person would help\n"
     "• Return to sensitive topics gently later in the consultation"),
    ("😢  Distressed / Crying Patient", TEAL,
     "Stop. Do not rush to fill silence or continue questioning immediately.",
     "• Pause and acknowledge non-verbally (pause, lean forward, offer tissues)\n"
     "• Name the emotion: 'It seems like this has been really difficult for you.'\n"
     "• Wait until the patient signals they are ready to continue\n"
     "• Do not say 'Don't worry' – it dismisses their concern\n"
     "• Document emotional state as part of the mental state examination"),
    ("👴  Collateral History", PURPLE,
     "Essential when patient cannot give a full history (dementia, confusion, children, intoxication).",
     "• Identify the source and their relationship to the patient\n"
     "• Confirm whether the patient has capacity and consents\n"
     "• Ask the collateral historian the same structured questions\n"
     "• Note discrepancies between patient and collateral accounts\n"
     "• Always document clearly: 'History obtained from wife as patient unable to provide details'"),
    ("🌍  Language Barrier", ORANGE,
     "Always use a trained medical interpreter, not a family member where avoidable.",
     "• Book a professional interpreter (telephone/video/in-person)\n"
     "• Speak to the patient, not the interpreter ('How are you feeling?')\n"
     "• Use short, simple sentences – one idea at a time\n"
     "• Confirm understanding by asking them to repeat back key points\n"
     "• Document language used and interpreter present"),
]

for i, (title, clr, subtitle, bullets) in enumerate(scenarios):
    col = i % 3 if i < 3 else (i - 3) % 2
    row = 0 if i < 3 else 1
    if i < 3:
        x = 0.35 + i * 4.3
        w = 4.1
    else:
        x = 0.35 + (i - 3) * 6.52
        w = 6.2

    y = 1.4 + row * 3.1
    h = 2.75

    add_rect(slide, x, y, w, 0.48, clr)
    add_tb(slide, x+0.1, y+0.08, w-0.2, 0.34, title, 10.5, bold=True, color=WHITE)
    add_rect(slide, x, y+0.48, w, 0.38, RGBColor(0xF0,0xF0,0xF0))
    add_tb(slide, x+0.1, y+0.52, w-0.2, 0.32, subtitle, 8.8, italic=True, color=DARK_TEXT)
    add_rect(slide, x, y+0.86, w, h-0.86, WHITE)
    add_tb(slide, x+0.1, y+0.94, w-0.2, h-1.0, bullets, 9, color=DARK_TEXT, wrap=True)

# =============================================================================
# SECTION DIVIDER 6
# =============================================================================
slide = prs.slides.add_slide(BLANK)
add_section_divider(slide, "06", "History-Taking Assessment",
                    "OSCE marking criteria · Calgary-Cambridge model · Grading frameworks")

# =============================================================================
# SLIDE 13 – OSCE Assessment Domains
# =============================================================================
slide = prs.slides.add_slide(BLANK)
slide_bg(slide)
add_slide_header(slide, "OSCE Assessment – How History Taking is Marked", "Section 06")

osce_data = [
    ["Domain", "Marks Available", "What Examiners Look For", "How Marks Are Lost"],
    ["Introduction\n& Consent",     "5-10%",
     "Name, role, purpose; patient ID confirmed (name + DOB); consent obtained; chaperone offered if appropriate",
     "Jumping straight to questions; not confirming identity; no consent"],
    ["PC & HPC\nContent",          "25-30%",
     "SOCRATES fully explored; timeline established; associated symptoms covered; red flags screened",
     "Missing associated symptoms; ignoring red flags; no timeline; superficial exploration"],
    ["Background\nHistory",        "20-25%",
     "PMH (MJ THREADS); drug history with allergies + reaction type; family history; social history",
     "Omitting allergies or reaction type; no occupation or smoking history; incomplete PMH"],
    ["Systems\nReview",            "10-15%",
     "Brief targeted screen across all major systems using appropriate questions",
     "Skipping entirely; asking irrelevant systems only; no red flag screening"],
    ["Communication\nTechnique",   "20-25%",
     "Open questions first; ICE explored; active listening; empathy demonstrated; signposting; no jargon",
     "Only closed questions; ICE absent; jargon used; no empathy; poor body language"],
    ["Professionalism\n& Ethics",  "5-10%",
     "Non-judgemental; sensitive to cultural/social context; maintains dignity; honest about role limits",
     "Appearing rushed; dismissive responses; blaming; breaching confidentiality"],
    ["Summarising\n& Closure",     "5-10%",
     "Accurate summary of key points; invites correction; explains next steps; asks if patient has questions",
     "Abrupt ending; no summary; patient not given opportunity to ask questions"],
]

table_shape(slide, 0.35, 1.45, 0, osce_data,
            col_widths=[2.3, 1.7, 4.65, 4.5], header_color=NAVY)

# =============================================================================
# SLIDE 14 – Calgary-Cambridge Model
# =============================================================================
slide = prs.slides.add_slide(BLANK)
slide_bg(slide)
add_slide_header(slide, "The Calgary-Cambridge Communication Model", "Section 06")

add_tb(slide, 0.4, 1.28, 12.5, 0.38,
       "A framework taught in most medical schools worldwide for structuring clinical consultations:", 12,
       italic=True, color=DARK_TEXT)

cc_steps = [
    ("1", "Initiating the Session",
     "Establish initial rapport with the patient.\nIdentify and acknowledge the reason for the consultation.\nAgree an agenda: 'Is there anything else you wanted to discuss today?'",
     NAVY),
    ("2", "Gathering Information",
     "Explore the problem from TWO perspectives:\n• Disease framework: biomedical signs/symptoms\n• Illness framework: patient's experience, ICE\nUse open → focussed → closed funnel approach.",
     TEAL),
    ("3", "Physical Examination",
     "Perform targeted examination after history.\nExplain findings to the patient throughout.\nMaintain dignity at all times.\nAsk permission before examining sensitive areas.",
     PURPLE),
    ("4", "Explanation & Planning",
     "Give information in chunks the patient can absorb.\nCheck understanding after each segment.\nUse diagrams, written information, or models.\nAchieve shared understanding and informed consent.",
     ORANGE),
    ("5", "Closing the Session",
     "Summarise the consultation.\nAgree on a forward plan with the patient.\nSafety netting: 'If things get worse or change, please come back or call immediately.'\nAppropriate point of closure.",
     GREEN),
]

# Central flow arrow
add_tb(slide, 6.2, 1.7, 1.0, 5.4, "↓\n↓\n↓\n↓\n↓", 22, bold=True, color=MID_GREY, align=PP_ALIGN.CENTER)

for i, (num, title, body, clr) in enumerate(cc_steps):
    row = i
    y = 1.65 + row * 1.06
    # Left: number + title
    add_rect(slide, 0.35, y, 1.1, 0.9, clr)
    add_tb(slide, 0.35, y+0.05, 1.1, 0.5, num, 24, bold=True,
           color=WHITE, align=PP_ALIGN.CENTER)
    add_tb(slide, 1.55, y+0.12, 4.45, 0.38, title, 11, bold=True, color=clr)
    # Right: body
    add_rect(slide, 7.3, y, 5.7, 0.9, TEAL_LIGHT if i % 2 == 0 else WHITE)
    add_tb(slide, 7.45, y+0.05, 5.45, 0.82, body, 8.8, color=DARK_TEXT, wrap=True)
    # Middle body small
    add_tb(slide, 1.55, y+0.5, 4.5, 0.38, body.split('\n')[0][:60], 8.5,
           italic=True, color=DARK_TEXT, wrap=True)

# =============================================================================
# SECTION DIVIDER 7
# =============================================================================
slide = prs.slides.add_slide(BLANK)
add_section_divider(slide, "07", "Clinical Documentation",
                    "SOAP notes · EHR best practice · Referral letters · Legal standards")

# =============================================================================
# SLIDE 15 – Documentation Principles
# =============================================================================
slide = prs.slides.add_slide(BLANK)
slide_bg(slide)
add_slide_header(slide, "Principles of Clinical Documentation", "Section 07")

add_tb(slide, 0.4, 1.28, 12.5, 0.32,
       "Good documentation is a professional, ethical, and legal requirement – not an afterthought.", 12,
       bold=True, italic=True, color=RED)

principles = [
    ("Legibility", "Write clearly in black ink for handwritten notes. If handwriting is unclear, print. "
     "Any illegible entry in medical notes is a medico-legal risk."),
    ("Date, Time & Signature", "Every entry must be dated (DD/MM/YYYY), timed (24-hour clock), and signed. "
     "Print your full name and role (e.g., 'F1 Doctor') beneath your signature."),
    ("Objective Language", "Document what the patient said and what you observed. "
     "Write: 'Patient reports 3-day history of central chest pain.' Not: 'Patient has angina.'"),
    ("Significant Negatives", "Record clinically important negative findings: 'No haemoptysis. No weight loss. "
     "No lymphadenopathy.' Absence of a finding carries equal clinical weight."),
    ("Contemporaneous", "Document as soon as possible after the encounter. "
     "Late entries must be labelled 'Late Entry' with the time/date of original event and time of documentation."),
    ("Error Correction", "Draw a single line through the error. Write 'error' or 'mistaken entry'. "
     "Sign and date the correction. NEVER use correction fluid or completely obscure an error."),
    ("Abbreviations", "Use only universally accepted abbreviations in your institution. "
     "When in doubt, write it out. 'SOB' for example could be misread in different contexts."),
    ("Confidentiality", "Medical records are confidential. Do not include unnecessary personal information. "
     "Restrict access to those with clinical need. Follow Caldicott Principles and GDPR."),
]

for i, (title, body) in enumerate(principles):
    col = i % 2
    row = i // 2
    x = 0.35 + col * 6.52
    y = 1.75 + row * 1.35
    w = 6.1
    clr = [NAVY, TEAL, PURPLE, ORANGE, RED, GREEN, AMBER, RGBColor(0x2E,0x86,0xAB)][i]
    add_rect(slide, x, y, 0.06, 1.15, clr)
    add_rect(slide, x+0.06, y, w-0.06, 1.15, WHITE)
    add_tb(slide, x+0.2, y+0.08, w-0.3, 0.3, title, 10.5, bold=True, color=clr)
    add_tb(slide, x+0.2, y+0.42, w-0.3, 0.68, body, 9.5, color=DARK_TEXT, wrap=True)

# =============================================================================
# SLIDE 16 – SOAP Format & EHR
# =============================================================================
slide = prs.slides.add_slide(BLANK)
slide_bg(slide)
add_slide_header(slide, "SOAP Documentation Format & Electronic Health Records", "Section 07")

# SOAP left panel
add_rect(slide, 0.35, 1.35, 6.2, 5.85, WHITE)
add_rect(slide, 0.35, 1.35, 6.2, 0.45, NAVY)
add_tb(slide, 0.5, 1.4, 6.0, 0.35, "SOAP Note Format", 13, bold=True, color=WHITE)

soap = [
    ("S", "Subjective",   "History",              NAVY,
     "Everything the patient tells you:\n"
     "• PC in patient's own words (quoted)\n"
     "• Full HPC with SOCRATES\n"
     "• PMH, DH (with doses), allergies\n"
     "• FH, SH, SR"),
    ("O", "Objective",    "Examination\n& Investigations", TEAL,
     "What you observe and measure:\n"
     "• Vital signs (HR, BP, RR, SpO2, Temp)\n"
     "• Physical examination findings\n"
     "• Investigation results ordered/received"),
    ("A", "Assessment",   "Diagnosis",             PURPLE,
     "Your clinical reasoning:\n"
     "• Working diagnosis\n"
     "• Differential diagnoses (ranked)\n"
     "• Brief justification for each"),
    ("P", "Plan",         "Management",            GREEN,
     "Actions taken and planned:\n"
     "• Investigations ordered\n"
     "• Treatments started\n"
     "• Referrals, safety netting, follow-up"),
]

for i, (letter, full, tag, clr, body) in enumerate(soap):
    y = 1.9 + i * 1.3
    add_rect(slide, 0.48, y, 0.5, 1.1, clr)
    add_tb(slide, 0.48, y+0.2, 0.5, 0.55, letter, 20, bold=True,
           color=WHITE, align=PP_ALIGN.CENTER)
    add_rect(slide, 0.98, y, 5.45, 1.1, TEAL_LIGHT if i % 2 == 0 else LIGHT_GREY)
    add_tb(slide, 1.1, y+0.05, 2.0, 0.28, full, 11, bold=True, color=clr)
    add_tb(slide, 1.1, y+0.32, 5.2, 0.72, body, 8.8, color=DARK_TEXT, wrap=True)

# EHR right panel
add_rect(slide, 6.9, 1.35, 6.1, 5.85, WHITE)
add_rect(slide, 6.9, 1.35, 6.1, 0.45, TEAL)
add_tb(slide, 7.05, 1.4, 5.9, 0.35, "Electronic Health Records (EHR)", 13, bold=True, color=WHITE)

ehr_sections = [
    ("Best Practice", [
        "Always log in with YOUR OWN credentials only",
        "Never log in with a colleague's username/password",
        "Lock the screen when leaving the workstation",
        "Review structured templates carefully – do not blindly accept defaults",
        "Avoid copy-paste of previous entries without individual review",
    ], TEAL),
    ("Data Entry Standards", [
        "Structured fields for: diagnoses, vitals, allergies, medications",
        "Free text for: narrative history, clinical reasoning, follow-up plans",
        "Flag allergy alerts – ensure they are up to date at every encounter",
        "Medication reconciliation at each admission and discharge",
        "Document who was present and whether consent was obtained",
    ], NAVY),
    ("Safety & Governance", [
        "Incident reports for EHR errors that affect patient care",
        "Read access logs – only access records with clinical need",
        "Data breaches must be reported per GDPR/local policy",
        "Photographic documentation requires explicit written consent",
    ], RED),
]

y = 1.92
for section_title, items, clr in ehr_sections:
    add_rect(slide, 6.98, y, 5.9, 0.3, clr)
    add_tb(slide, 7.08, y+0.04, 5.7, 0.24, section_title, 10, bold=True, color=WHITE)
    y += 0.32
    for item in items:
        add_tb(slide, 7.08, y, 5.7, 0.4, f"  •  {item}", 8.8, color=DARK_TEXT, wrap=True)
        y += 0.38
    y += 0.12

# =============================================================================
# SECTION DIVIDER 8
# =============================================================================
slide = prs.slides.add_slide(BLANK)
add_section_divider(slide, "08", "Red Flags & Clinical Reasoning",
                    "Alarm features that must not be missed in history taking")

# =============================================================================
# SLIDE 17 – Red Flags
# =============================================================================
slide = prs.slides.add_slide(BLANK)
slide_bg(slide)
add_slide_header(slide, "Red Flags in History Taking – System by System", "Section 08")

add_tb(slide, 0.4, 1.28, 12.5, 0.3,
       "These features indicate potentially serious pathology requiring urgent investigation or referral:", 11,
       bold=True, italic=True, color=RED)

rf_data = [
    ["System", "Red Flag Features", "Possible Serious Diagnosis"],
    ["Cardiovascular", "Exertional chest pain/tightness; pain radiating to arm or jaw; syncope; severe dyspnoea at rest; new onset palpitations with haemodynamic compromise",
     "ACS, PE, aortic dissection, malignant arrhythmia"],
    ["Respiratory", "Haemoptysis (blood in sputum); progressive unexplained breathlessness; stridor; unilateral pleural effusion; night sweats + weight loss",
     "Lung cancer, TB, PE, epiglottitis"],
    ["Gastrointestinal", "Dysphagia (esp. progressive/solids); unexplained weight loss >5% in 3 months; rectal bleeding; change in bowel habit >6 weeks; persistent vomiting; palpable mass",
     "GI cancer, IBD, bowel obstruction, upper GI bleed"],
    ["Genitourinary", "Painless macroscopic haematuria; post-menopausal bleeding; loin pain + fever + haematuria; testicular mass",
     "Bladder/renal cancer, endometrial cancer, pyelonephritis, testicular cancer"],
    ["Neurological", "Thunderclap headache ('worst ever'); sudden focal neurological deficit; first seizure; progressive cognitive decline; papilloedema; neck stiffness + photophobia",
     "SAH, stroke, meningitis, SOL, temporal arteritis"],
    ["Musculoskeletal", "Bone pain at rest or at night; unexplained back pain in >50 yrs with systemic symptoms; joint swelling with fever and systemics; new onset hip pain after trauma in elderly",
     "Malignancy (primary/metastatic), septic arthritis, fracture"],
    ["General / Systemic", "Unexplained weight loss; drenching night sweats; fever of unknown origin; new persistent fatigue; palpable lymphadenopathy",
     "Lymphoma, leukaemia, solid malignancy, TB, HIV"],
]

table_shape(slide, 0.35, 1.65, 0, rf_data,
            col_widths=[2.4, 6.3, 4.1], header_color=RED)

# =============================================================================
# SECTION DIVIDER 9
# =============================================================================
slide = prs.slides.add_slide(BLANK)
add_section_divider(slide, "09", "Worked Clinical Example",
                    "A complete history end-to-end – chest pain presentation")

# =============================================================================
# SLIDE 18 – Worked Example Part 1
# =============================================================================
slide = prs.slides.add_slide(BLANK)
slide_bg(slide)
add_slide_header(slide, "Worked Example – Mr. John Smith, 58M (Part 1)", "Section 09")

add_tb(slide, 0.4, 1.28, 12.5, 0.3,
       "A model history for stable angina – use this as a template for your own clerking notes.", 11,
       italic=True, color=DARK_TEXT)

ex1 = [
    ("Demographics",
     "Mr. John Smith | DOB: 12/03/1968 | 58-year-old male | Retired HGV driver\nReferred by GP | NHS No: 123-456-7890"),
    ("Presenting Complaint (PC)",
     "\"Chest tightness and breathlessness when walking up stairs, for the last 3 weeks.\"  (Patient's own words)"),
    ("History of Presenting Complaint (HPC)",
     "Site: Central chest, behind sternum. Radiation: Left arm and occasionally jaw.\n"
     "Onset: Gradual over 3 weeks. Occurs consistently on moderate exertion (stairs, hills, carrying shopping).\n"
     "Character: Tight, squeezing sensation – not sharp or stabbing. Severity: 6/10 at worst.\n"
     "Timing: Lasts 3-5 minutes, resolves with rest. No rest pain. No episodes at night.\n"
     "Exacerbating: Physical exertion, cold weather. Relieving: Rest within 5 min. Not relieved by antacids.\n"
     "Associated: Mild breathlessness and diaphoresis during episodes. No syncope, palpitations, or cough.\n"
     "Red Flags: No rest pain. No prolonged episodes (>20 min). No haemodynamic compromise. No fever."),
    ("Patient's ICE",
     "Ideas: 'I think it might be my heart – my dad had a heart attack.'\n"
     "Concerns: 'I'm worried I'm going to have a heart attack too.'\n"
     "Expectations: 'I'd like an ECG and to know what's going on.'"),
]

y = 1.68
colors_left = [NAVY, TEAL, PURPLE, AMBER]
for i, (title, body) in enumerate(ex1):
    clr = colors_left[i]
    h = 0.55 if i < 2 else (1.72 if i == 2 else 0.72)
    add_rect(slide, 0.35, y, 2.8, h, clr)
    add_tb(slide, 0.45, y+0.1, 2.6, h-0.15, title, 10.5, bold=True, color=WHITE, wrap=True)
    add_rect(slide, 3.15, y, 9.85, h, TEAL_LIGHT if i % 2 == 0 else WHITE)
    add_tb(slide, 3.28, y+0.07, 9.6, h-0.12, body, 9.5, color=DARK_TEXT, wrap=True)
    y += h + 0.08

# =============================================================================
# SLIDE 19 – Worked Example Part 2
# =============================================================================
slide = prs.slides.add_slide(BLANK)
slide_bg(slide)
add_slide_header(slide, "Worked Example – Mr. John Smith, 58M (Part 2)", "Section 09")

ex2 = [
    ("Past Medical History",
     "Hypertension (dx 2015, controlled). Hypercholesterolaemia (dx 2018). Appendicectomy 1993.\n"
     "No known diabetes, asthma, or previous cardiac events. No previous hospitalisations for chest symptoms."),
    ("Drug History",
     "Amlodipine 5mg OD (HTN). Atorvastatin 40mg ON (cholesterol). Compliant with both.\n"
     "No OTC medications. No herbal remedies. NKDA (No Known Drug Allergies)."),
    ("Family History",
     "Father: MI aged 62, died 70 (CVA). Mother: Type 2 DM, alive, aged 82.\n"
     "Brother (55): Hypertension, alive. No family history of cancer, epilepsy, or bleeding disorders."),
    ("Social History",
     "Smoking: Ex-smoker – quit 2018. 15 pack-year history (1 pack/day for 15 years).\n"
     "Alcohol: ~14 units/week (beer, 3-4 evenings). CAGE score 1/4 (not suggestive of dependence).\n"
     "Drugs: None recreational.\n"
     "Occupation: Retired. Previously drove HGVs – DVLA notification discussed.\n"
     "Housing: 2-storey house, lives with wife. Manages all ADLs independently."),
    ("Systems Review",
     "CVS: as above. Resp: mild exertional dyspnoea only, no cough or wheeze.\n"
     "GI: appetite normal, no weight loss, regular bowels, no PR bleeding.\n"
     "GU: nocturia x1. Neuro: no headache, dizziness, or focal deficit.\n"
     "MSK: mild bilateral knee OA. Skin: no rashes. Psych: mood good, no low mood or anxiety."),
]

y = 1.42
colors_left2 = [ORANGE, RED, GREEN, RGBColor(0x2E,0x86,0xAB), PURPLE]
heights2 = [0.65, 0.65, 0.65, 1.12, 1.12]
for i, (title, body) in enumerate(ex2):
    clr = colors_left2[i]
    h = heights2[i]
    add_rect(slide, 0.35, y, 2.8, h, clr)
    add_tb(slide, 0.45, y+0.1, 2.6, h-0.15, title, 10.5, bold=True, color=WHITE, wrap=True)
    add_rect(slide, 3.15, y, 9.85, h, TEAL_LIGHT if i % 2 == 0 else WHITE)
    add_tb(slide, 3.28, y+0.07, 9.6, h-0.12, body, 9.5, color=DARK_TEXT, wrap=True)
    y += h + 0.06

# =============================================================================
# SLIDE 20 – Worked Example – Clinical Summary & Plan
# =============================================================================
slide = prs.slides.add_slide(BLANK)
slide_bg(slide)
add_slide_header(slide, "Worked Example – Clinical Impression & Management Plan", "Section 09")

# Diagnosis box
add_rect(slide, 0.35, 1.35, 12.7, 0.55, NAVY)
add_tb(slide, 0.5, 1.42, 12.4, 0.42,
       "Working Diagnosis: Stable Angina Pectoris (Canadian Cardiovascular Society Class II)", 14,
       bold=True, color=WHITE)

# Risk Factors
add_rect(slide, 0.35, 1.95, 6.2, 2.3, RGBColor(0xFF,0xEE,0xEE))
add_rect(slide, 0.35, 1.95, 6.2, 0.4, RED)
add_tb(slide, 0.5, 1.99, 6.0, 0.34, "⚠  Cardiovascular Risk Factors Present", 11, bold=True, color=WHITE)
rf_items = [
    "Male sex", "Age 58", "Hypertension", "Hypercholesterolaemia",
    "Ex-smoker (15 pack-years)", "Family history of MI (father, age 62)"
]
for j, rf in enumerate(rf_items):
    yy = 2.46 + j * 0.29
    add_rect(slide, 0.55, yy+0.04, 0.16, 0.16, RED)
    add_tb(slide, 0.82, yy, 5.5, 0.28, rf, 10, color=DARK_TEXT)

# Differentials
add_rect(slide, 6.9, 1.95, 6.1, 2.3, TEAL_LIGHT)
add_rect(slide, 6.9, 1.95, 6.1, 0.4, TEAL)
add_tb(slide, 7.05, 1.99, 5.9, 0.34, "Differential Diagnoses", 11, bold=True, color=WHITE)
ddx = [
    ("1st", "Stable angina pectoris",          "Most likely – classic exertional pattern"),
    ("2nd", "Unstable angina / NSTEMI",         "No rest pain or prolonged episode – less likely now"),
    ("3rd", "Musculoskeletal chest pain",        "Less likely – tight central radiation pattern"),
    ("4th", "GORD / oesophageal spasm",          "Not relieved by antacids – lower probability"),
]
for j, (rank, dx, reason) in enumerate(ddx):
    yy = 2.46 + j * 0.42
    add_tb(slide, 7.05, yy, 0.55, 0.34, rank, 9, bold=True, color=TEAL)
    add_tb(slide, 7.65, yy, 5.2, 0.22, dx, 10, bold=True, color=NAVY)
    add_tb(slide, 7.65, yy+0.2, 5.2, 0.22, reason, 8.8, italic=True, color=DARK_TEXT)

# Investigations
add_rect(slide, 0.35, 4.35, 6.2, 2.85, WHITE)
add_rect(slide, 0.35, 4.35, 6.2, 0.4, PURPLE)
add_tb(slide, 0.5, 4.4, 6.0, 0.34, "Investigations", 11, bold=True, color=WHITE)
inv = [
    "Resting 12-lead ECG (immediate)",
    "Fasting lipid profile and HbA1c",
    "FBC, U&E, LFTs, TFTs, CRP",
    "Resting and stress echocardiogram",
    "CT Coronary Angiogram (CTCA) – 1st-line per NICE NG185",
    "Blood pressure monitoring (24h ABPM)",
]
for j, inv_item in enumerate(inv):
    add_tb(slide, 0.55, 4.86 + j*0.37, 5.85, 0.34, f"• {inv_item}", 10, color=DARK_TEXT)

# Management
add_rect(slide, 6.9, 4.35, 6.1, 2.85, WHITE)
add_rect(slide, 6.9, 4.35, 6.1, 0.4, GREEN)
add_tb(slide, 7.05, 4.4, 5.9, 0.34, "Initial Management", 11, bold=True, color=WHITE)
mgmt = [
    "GTN spray 400 micrograms sublingual PRN (acute relief)",
    "Aspirin 75mg OD (antiplatelet)",
    "Atorvastatin – continue / consider uptitration",
    "Refer to Cardiology (2-week wait)",
    "DVLA notification (Group 2 licence – must inform)",
    "Lifestyle: smoking cessation reinforcement, exercise advice, diet",
]
for j, m in enumerate(mgmt):
    add_tb(slide, 7.05, 4.86 + j*0.37, 5.85, 0.34, f"• {m}", 10, color=DARK_TEXT)

# =============================================================================
# SECTION DIVIDER 10
# =============================================================================
slide = prs.slides.add_slide(BLANK)
add_section_divider(slide, "10", "Quality Checklist & Common Pitfalls",
                    "OSCE-ready self-assessment and errors to avoid")

# =============================================================================
# SLIDE 21 – Common Pitfalls
# =============================================================================
slide = prs.slides.add_slide(BLANK)
slide_bg(slide)
add_slide_header(slide, "Common History-Taking Errors – What to Avoid", "Section 10")

add_tb(slide, 0.4, 1.28, 12.5, 0.3,
       "These are the most frequently observed errors in student OSCEs and real clinical practice:", 11,
       bold=False, italic=True, color=DARK_TEXT)

pitfalls = [
    ("Starting with closed questions", "Open questions must come first. Closed questions are for clarification only, "
     "after open exploration is complete. Jumping straight to yes/no questions is the #1 communication error."),
    ("Interrupting too early", "Research shows doctors interrupt patients on average within 18 seconds. "
     "Allow at least 1-2 minutes for the patient's uninterrupted opening account."),
    ("Ignoring ICE", "Not asking about Ideas, Concerns, and Expectations leaves the patient's real agenda unaddressed. "
     "Often the reason for the visit is a concern about a serious diagnosis, not the symptom itself."),
    ("Missing drug history / allergies", "A patient safety critical failure. Always ask for all medications, "
     "OTC/herbal, and confirm NKDA or document each allergy with the exact reaction type."),
    ("Forgetting social history", "Omitting smoking, alcohol, and occupation is extremely common. "
     "These factors profoundly affect diagnosis, management, and risk stratification."),
    ("No systems review", "Skipping the systems review means missing unrelated significant pathology. "
     "It takes 2-3 minutes and is worth significant OSCE marks."),
    ("Medical jargon", "Using terms like 'haemoptysis', 'dyspnoea', or 'HPC' with patients leads to missed information. "
     "Always translate to plain language and confirm understanding."),
    ("Poor closure", "Ending abruptly without summarising, explaining next steps, or asking if the patient "
     "has questions is unprofessional and loses OSCE marks. Always signpost your closure."),
    ("Incomplete documentation", "Missing date, time, signature, or printed name on notes is a medico-legal risk. "
     "Documenting a conclusion without supporting evidence is equally problematic."),
    ("Not screening for red flags", "Failing to ask system-specific red flag questions (weight loss, haemoptysis, etc.) "
     "is a patient safety failure and a common cause of delayed diagnosis."),
]

for i, (title, body) in enumerate(pitfalls):
    col = i % 2
    row = i // 2
    x = 0.35 + col * 6.52
    y = 1.68 + row * 1.12
    w = 6.1
    add_rect(slide, x, y, 0.38, 0.38, RED)
    add_tb(slide, x+0.02, y+0.04, 0.36, 0.28, str(i+1), 13, bold=True,
           color=WHITE, align=PP_ALIGN.CENTER)
    add_tb(slide, x+0.48, y+0.03, w-0.5, 0.3, title, 10, bold=True, color=RED)
    add_rect(slide, x, y+0.42, w, 0.62, TEAL_LIGHT if i % 2 == 0 else WHITE)
    add_tb(slide, x+0.12, y+0.46, w-0.2, 0.56, body, 8.8, color=DARK_TEXT, wrap=True)

# =============================================================================
# SLIDE 22 – Pre-OSCE Checklist
# =============================================================================
slide = prs.slides.add_slide(BLANK)
slide_bg(slide)
add_slide_header(slide, "Pre-OSCE & Pre-Clinic Checklist", "Section 10")

add_tb(slide, 0.4, 1.28, 12.5, 0.3,
       "Use this checklist to self-assess after every practice history – aim for 100% completion:", 11,
       italic=True, color=DARK_TEXT)

checklist_items = [
    ("Introduction & Consent",   ["☐  Introduced myself (name + role)", "☐  Confirmed patient identity (name + DOB)",
     "☐  Stated purpose; obtained consent", "☐  Good eye contact; open body language"]),
    ("PC & HPC",                 ["☐  Let patient open freely with open question", "☐  Full SOCRATES completed",
     "☐  Timeline established", "☐  Associated symptoms explored", "☐  Red flags screened"]),
    ("Background History",       ["☐  PMH via MJ THREADS", "☐  Drug history: all meds, doses, compliance",
     "☐  Allergies documented with reaction type", "☐  Family history (1st degree relatives)",
     "☐  Social history: smoking, alcohol, occupation, housing, ADLs"]),
    ("ICE & Communication",      ["☐  Ideas explored", "☐  Concerns addressed",
     "☐  Expectations clarified", "☐  Empathy expressed", "☐  No medical jargon used"]),
    ("Systems Review",           ["☐  All major systems briefly screened",
     "☐  Relevant red flags re-checked", "☐  Functional impact assessed"]),
    ("Closure & Documentation",  ["☐  Summarised key points accurately", "☐  Invited correction",
     "☐  Explained next steps clearly", "☐  Asked if patient had questions",
     "☐  Documentation: date, time, signature, printed name, legible"]),
]

for i, (section, items) in enumerate(checklist_items):
    col = i % 3
    row = i // 3
    x = 0.35 + col * 4.32
    y = 1.68 + row * 2.7
    w = 4.1
    clr = [NAVY, TEAL, PURPLE, ORANGE, RED, GREEN][i]
    add_rect(slide, x, y, w, 0.42, clr)
    add_tb(slide, x+0.1, y+0.07, w-0.2, 0.32, section, 10.5, bold=True, color=WHITE)
    add_rect(slide, x, y+0.42, w, len(items)*0.46, WHITE)
    for j, item in enumerate(items):
        add_tb(slide, x+0.12, y+0.48 + j*0.46, w-0.24, 0.42, item, 9.5, color=DARK_TEXT)

# =============================================================================
# SLIDE 23 – KEY TAKEAWAYS
# =============================================================================
slide = prs.slides.add_slide(BLANK)
add_rect(slide, 0, 0, 13.333, 7.5, NAVY)
add_rect(slide, 0, 0, 0.7, 7.5, TEAL)
add_rect(slide, 0, 6.5, 13.333, 1.0, RGBColor(0x0A, 0x55, 0x58))
add_rect(slide, 0.7, 3.15, 12.633, 0.07, AMBER)

add_tb(slide, 1.1, 0.4, 11, 0.65,
       "Key Takeaways", 34, bold=True, color=WHITE)
add_tb(slide, 1.1, 1.1, 11, 0.45,
       "Everything you need to remember from this presentation:", 14,
       color=TEAL_LIGHT, italic=True)

takeaways = [
    ("Structure First", "Always follow the 8-component structure: PC, HPC, PMH, DH, FH, SH, SR, Demographics"),
    ("SOCRATES", "Use it for every significant symptom – it is the gold standard and earns OSCE marks"),
    ("ICE Every Time", "Explore Ideas, Concerns and Expectations in every consultation – it defines patient-centred care"),
    ("Drug Safety", "Never skip allergies. Document the reaction type, not just 'allergy'. NKDA must be confirmed."),
    ("Red Flags", "System-specific alarm features must be actively screened for, not just passively waited for"),
    ("Document Properly", "Date, time, sign every entry. Objective language. Significant negatives. No correction fluid."),
    ("Communication = Marks", "Open questions, active listening, empathy, and signposting – these are as marked as content"),
]

for i, (title, body) in enumerate(takeaways):
    col = i % 4 if i < 4 else (i - 4) % 3
    row = 0 if i < 4 else 1
    if i < 4:
        x = 1.1 + col * 3.04
        y = 1.72
        w = 2.85
    else:
        x = 1.1 + col * 3.74
        y = 3.5
        w = 3.55

    clr = [AMBER, TEAL, GREEN, RED, PURPLE, ORANGE, RGBColor(0x2E,0x86,0xAB)][i]
    add_rect(slide, x, y, w, 1.45, RGBColor(0x0D, 0x25, 0x40))
    add_rect(slide, x, y, w, 0.08, clr)
    add_tb(slide, x+0.12, y+0.16, w-0.24, 0.3, title, 10.5, bold=True, color=clr)
    add_tb(slide, x+0.12, y+0.5, w-0.24, 0.9, body, 9, color=WHITE, wrap=True)

add_tb(slide, 1.1, 6.55, 10, 0.5,
       "Clinical History Taking: Assessment · Patient Communication · Documentation  |  Medical Education 2026",
       10, color=MID_GREY)

# =============================================================================
# SAVE
# =============================================================================
prs.save(OUTPUT)
print(f"Saved: {OUTPUT}")
print(f"Total slides: {len(prs.slides)}")
Running Command

cd /tmp/workspace/clinical-history-guide && python generate_pptx.py 2>&1

Running Command

ls -lh /tmp/workspace/clinical-history-guide/Clinical_History_Taking.pptx

Your PowerPoint presentation is ready! Here is your download link:

Clinical History Taking

Presentation · PPTX


What's inside – 33 slides, 16:9 widescreen, professional Navy/Teal/Amber design:
Slide(s)Content
1Title slide with topic tags
2Full table of contents (10 sections)
3Section divider: Structure of History
4The 8 Components – colour-coded cards
5SOCRATES framework – 8 detailed cards with sample questions
6HPC Deep Dive – timeline, red flags, functional impact, patient perspective
7Section divider: Background History
8MJ THREADS – Past Medical History mnemonic, all 9 conditions explained
9Drug History – what to ask + Allergy documentation + OSCE tips
10Family History & Social History side by side (smoking, CAGE, ADLs, travel…)
11Section divider: Systems Review
12Systems Review table – 9 systems with screening questions + red flags
13Section divider: Patient Communication
148 Core Communication Skills – with OSCE context
15ICE Framework – deep dive with sample phrases + why each matters
16Challenging Scenarios – angry, withdrawn, distressed, collateral, interpreter
17Section divider: Assessment
18OSCE Domains – marking criteria and how marks are lost
19Calgary-Cambridge Model – all 5 steps
20Section divider: Documentation
218 Documentation Principles
22SOAP Format + EHR best practice side by side
23Section divider: Red Flags
24Red Flags table – 7 systems, features, and serious diagnoses
25Section divider: Worked Example
26Worked example Part 1 – PC, HPC, ICE (Mr. Smith, chest pain)
27Worked example Part 2 – PMH, DH, FH, SH, SR
28Clinical impression, differentials, investigations, management plan
29Section divider: Quality Checklist
3010 Common Pitfalls with explanations
31Pre-OSCE 6-section checklist with tick boxes
32Key Takeaways (summary slide)
This is a shared conversation. Sign in to Orris to start your own chat.