Please create a colorful handbook pdf for me using the attached file in very easy and interesting, creative and logical way and mention only that information which is useful for me as an new young doctor in the OPD today without any kind of experience. Use this attached protocol and prepare for me my colorful handbook pdf in English.
cd /tmp/workspace/attachments/ea8c60f7-bde7-4bba-b734-215907de7950/ && ls -la && python3 -c " import pdfplumber with pdfplumber.open('DOC-20261024-WA0048.pdf') as p: print(len(p.pages)) for i,pg in enumerate(p.pages): t=pg.extract_text() or '' print('=====PAGE',i+1,len(t)) print(t) " | head -400
cd /tmp/workspace/attachments/ea8c60f7-bde7-4bba-b734-215907de7950/ && mkdir -p /tmp/workspace/htn-handbook && python3 -c " import pdfplumber with pdfplumber.open('DOC-20261024-WA0048.pdf') as p: out='' for i,pg in enumerate(p.pages): t=pg.extract_text() or '' out+='\n=====PAGE %d\n'%(i+1)+t open('/tmp/workspace/htn-handbook/protocol.txt','w').write(out) "; wc -l /tmp/workspace/htn-handbook/protocol.txt
"hypertension"[MeSH Terms] AND "antihypertensive agents"[MeSH Terms]
cd /tmp/workspace/htn-handbook && python3 -c " import pdfplumber with pdfplumber.open('/tmp/workspace/attachments/ea8c60f7-bde7-4bba-b734-215907de7950/DOC-20261024-WA0048.pdf') as p: p.pages[17].to_image(resolution=110).save('p18.png') print('ok') "; ls -la; python3 -c "import reportlab;print(reportlab.Version)"; fc-list | grep -i -E "dejavu|noto|liberation" | head
ls /usr/share/fonts/ /usr/share/fonts/*/ | head -40; python3 -c "import reportlab,os;print(os.listdir(os.path.join(os.path.dirname(reportlab.__file__),'fonts')))"
~/htn-handbook/build.py
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import mm
from reportlab.platypus import (BaseDocTemplate, PageTemplate, Frame, Paragraph, Spacer, Table,
TableStyle, PageBreak, KeepTogether, Flowable)
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_CENTER
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
FD = '/usr/share/fonts/google-droid-sans-fonts/'
pdfmetrics.registerFont(TTFont('D', FD + 'DroidSans.ttf'))
pdfmetrics.registerFont(TTFont('D-B', FD + 'DroidSans-Bold.ttf'))
pdfmetrics.registerFontFamily('D', normal='D', bold='D-B', italic='D', boldItalic='D-B')
C = lambda h: colors.HexColor(h)
NAVY, BLUE, TEAL, GREEN = C('#1F2A5A'), C('#2F80ED'), C('#0E9F8E'), C('#2E9E55')
AMBER, ORANGE, CORAL, RED = C('#F5A623'), C('#F2711C'), C('#EF5B5B'), C('#D63A3A')
PURPLE, PINK, GREY = C('#7C4DDB'), C('#E04F9A'), C('#5B6275')
STOPCOL = [BLUE, CORAL, PURPLE, GREEN, TEAL, PINK, ORANGE, BLUE, PURPLE, RED, GREEN]
W = 180 * mm
PW, PH = A4
def tint(c, k=0.88):
return colors.Color(c.red + (1 - c.red) * k, c.green + (1 - c.green) * k, c.blue + (1 - c.blue) * k)
def S(name, **kw):
base = dict(fontName='D', fontSize=9.2, leading=12.6, textColor=C('#222633'))
base.update(kw)
return ParagraphStyle(name, **base)
body = S('body')
small = S('small', fontSize=8, leading=10.5, textColor=GREY)
bul = S('bul', leftIndent=10, bulletIndent=1, spaceAfter=1.5)
bulw = S('bulw', leftIndent=10, bulletIndent=1, spaceAfter=1.5, textColor=colors.white)
cell = S('cell', fontSize=8.6, leading=11)
cellb = S('cellb', fontSize=8.6, leading=11, fontName='D-B')
cellw = S('cellw', fontSize=8.6, leading=11, fontName='D-B', textColor=colors.white)
h2 = S('h2', fontName='D-B', fontSize=12, leading=15, spaceBefore=6, spaceAfter=3)
def P(t, st=body):
return Paragraph(t, st)
def B(t, st=bul):
return Paragraph(t, st, bulletText='\u2022')
def ctitle(color):
return S('ct' + str(id(color)), fontName='D-B', fontSize=10.5, leading=13, textColor=color, spaceAfter=3)
def card(title, items, color, width=W, bg=None):
inner = [Paragraph(title, ctitle(color))]
for it in items:
if isinstance(it, str):
inner.append(B(it))
else:
inner.append(it)
t = Table([['', inner]], colWidths=[3 * mm, width - 3 * mm])
t.setStyle(TableStyle([
('BACKGROUND', (0, 0), (0, 0), color),
('BACKGROUND', (1, 0), (1, 0), bg or tint(color, 0.9)),
('LEFTPADDING', (1, 0), (1, 0), 8), ('RIGHTPADDING', (1, 0), (1, 0), 8),
('TOPPADDING', (0, 0), (-1, -1), 6), ('BOTTOMPADDING', (0, 0), (-1, -1), 6),
('LEFTPADDING', (0, 0), (0, 0), 0), ('RIGHTPADDING', (0, 0), (0, 0), 0),
('VALIGN', (0, 0), (-1, -1), 'TOP'),
]))
return t
def solid(title, items, color, width=W):
inner = [Paragraph(title, S('st', fontName='D-B', fontSize=10.5, leading=13, textColor=colors.white, spaceAfter=3))]
for it in items:
inner.append(B(it, bulw) if isinstance(it, str) else it)
t = Table([[inner]], colWidths=[width])
t.setStyle(TableStyle([('BACKGROUND', (0, 0), (-1, -1), color), ('ROUNDEDCORNERS', [6, 6, 6, 6]),
('LEFTPADDING', (0, 0), (-1, -1), 9), ('RIGHTPADDING', (0, 0), (-1, -1), 9),
('TOPPADDING', (0, 0), (-1, -1), 7), ('BOTTOMPADDING', (0, 0), (-1, -1), 7)]))
return t
def row2(a, b, gap=4 * mm):
w = (W - gap) / 2
t = Table([[a, '', b]], colWidths=[w, gap, w])
t.setStyle(TableStyle([('VALIGN', (0, 0), (-1, -1), 'TOP'), ('LEFTPADDING', (0, 0), (-1, -1), 0),
('RIGHTPADDING', (0, 0), (-1, -1), 0), ('TOPPADDING', (0, 0), (-1, -1), 0),
('BOTTOMPADDING', (0, 0), (-1, -1), 0)]))
return t
def cw(width): # half width for cards in row2
return (W - 4 * mm) / 2
def tbl(data, widths, color, header=True, zebra=True, first_bold=False, extra=None):
rows = []
for r, row in enumerate(data):
out = []
for c_, v in enumerate(row):
if isinstance(v, str):
if header and r == 0:
out.append(Paragraph(v, cellw))
elif first_bold and c_ == 0:
out.append(Paragraph(v, cellb))
else:
out.append(Paragraph(v, cell))
else:
out.append(v)
rows.append(out)
t = Table(rows, colWidths=widths, repeatRows=1 if header else 0)
st = [('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
('TOPPADDING', (0, 0), (-1, -1), 3.5), ('BOTTOMPADDING', (0, 0), (-1, -1), 3.5),
('LEFTPADDING', (0, 0), (-1, -1), 5), ('RIGHTPADDING', (0, 0), (-1, -1), 5),
('LINEBELOW', (0, 0), (-1, -1), 0.4, tint(color, 0.7))]
if header:
st.append(('BACKGROUND', (0, 0), (-1, 0), color))
if zebra:
for r in range(1 if header else 0, len(rows)):
if r % 2 == 0:
st.append(('BACKGROUND', (0, r), (-1, r), tint(color, 0.92)))
if extra:
st += extra
t.setStyle(TableStyle(st))
return t
class Banner(Flowable):
def __init__(self, num, title, sub, color):
super().__init__()
self.num, self.title, self.sub, self.color = num, title, sub, color
self.width, self.height = W, 26 * mm
def wrap(self, aw, ah):
return W, self.height
def draw(self):
c = self.canv
c.setFillColor(self.color)
c.roundRect(0, 0, W, self.height, 8, stroke=0, fill=1)
c.saveState()
c.setFillAlpha(0.16)
c.setFillColor(colors.white)
c.circle(W - 14 * mm, self.height - 4 * mm, 22 * mm, stroke=0, fill=1)
c.circle(W - 40 * mm, 2 * mm, 12 * mm, stroke=0, fill=1)
c.restoreState()
c.setFillColor(colors.white)
c.circle(16 * mm, self.height / 2, 9 * mm, stroke=0, fill=1)
c.setFillColor(self.color)
c.setFont('D-B', 20)
c.drawCentredString(16 * mm, self.height / 2 - 7, str(self.num))
c.setFillColor(colors.white)
c.setFont('D-B', 7.5)
c.drawString(30 * mm, self.height - 8 * mm, 'STOP %d' % self.num)
c.setFont('D-B', 17)
c.drawString(30 * mm, self.height - 15 * mm, self.title)
c.setFont('D', 9)
c.drawString(30 * mm, self.height - 21 * mm, self.sub)
def banner(n, title, sub):
return [Banner(n, title, sub, STOPCOL[n - 1]), Spacer(1, 4 * mm)]
def tip(text, color=AMBER, label='ROOKIE TIP'):
inner = [Paragraph('<font name="D-B" color="%s">%s</font> %s' % (color.hexval().replace('0x', '#'), label, text), body)]
t = Table([[inner]], colWidths=[W])
t.setStyle(TableStyle([('BACKGROUND', (0, 0), (-1, -1), tint(color, 0.85)),
('BOX', (0, 0), (-1, -1), 1, color), ('ROUNDEDCORNERS', [5, 5, 5, 5]),
('LEFTPADDING', (0, 0), (-1, -1), 8), ('RIGHTPADDING', (0, 0), (-1, -1), 8),
('TOPPADDING', (0, 0), (-1, -1), 5), ('BOTTOMPADDING', (0, 0), (-1, -1), 5)]))
return t
def sp(h=3):
return Spacer(1, h * mm)
# ---------------- page decorations ----------------
def draw_heart(c, cx, cy, s, col):
c.setFillColor(col)
p = c.beginPath()
p.moveTo(cx, cy - s)
p.curveTo(cx - 2.2 * s, cy - 0.1 * s, cx - 1.1 * s, cy + 1.5 * s, cx, cy + 0.6 * s)
p.curveTo(cx + 1.1 * s, cy + 1.5 * s, cx + 2.2 * s, cy - 0.1 * s, cx, cy - s)
c.drawPath(p, stroke=0, fill=1)
def cover(c, doc):
c.saveState()
c.setFillColor(NAVY)
c.rect(0, 0, PW, PH, stroke=0, fill=1)
for (x, y, r, col, a) in [(PW - 20 * mm, PH - 30 * mm, 70 * mm, BLUE, .35), (10 * mm, PH - 120 * mm, 45 * mm, PINK, .25),
(PW - 10 * mm, 90 * mm, 60 * mm, TEAL, .30), (30 * mm, 30 * mm, 40 * mm, AMBER, .25),
(PW / 2, PH - 150 * mm, 25 * mm, PURPLE, .35)]:
c.setFillColor(col)
c.setFillAlpha(a)
c.circle(x, y, r, stroke=0, fill=1)
c.setFillAlpha(1)
draw_heart(c, PW / 2, PH - 78 * mm, 26 * mm, CORAL)
# ECG-style line
c.setStrokeColor(colors.white)
c.setLineWidth(3)
y0 = PH - 78 * mm
pts = [(25, 0), (70, 0), (80, 10), (90, -8), (100, 40), (112, -45), (124, 18), (134, 0), (190, 0)]
p = c.beginPath()
x0 = PW / 2 - 95 * mm / 1.0 * 0.0
sx = (PW - 50 * mm) / 190.0
ox = 25 * mm
first = True
for (x, y) in pts:
px, py = ox + (x - 25) * sx * 1.15, y0 + y * 0.9
if first:
p.moveTo(ox, y0)
first = False
p.lineTo(px, py)
c.drawPath(p, stroke=1, fill=0)
c.setFillColor(colors.white)
c.setFont('D-B', 36)
c.drawCentredString(PW / 2, PH - 135 * mm, 'HYPERTENSION')
c.setFillColor(AMBER)
c.setFont('D-B', 36)
c.drawCentredString(PW / 2, PH - 150 * mm, 'OPD SURVIVAL GUIDE')
c.setFillColor(colors.white)
c.setFont('D', 13)
c.drawCentredString(PW / 2, PH - 163 * mm, 'A colourful first-day handbook for the new doctor')
c.drawCentredString(PW / 2, PH - 170 * mm, 'Adult patients with high blood pressure')
# chips
labels = [('Measure', BLUE), ('Confirm', CORAL), ('Grade', PURPLE), ('Risk', PINK), ('Treat', GREEN), ('Crisis', RED)]
x = 22 * mm
for t, col in labels:
w = 24 * mm
c.setFillColor(col)
c.roundRect(x, PH - 195 * mm, w, 8 * mm, 4 * mm, stroke=0, fill=1)
c.setFillColor(colors.white)
c.setFont('D-B', 9)
c.drawCentredString(x + w / 2, PH - 192.4 * mm, t)
x += w + 3.2 * mm
c.setFillColor(colors.white)
c.setFont('D', 8.5)
c.drawCentredString(PW / 2, 22 * mm, 'Built from: Clinical Protocol "Diagnostics and treatment of patients (adult population) with arterial hypertension"')
c.drawCentredString(PW / 2, 17 * mm, 'Ministry of Health of the Republic of Belarus, Decision No. 38 of 25 April 2026')
c.setFont('D', 7.5)
c.setFillColor(C('#B8C0E0'))
c.drawCentredString(PW / 2, 11 * mm, 'Study aid only. Always follow your senior doctor, your hospital rules and the current drug leaflet.')
c.restoreState()
def later(c, doc):
c.saveState()
c.setFillColor(NAVY)
c.rect(0, PH - 6 * mm, PW, 6 * mm, stroke=0, fill=1)
for i, col in enumerate([BLUE, CORAL, PURPLE, GREEN, TEAL, PINK, ORANGE]):
c.setFillColor(col)
c.rect(i * PW / 7, PH - 6 * mm, PW / 7, 1.6 * mm, stroke=0, fill=1)
c.setStrokeColor(C('#D5D9E8'))
c.line(15 * mm, 13 * mm, PW - 15 * mm, 13 * mm)
c.setFont('D', 7.5)
c.setFillColor(GREY)
c.drawString(15 * mm, 8.5 * mm, 'Hypertension OPD Survival Guide | Belarus MoH Protocol No. 38 (2026)')
c.setFont('D-B', 8.5)
c.setFillColor(NAVY)
c.drawRightString(PW - 15 * mm, 8.5 * mm, 'Page %d' % (doc.page))
c.restoreState()
# ---------------- content ----------------
E = []
E += [Spacer(1, 1), PageBreak()]
# ---- ROADMAP ----
E.append(P('<font name="D-B" size="20" color="#1F2A5A">Your OPD roadmap</font>', S('x', leading=24)))
E.append(P('Every hypertension patient walks the same 11 stops. Learn the route once and you will never feel lost.', body))
E.append(sp(3))
road = [
('Measure BP properly', 'Can I trust this number?'),
('Confirm the diagnosis', 'Is it really hypertension, or white-coat / masked?'),
('Grade the BP', 'Grade I, II or III?'),
('History, exam and tests', 'What should I ask, examine and order?'),
('Rule out secondary causes', 'Is something else driving the pressure?'),
('Risk and diagnosis wording', 'How dangerous is it? How do I write it?'),
('Treatment plan', 'Targets, lifestyle, which pills first?'),
('Drug dose cheat sheet', 'Start dose, usual dose, maximum dose'),
('Beyond BP', 'Statin, aspirin, uric acid'),
('Hypertensive crisis', 'Send home or send to hospital?'),
('Follow-up schedule', 'When to see the patient again, what to check'),
]
rows = []
for i, (a, b) in enumerate(road):
col = STOPCOL[i]
rows.append([Paragraph('<font name="D-B" size="13" color="white">%d</font>' % (i + 1), S('n', alignment=TA_CENTER, leading=16)),
[Paragraph('<font name="D-B" size="10.5" color="%s">%s</font>' % (col.hexval().replace('0x', '#'), a), body),
Paragraph(b, small)]])
t = Table(rows, colWidths=[12 * mm, W - 12 * mm - 2 * mm])
st = [('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), ('TOPPADDING', (0, 0), (-1, -1), 3.5), ('BOTTOMPADDING', (0, 0), (-1, -1), 3.5),
('LEFTPADDING', (1, 0), (1, -1), 8)]
for i in range(len(road)):
st += [('BACKGROUND', (0, i), (0, i), STOPCOL[i]), ('BACKGROUND', (1, i), (1, i), tint(STOPCOL[i], 0.9)),
('LINEBELOW', (0, i), (-1, i), 2.5, colors.white)]
t.setStyle(TableStyle(st))
E.append(t)
E.append(sp(5))
E.append(solid('The 30-second summary (if you remember nothing else)', [
'Measure BP on <b>every patient, every visit</b>, whatever they came for.',
'Hypertension = office BP <b>140/90 or more</b>, confirmed on <b>2 visits, 1-2 weeks apart</b> (or >180/110 plus organ damage on one visit).',
'Most patients start with a <b>2-drug combination</b>: ACE inhibitor or ARB, plus a calcium channel blocker or thiazide-type diuretic.',
'First target is <b>under 140/90</b>. If tolerated, aim for <b>130/80 or lower, but never below 120/70</b>.',
'Not controlled on 2 drugs? Go to <b>3 drugs</b> (ACEi/ARB + CCB + diuretic).',
'Crisis with <b>organ damage</b> (brain, heart, lungs, kidney, aorta) = <b>emergency hospital admission</b>.',
], NAVY))
E.append(PageBreak())
# ---- STOP 1 ----
E += banner(1, 'Measure BP like a pro', 'A wrong number leads to a wrong diagnosis. Get the basics right.')
E.append(tip('Measure and write down BP at <b>every</b> outpatient visit, for <b>any</b> complaint. This is how hypertension is caught early.', BLUE, 'GOLDEN RULE'))
E.append(sp(3))
E.append(row2(
card('1. Prepare the patient', [
'No food, coffee/tea, smoking or exercise for <b>30 minutes</b> before.',
'<b>Rest 5 minutes</b> seated, back supported, comfortable room.',
'Legs <b>uncrossed</b>, feet flat on the floor.',
'<b>No talking</b> during the measurement.',
'Arm resting on the table, sleeve off (no tight clothing on the arm).',
], BLUE, cw(0)),
card('2. Set up the cuff', [
'Pick the <b>right cuff size</b> for the upper-arm circumference.',
'Middle of the cuff at <b>heart level</b>.',
'Bladder centre over the <b>brachial artery</b> in the elbow crease.',
'Lower edge <b>2-3 cm above</b> the elbow crease.',
'Cuff snug and even: about <b>one finger</b> fits under it.',
], TEAL, cw(0))))
E.append(sp(3))
E.append(card('3. Take the reading', [
'<b>First visit: measure both arms.</b> Use the arm with the <b>higher</b> reading from then on.',
'SBP difference between arms <b>over 10 mmHg</b>: repeat to confirm. A lasting difference <b>over 20 mmHg</b>: look for peripheral arterial disease.',
'Use an automatic electronic or auscultatory device with an <b>upper-arm cuff</b>. Also count the <b>heart rate</b>.',
'Auscultation: <b>Korotkoff I</b> = systolic, <b>Korotkoff V</b> = diastolic. Deflate at about <b>2 mmHg per second</b>.',
'Take <b>at least 2 readings</b>, 1-2 minutes apart. If they differ by <b>more than 10 mmHg</b>, take a 3rd.',
'Record the <b>average of the last two</b>, to the nearest 2 mmHg.',
], PURPLE))
E.append(sp(3))
E.append(card('4. Check standing BP in these patients (postural hypotension hunt)', [
'Treated patients when you suspect postural hypotension. Extra important if <b>over 60 years</b>, <b>diabetes</b>, Parkinson disease or dementia.',
'Measure sitting, then <b>standing</b>. Positive if SBP falls <b>20 mmHg or more</b> or DBP falls <b>10 mmHg or more</b> within <b>3 minutes</b>.',
], ORANGE))
E.append(PageBreak())
# ---- STOP 2 ----
E += banner(2, 'Is it really hypertension?', 'Confirm before you label. One high reading is not a diagnosis.')
E.append(row2(
card('The definition', [
'Chronic, stable rise of office BP: <b>SBP 140 or more</b> and/or <b>DBP 90 or more</b>.',
], CORAL, cw(0)),
card('The confirmation rule', [
'<b>At least 2 readings</b> at <b>at least 2 visits</b>, <b>1-2 weeks</b> apart.',
'<b>Shortcut:</b> one visit is enough if BP is <b>over 180/110</b> AND there are signs of organ damage (LVH, vascular or kidney damage).',
], RED, cw(0))))
E.append(sp(3))
E.append(P('Normal and abnormal BP depend on <i>where</i> you measure it', h2))
E.append(tbl([
['Method', 'Hypertension if', 'Normal if'],
['<b>Office</b> BP', '140/90 or more', 'under 140/90'],
['<b>Home</b> BP (7-day average)', 'over 135/85 (matches office 140/90)', 'under 135/85'],
['<b>ABPM</b> daytime average', '135/85 or more', 'under 135/85'],
['<b>ABPM</b> night-time average', '120/70 or more', 'under 120/70'],
['<b>ABPM</b> 24-hour average', '130/80 or more', 'under 130/80'],
], [60 * mm, 75 * mm, 45 * mm], CORAL))
E.append(sp(3))
E.append(row2(
card('White-coat hypertension (IOAH)', [
'<b>Office</b> BP is high at several visits (over 140/90).',
'<b>ABPM</b> (under 130/80) or <b>home</b> BP (under 135/85) is normal.',
'The clinic itself raises the BP.',
], AMBER, cw(0)),
card('Masked hypertension (IAAH)', [
'<b>Office</b> BP is normal at several visits (under 140/90).',
'<b>ABPM</b> (over 130/80) or <b>home</b> BP (over 135/85) is high.',
'Looks fine in clinic, high in real life.',
], PURPLE, cw(0))))
E.append(sp(2))
E.append(tip('If white-coat or masked hypertension comes <b>with organ damage</b>, it is diagnosed as hypertension (I10).', CORAL, 'REMEMBER'))
E.append(sp(3))
E.append(P('Which patients need 24-hour ABPM? (ask a senior if unsure)', h2))
E.append(row2(
card('Suspect white-coat or masked', [
'Grade I hypertension, no organ damage, low risk.',
'Big difference (over 20 mmHg) between office readings, or between home and office.',
'High-normal office BP with organ damage or high risk; suspected high night-time BP.',
'Suspected white-coat effect in a treated patient.',
], CORAL, cw(0)),
card('Other reasons', [
'Dizziness, falls or fainting (suspected hypotension or autonomic dysfunction).',
'Sorting <b>true</b> from <b>pseudo-resistant</b> hypertension.',
'Glaucoma; sleep apnoea, diabetes, CKD or transplant (night BP).',
'High-risk groups and pregnancy; symptomatic hypotension in over 60s (over-treatment, postural or post-meal drop).',
], TEAL, cw(0))))
E.append(PageBreak())
E.append(row2(
card('ABPM: how it works', [
'Runs <b>24 hours</b>: a reading every <b>15-30 min</b> awake, every <b>30-60 min</b> asleep.',
'Needs at least <b>70%</b> valid readings to interpret.',
'Repeat the test if under <b>20</b> awake readings or under <b>7</b> night readings.',
], BLUE, cw(0)),
card('Home BP: teach the patient first', [
'<b>7 days</b>, <b>twice daily</b> (morning and evening), at the same time, <b>before meals and before pills</b>.',
'At least 2 readings, 1 minute or more apart; use the average. <b>Ignore day 1.</b>',
'Automatic <b>upper-arm oscillometric</b> device. Patient writes values in a diary if the device has no memory.',
], GREEN, cw(0))))
E.append(sp(2))
E.append(P('Home BP is also used to: find white-coat/masked hypertension, confirm resistant or uncontrolled hypertension, adjust doses, follow high and very high risk patients, and check treatment response in the <b>7 days before the next visit</b>.', small))
E.append(sp(4))
# ---- STOP 3 ----
E += banner(3, 'Grade the blood pressure', 'Use the office BP. Colour code it in your head.')
grade_rows = [
['Category', 'Systolic (mmHg)', 'Diastolic (mmHg)'],
['Optimal', 'under 120', 'under 80'],
['Normal', '120-129', '80-84'],
['High normal', '130-139', '85-89'],
['<b>Grade I</b> hypertension', '140-159', '90-99'],
['<b>Grade II</b> hypertension', '160-179', '100-109'],
['<b>Grade III</b> hypertension', '180 or more', '110 or more'],
['Isolated systolic (ISH)', '140 or more', 'under 90'],
['Isolated diastolic (IDH)', 'under 140', '90 or more'],
]
gcols = [C('#3BA55D'), C('#7BC96F'), C('#C8D84A'), AMBER, ORANGE, RED, PURPLE, PINK]
extra = []
for i, gc in enumerate(gcols):
extra.append(('BACKGROUND', (0, i + 1), (0, i + 1), tint(gc, 0.55)))
E.append(tbl(grade_rows, [70 * mm, 55 * mm, 55 * mm], NAVY, zebra=False, extra=extra))
E.append(sp(3))
E.append(row2(
card('Golden rules of grading', [
'If SBP and DBP fall in <b>different categories</b>, use the <b>higher</b> one.',
'Grading is most accurate in <b>new</b> patients or patients <b>not yet on treatment</b>.',
'ISH is graded I, II or III by the <b>systolic</b> value. IDH by the <b>diastolic</b> value.',
], PURPLE, cw(0)),
card('Quick example', [
'<b>BP 168/92</b>: SBP says Grade II, DBP says Grade I. The answer is <b>Grade II</b>.',
'<b>BP 150/110</b>: SBP says Grade I, DBP says Grade III. The answer is <b>Grade III</b>.',
], PINK, cw(0))))
E.append(PageBreak())
# ---- STOP 4 ----
E += banner(4, 'History, exam and tests', 'What to ask, what to measure, what to order on day one.')
E.append(P('Ask these questions', h2))
E.append(row2(
card('The BP story', [
'When was hypertension found? How long? Past BP levels?',
'Drugs taken before and now (BP pills and others).',
'Past <b>side effects or intolerance</b> to BP drugs.',
'<b>Adherence</b>: does the patient really take the pills?',
'Symptoms of hypertension and of other diseases.',
], GREEN, cw(0)),
card('Risk factors and lifestyle', [
'Smoking, alcohol, <b>salt</b> intake, diet.',
'Low physical activity, overweight/obesity.',
'Sleep problems; anxiety, depression, social isolation.',
'Dyslipidaemia.',
], ORANGE, cw(0))))
E.append(sp(2))
E.append(row2(
card('Other diseases (now or past)', [
'Heart attack, heart failure, stroke, TIA.',
'Diabetes, chronic kidney disease (CKD).',
], RED, cw(0)),
card('Family history', [
'Hypertension, <b>early</b> cardiovascular events, familial hypercholesterolaemia, diabetes, CKD.',
], PURPLE, cw(0))))
E.append(sp(2))
E.append(P('Examine and measure', h2))
E.append(B('BP (both arms, see Stop 1) and <b>heart rate</b>.'))
E.append(B('Look for <b>clinical signs of target organ damage</b> (heart, brain, kidneys, vessels, eyes).'))
E.append(B('<b>Weight, height, BMI and waist circumference.</b> Obesity: BMI over 30, or waist over 102 cm (men) / 88 cm (women).'))
E.append(sp(2))
E.append(P('Tests for every new patient (the "must do" list)', h2))
E.append(tbl([
['Group', 'What to order'],
['<b>Blood</b>', 'Full blood count (Hb, haematocrit, WBC, platelets) | <b>Fasting glucose</b> | <b>Lipids</b>: total cholesterol, LDL, HDL, triglycerides | <b>Potassium, sodium, uric acid, creatinine</b> and eGFR (CKD-EPI) | <b>Liver</b>: bilirubin, ALT'],
['<b>Urine</b>', 'General urine analysis with sediment | <b>Albuminuria</b> (albumin/creatinine ratio, or 24 h). If unavailable, qualitative protein test'],
['<b>Heart</b>', '<b>12-lead ECG</b> | <b>Echocardiography</b>'],
], [28 * mm, 152 * mm], GREEN))
E.append(sp(3))
E.append(P('Order when there is a reason (or treatment is failing)', h2))
E.append(tbl([
['Test or referral', 'When'],
['Fundoscopy', 'Grade II-III hypertension, or hypertension + diabetes (hypertensive retinopathy)'],
['Neurologist / endocrinologist', 'To exclude secondary hypertension; neurological complications'],
['24-h ECG (Holter)', 'Suspected rhythm or conduction problems'],
['Bicycle ergometry or treadmill', 'To exclude coronary heart disease'],
['Carotid (pre-cerebral) ultrasound', 'Neck bruit, TIA/stroke history, atherosclerosis elsewhere'],
['Kidney ultrasound + renal artery flow', 'Low eGFR, albuminuria, or suspected secondary hypertension'],
['CT / MRI brain', 'Neurological symptoms'],
['CT or MR angiography of renal arteries', 'Suspected renovascular hypertension'],
['Cardiac surgeon / vascular surgeon', 'Highest-level centres, selected cases'],
], [70 * mm, 110 * mm], TEAL))
E.append(PageBreak())
# ---- STOP 5 ----
E += banner(5, 'Rule out secondary causes', 'Primary (essential) hypertension has no known cause. Secondary hypertension does, and may be fixable.')
E.append(tip('Secondary hypertension is caused by kidney, big-artery, hormonal or other <b>potentially reversible</b> problems. Think of it when BP is hard to control, or when kidney tests, hormones or neurology look abnormal. Ask the endocrinologist / neurologist and order a kidney ultrasound with renal artery flow.', TEAL, 'WHY IT MATTERS'))
E.append(sp(3))
chips = [
('Kidney (nephrogenic)', ['Chronic glomerulonephritis', 'Chronic pyelonephritis', 'Diabetic nephropathy', 'Polycystic kidney disease', 'Vasculitis, amyloidosis, TB', 'Kidney tumours, injuries', 'Pregnancy nephropathy', 'Congenital kidney anomalies'], BLUE),
('Renal arteries (vasorenal)', ['Atherosclerosis', 'Fibromuscular dysplasia', 'Aortoarteritis', 'Compression by haematoma or tumour', 'Congenital: atresia, aneurysm, fistula'], PURPLE),
('Hormonal (endocrine)', ['Primary aldosteronism', 'Cushing syndrome / disease', 'Phaeochromocytoma', 'Thyroid: hypo- or hyperthyroidism', 'Hyperparathyroidism', 'Acromegaly'], ORANGE),
('Big arteries', ['Atherosclerosis', 'Coarctation of the aorta', 'Aorta / brachiocephalic stenosis'], RED),
('Nervous system and sleep', ['CNS tumour, trauma, encephalitis', 'Raised intracranial pressure', '<b>Obstructive sleep apnoea</b>', 'Lead poisoning, acute porphyria'], GREEN),
]
cards = [card(t, items, col, cw(0)) for (t, items, col) in chips]
for i in range(0, len(cards), 2):
a = cards[i]
b = cards[i + 1] if i + 1 < len(cards) else ''
E.append(row2(a, b))
E.append(sp(2))
E.append(solid('Drugs and substances that raise BP (always check the medicine list!)', [
'Hormonal contraceptives | Corticosteroids | Mineralocorticoids | Sympathomimetics | Cocaine',
'<b>NSAIDs</b> | Cyclosporine | Erythropoietin | Foods with tyramine-type amines or MAO inhibitors',
'Cancer treatment: EGFR inhibitors, alkylating agents; radiotherapy to the neck',
], PINK))
E.append(PageBreak())
# ---- STOP 6 ----
E += banner(6, 'Risk and diagnosis wording', 'Hypertension is more than a number. Risk decides how hard you treat.')
E.append(P('Total cardiovascular risk = chance of CV death, non-fatal heart attack or non-fatal stroke within <b>10 years</b>. Count three things:', body))
E.append(sp(2))
E.append(row2(
card('A. Risk factors', [
'Male sex',
'Age: men over 55, women over 65',
'Smoking (current or past)',
'Cholesterol over 4.9, LDL over 3.0, HDL under 1.0 (M) / 1.2 (F), TG over 1.7 mmol/L',
'Fasting glucose 6.1-6.9 mmol/L (5.6-6.0 capillary)',
'Post-meal glucose over 7.8 and under 11.1',
'BMI over 30; waist over 102 cm (M) / 88 cm (F)',
'Uric acid over 360 \u00b5mol/L',
'Early CVD in family (men under 55, women under 65)',
'Menopause before 45',
'Resting heart rate over 80',
], ORANGE, cw(0)),
[card('B. Organ damage (no symptoms)', [
'Pulse pressure over 60 mmHg',
'LVH on ECG (Sokolov-Lyon over 35 mm, or RaVL over 11 mm, Cornell product over 2440)',
'LVH on echo (LV mass index: men over 115, women over 95 g/m2)',
'Plaque on imaging (under 50% stenosis)',
'CKD stage 3 (eGFR 30-59)',
'Albuminuria 30-300 mg/24 h',
'Severe retinopathy (haemorrhages, exudates, papilloedema)',
], PURPLE, cw(0)), sp(2),
card('C. Associated clinical conditions', [
'Stroke, TIA, brain haemorrhage',
'Coronary disease: heart attack, angina, stent/bypass',
'Atrial fibrillation',
'Heart failure (stage IIA or more)',
'Plaque with over 50% stenosis',
'CKD stage 4-5 (eGFR under 30)',
'Diabetes (counts here too)',
], RED, cw(0))]))
E.append(sp(3))
E.append(P('Risk matrix: find your patient', h2))
R = {'n': ('No risk', C('#BFE3C5'), colors.black), 'l': ('Low', C('#7BC96F'), colors.black),
'm': ('Medium', AMBER, colors.black), 'h': ('High', ORANGE, colors.white), 'v': ('Very high', RED, colors.white)}
mat = [
('No other risk factors', 'nlmh'),
('1-2 risk factors', 'lmhh'),
('3 or more risk factors', 'mhhv'),
('Organ damage or CKD stage 3', 'hhhv'),
('Associated condition, CKD 4+, or diabetes', 'vvvv'),
]
hdr = ['', 'High normal<br/>130-139 / 85-89', 'Grade I<br/>140-159 / 90-99', 'Grade II<br/>160-179 / 100-109', 'Grade III<br/>180+ / 110+']
data = [[Paragraph(h, S('mh', fontName='D-B', fontSize=8, leading=10, textColor=colors.white, alignment=TA_CENTER)) for h in hdr]]
ex = [('BACKGROUND', (0, 0), (-1, 0), NAVY)]
for r, (lab, codes) in enumerate(mat):
row = [Paragraph(lab, cellb)]
for c_, ch in enumerate(codes):
name, bg, fg = R[ch]
row.append(Paragraph(name, S('mc', fontName='D-B', fontSize=9, leading=11, textColor=fg, alignment=TA_CENTER)))
ex.append(('BACKGROUND', (c_ + 1, r + 1), (c_ + 1, r + 1), bg))
ex.append(('BACKGROUND', (0, r + 1), (0, r + 1), C('#EEF0F8')))
data.append(row)
mt = Table(data, colWidths=[52 * mm, 32 * mm, 32 * mm, 32 * mm, 32 * mm])
mt.setStyle(TableStyle(ex + [('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), ('GRID', (0, 0), (-1, -1), 1.5, colors.white),
('TOPPADDING', (0, 0), (-1, -1), 5), ('BOTTOMPADDING', (0, 0), (-1, -1), 5)]))
E.append(mt)
E.append(PageBreak())
E.append(P('How to write the diagnosis', h2))
E.append(P('Always include, in this order:', body))
E.append(sp(1))
steps = [('1', 'Hypertension grade', BLUE), ('2', 'Risk group (low, medium, high, very high)', PURPLE), ('3', 'Main risk factors (obesity, dyslipidaemia, high fasting glucose)', ORANGE),
('4', 'Associated clinical conditions and diabetes', RED)]
rows = [[Paragraph('<font name="D-B" color="white" size="12">%s</font>' % n, S('c', alignment=TA_CENTER, leading=14)), Paragraph(t, body)] for n, t, c_ in steps]
t = Table(rows, colWidths=[10 * mm, W - 10 * mm])
t.setStyle(TableStyle([('BACKGROUND', (0, i), (0, i), steps[i][2]) for i in range(4)] +
[('BACKGROUND', (1, i), (1, i), tint(steps[i][2], 0.9)) for i in range(4)] +
[('LINEBELOW', (0, 0), (-1, -1), 2, colors.white), ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
('TOPPADDING', (0, 0), (-1, -1), 4), ('BOTTOMPADDING', (0, 0), (-1, -1), 4), ('LEFTPADDING', (1, 0), (1, -1), 8)]))
E.append(t)
E.append(sp(3))
E.append(P('Examples from the protocol', h2))
for ex_t, col in [
('Arterial hypertension, <b>grade II</b>. <b>Risk 3</b>. Obesity grade II (BMI 36.0).', GREEN),
('Arterial hypertension, <b>grade I</b>. <b>Risk 4</b>. Uncomplicated hypertensive crisis, stopped (date). Type 2 diabetes, compensated. Hypertriglyceridaemia (TG 6.8 mmol/L).', ORANGE),
('Arterial hypertension, <b>grade III</b>. <b>Risk 4</b>. Pre-stage heart failure. Dyslipidaemia (LDL 4.8, TG 2.4 mmol/L).', PURPLE),
('IHD: post-infarction cardiosclerosis (date). Coronary stenoses, PCI (date). Arterial hypertension grade II. <b>Risk 4</b>. CHF with reduced EF (38%), stage 1, FC II.', RED),
]:
E.append(card('', [P(ex_t)], col))
E.append(sp(1.5))
E.append(sp(1))
E.append(tip('In the protocol examples, <b>Risk 3 = high</b> and <b>Risk 4 = very high</b> (this numbering is implied by the examples, not spelled out). Ask your senior which wording your hospital uses.', AMBER, 'NOTE'))
E.append(PageBreak())
# ---- STOP 7 ----
E += banner(7, 'The treatment plan', 'Goal: fewer heart attacks, strokes and kidney failure, not just a nicer number.')
E.append(row2(
card('What we are trying to do', [
'Fix <b>all modifiable risk factors</b>.',
'Prevent or slow <b>organ damage</b>.',
'Find and treat <b>IHD, CKD, diabetes</b> and other CVD.',
'Bring BP to <b>target</b>.',
], GREEN, cw(0)),
solid('BP targets', [
'<font size="16"><b>Step 1: under 140/90</b></font>',
'<font size="13"><b>Step 2: 130/80 or lower</b></font> if the patient tolerates it',
'<b>But never below 120/70</b>',
], NAVY, cw(0))))
E.append(sp(3))
E.append(card('Lifestyle: for EVERY patient, every grade', [
'<b>Stop smoking</b> (also e-cigarettes) and avoid passive smoke.',
'<b>Weight:</b> aim for BMI 20-25. Waist under 94 cm (men) / under 80 cm (women).',
'<b>Salt:</b> under 5 g per day.',
'<b>Alcohol:</b> under 10 standard drinks a week (1 drink = 10 g ethanol). Best is none.',
'<b>Diet:</b> low saturated fat, more whole grains and fibre, vegetables 200 g or more and fruit 200 g or more daily, less sugary drinks, less red meat.',
'<b>Exercise:</b> moderate aerobic 150 min or more per week, or intense 75 min or more, or a mix.',
'Refer to the <b>"School for Patients with Hypertension"</b> where available.',
], TEAL))
E.append(sp(3))
E.append(P('The drug ladder', h2))
lad = [
('STEP 1', 'Start with a two-drug combination', 'ACE inhibitor <b>or</b> ARB + long-acting CCB <b>or</b> thiazide / thiazide-like diuretic. Prefer one <b>fixed-dose pill</b>.', BLUE),
('STEP 2', 'Still above 140/90 (office)?', 'Move to <b>three drugs</b>: ACEi/ARB + long-acting CCB + thiazide-type diuretic, preferably in one pill.', PURPLE),
('STEP 3', 'Still not controlled?', 'This may be <b>resistant hypertension</b>. Confirm with ABPM / home BP, then see the next page.', RED),
]
rows = []
for a, b, c_, col in lad:
rows.append([Paragraph('<font name="D-B" color="white" size="10">%s</font>' % a, S('l', alignment=TA_CENTER, leading=12)),
[Paragraph('<font name="D-B" size="10.5">%s</font>' % b, body), Paragraph(c_, body)]])
t = Table(rows, colWidths=[22 * mm, W - 22 * mm])
t.setStyle(TableStyle([('BACKGROUND', (0, i), (0, i), lad[i][3]) for i in range(3)] +
[('BACKGROUND', (1, i), (1, i), tint(lad[i][3], 0.9)) for i in range(3)] +
[('LINEBELOW', (0, 0), (-1, -1), 2.5, colors.white), ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
('TOPPADDING', (0, 0), (-1, -1), 5), ('BOTTOMPADDING', (0, 0), (-1, -1), 5), ('LEFTPADDING', (1, 0), (1, -1), 8)]))
E.append(t)
E.append(sp(3))
E.append(row2(
card('Single drug is OK only if...', [
'Low-risk <b>grade I</b> (BP under 150/95), or',
'Age <b>80 or older</b>, or',
'<b>Frail</b> older patient (senile asthenia).',
], AMBER, cw(0)),
card('Add a beta-blocker when...', [
'Angina, previous heart attack, heart failure.',
'To control heart rate (resting HR over 80).',
'Always <b>together with</b> the main drugs, not as the usual first choice.',
], ORANGE, cw(0))))
E.append(sp(2))
E.append(P('<b>Main classes:</b> ACE inhibitors, ARBs, long-acting calcium channel blockers, thiazide / thiazide-like diuretics, beta-blockers. <b>Extra classes</b> (when control is poor or main drugs are not tolerated): mineralocorticoid antagonists, loop diuretics, alpha-blockers, centrally acting drugs.', body))
E.append(PageBreak())
E.append(P('Resistant hypertension: the 5-step rescue', h2))
E.append(P('<b>Definition:</b> BP stays at 140/90 or above despite lifestyle measures and optimal or maximum tolerated doses of <b>3 or more drugs including a diuretic</b>, in an adherent patient, with secondary causes excluded.', body))
E.append(sp(2))
res = [
('1', 'Confirm it', 'Use <b>ABPM and/or home BP</b>. Exclude pseudo-resistance: poor adherence, wrong therapy, wrong BP technique, severe arterial calcification, BP-raising drugs, lifestyle factors, sleep apnoea.', BLUE),
('2', 'Optimise the triple', 'ACEi/ARB + thiazide-type diuretic + long-acting CCB, ideally fixed-dose.', PURPLE),
('3', 'Strengthen the diuretic', 'Increase the dose, or switch hydrochlorothiazide to <b>chlorthalidone or indapamide</b>, and/or add <b>torasemide 10 mg/day</b> (especially CKD or fluid retention).', TEAL),
('4', 'Add spironolactone', '<b>25-50 mg/day</b>, checking electrolytes and eGFR. If not tolerated: eplerenone (off-label, needs a medical council decision).', ORANGE),
('5', 'Next options', 'Alpha-blocker, beta-blocker, or central drug (moxonidine, clonidine). Sacubitril/valsartan may be combined with other BP drugs <b>except ACEi/ARB</b> (off-label, council decision). Selected patients: <b>renal denervation</b> in specialised cardiac centres.', RED),
]
rows = []
for n, a, b, col in res:
rows.append([Paragraph('<font name="D-B" color="white" size="13">%s</font>' % n, S('r', alignment=TA_CENTER, leading=15)),
[Paragraph('<font name="D-B" size="10" color="%s">%s</font>' % (col.hexval().replace('0x', '#'), a), body), Paragraph(b, body)]])
t = Table(rows, colWidths=[11 * mm, W - 11 * mm])
t.setStyle(TableStyle([('BACKGROUND', (0, i), (0, i), res[i][3]) for i in range(5)] +
[('BACKGROUND', (1, i), (1, i), tint(res[i][3], 0.9)) for i in range(5)] +
[('LINEBELOW', (0, 0), (-1, -1), 2.5, colors.white), ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
('TOPPADDING', (0, 0), (-1, -1), 5), ('BOTTOMPADDING', (0, 0), (-1, -1), 5), ('LEFTPADDING', (1, 0), (1, -1), 8)]))
E.append(t)
E.append(sp(4))
E.append(tip('Off-label use (a drug used outside its leaflet) needs a <b>medical consultation</b> decision. Never start it alone.', RED, 'SAFETY'))
E.append(sp(4))
# ---- STOP 8 ----
E += banner(8, 'Drug dose cheat sheet', 'All doses are oral, once a day unless stated. Doses are mg per day.')
W4 = [45 * mm, 45 * mm, 45 * mm, 45 * mm]
def drugtab(title, col, rows_, note=None):
data = [['Drug', 'Start', 'Usual', 'Max']] + rows_
t = tbl(data, [46 * mm, 38 * mm, 52 * mm, 44 * mm], col, first_bold=True)
items = [Paragraph('<font name="D-B" size="10.5" color="%s">%s</font>' % (col.hexval().replace('0x', '#'), title), body), Spacer(1, 1.5 * mm), t]
if note:
items += [Spacer(1, 1 * mm), Paragraph(note, small)]
items.append(Spacer(1, 3 * mm))
return KeepTogether(items)
E.append(drugtab('ACE inhibitors (first line)', BLUE, [
['Lisinopril', '2.5-5', '20', '80'], ['Perindopril', '2-2.5', '4-8 (to 10)', '8-10'], ['Ramipril', '2.5', '5-10', '10'],
['Fosinopril', '10', '10-20', '20-40'], ['Enalapril', '2.5-5', '20', '40']]))
E.append(drugtab('ARBs (first line)', PURPLE, [
['Azilsartan', '40', '40', '80'], ['Valsartan', '80', '80-160', '320'], ['Irbesartan', '75', '150', '300'],
['Candesartan', '8', '8-16', '32'], ['Losartan', '50', '50', '100'], ['Olmesartan', '10', '20', '40'], ['Telmisartan', '40', '40', '80']]))
E.append(drugtab('Long-acting calcium channel blockers (first line)', TEAL, [
['Amlodipine', '2.5', '5', '10'], ['Lercanidipine', '10', '10', '20'], ['Lacidipine', '2', '4-6', '6'],
['Nifedipine (retard)', '20-30', '20-30', '60'], ['Verapamil (slow-release)', '180-240', '240 once, or 180 once-twice', '360-480'],
['Diltiazem (retard)', '180', '180 once-twice', '360']]))
E.append(PageBreak())
E.append(drugtab('Diuretics', ORANGE, [
['Indapamide SR', '1.5', '1.5', '1.5 (2.5 for the 2.5 mg form)'], ['Hydrochlorothiazide', '12.5-25', '12.5-25', '25'],
['Chlorthalidone', '12.5-25', '12.5-50', '50'], ['Spironolactone', '25', '25-50', '50'],
['Torasemide', '2.5', '2.5-5', 'up to 40 (oedema)'], ['Eplerenone (off-label)', '25', '25-50', '50']]))
E.append(drugtab('Beta-blockers (for the special indications)', GREEN, [
['Atenolol', '25', '50', '100'], ['Bisoprolol', '2.5', '5', '20'], ['Carvedilol', '6.25-12.5', '25', '50'],
['Metoprolol', '25', '25-100, 1-2 times/day', '200'], ['Nebivolol', '2.5', '5', '10']]))
E.append(drugtab('Alpha-blockers and centrally acting drugs (extra classes)', PINK, [
['Doxazosin', '1', '2-8', '16'], ['Terazosin', '1', '2-10', '20'],
['Clonidine', '0.0375-0.075, 2-3 times/day', '0.075, 2-3 times/day', '0.9'],
['Moxonidine', '0.2', '0.4-0.6', '0.4-0.6'],
['Methyldopa', '250, 2-3 times/day', '500-2000 in 2-4 doses', '3000']],
note='<b>Methyldopa: only in pregnancy and breast-feeding.</b>'))
E.append(drugtab('Special', RED, [
['Sacubitril/valsartan (off-label, council decision)', '200 mg (sacubitril + valsartan total) once', '200', '400']],
note='Use with other BP drugs <b>except ACE inhibitors and ARBs</b>.'))
E.append(tip('Check the <b>drug leaflet</b> and your hospital formulary before prescribing. Doses are reduced in kidney or liver problems and in the elderly.', RED, 'ALWAYS'))
E.append(PageBreak())
# ---- STOP 9 ----
E += banner(9, 'Beyond BP: protect the whole patient', 'Statin, aspirin and uric acid depend on risk and other diseases.')
E.append(P('Cholesterol-lowering (statins are the drug of choice)', h2))
E.append(P('Other options: ezetimibe, fibrates, PCSK9 inhibitors, or combinations. Aim to lower LDL by <b>more than 50%</b> from baseline AND reach these targets:', body))
E.append(sp(2))
ld = [
('Very high risk (SCORE)', 'LDL under 1.4 mmol/L', RED),
('CVD patient with a second event (heart attack/unstable angina, revascularisation, stroke, TIA) within 2 years', 'LDL under 1.0 mmol/L', C('#8E1B1B')),
('High risk (SCORE)', 'LDL under 1.8 mmol/L', ORANGE),
('Moderate risk (SCORE)', 'LDL under 2.6 mmol/L', AMBER),
('Low risk (SCORE)', 'LDL under 3.0 mmol/L', GREEN),
]
rows = [[Paragraph(a, cellb), Paragraph('<font color="white">%s</font>' % b, cellw)] for a, b, c_ in ld]
t = Table(rows, colWidths=[115 * mm, 65 * mm])
t.setStyle(TableStyle([('BACKGROUND', (0, i), (0, i), tint(ld[i][2], 0.85)) for i in range(5)] +
[('BACKGROUND', (1, i), (1, i), ld[i][2]) for i in range(5)] +
[('LINEBELOW', (0, 0), (-1, -1), 2, colors.white), ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
('TOPPADDING', (0, 0), (-1, -1), 5), ('BOTTOMPADDING', (0, 0), (-1, -1), 5), ('LEFTPADDING', (0, 0), (-1, -1), 8)]))
E.append(t)
E.append(sp(4))
E.append(row2(
card('Antiplatelet (aspirin)', [
'Give <b>low-dose aspirin 75-100 mg/day</b> if the patient has had a <b>heart attack, ischaemic stroke/TIA</b> or has <b>peripheral artery disease</b> (atherosclerotic).',
'Aspirin intolerant: a <b>P2Y12 inhibitor</b>.',
'<b>Do NOT give aspirin for primary prevention</b> if there is no atherosclerotic disease. Benefit is unproven.',
], RED, cw(0)),
card('Uric acid-lowering', [
'Treat <b>hypertension + hyperuricaemia</b>.',
'Target: under <b>300 \u00b5mol/L</b> if very high/high risk with 2 or more of: hypertension, diabetes, dyslipidaemia, recent stroke/MI, CKD. Otherwise under <b>360</b>.',
'<b>Allopurinol</b>; febuxostat if allopurinol is not tolerated. Check uric acid <b>twice a year</b>.',
'Add a low-purine diet and regular exercise.',
], PURPLE, cw(0))))
E.append(PageBreak())
# ---- STOP 10 ----
E += banner(10, 'Hypertensive crisis', 'A sudden, big BP rise with symptoms. The question is: is any organ being damaged right now?')
E.append(row2(
solid('UNCOMPLICATED crisis', [
'High BP + symptoms, <b>no acute organ damage</b>.',
'Lower <b>gradually</b>: no more than <b>25% in the first 2 hours</b>, then normal range within <b>24 hours</b>.',
'Afterwards: ambulatory BP monitoring, and restart or <b>intensify</b> regular therapy.',
], ORANGE, cw(0)),
solid('COMPLICATED crisis', [
'High BP + <b>acute, life-threatening organ damage</b>.',
'<b>Emergency hospitalisation.</b>',
'IV drugs; speed and size of the BP fall depend on the emergency.',
], RED, cw(0))))
E.append(sp(3))
E.append(card('Uncomplicated crisis: choose ONE drug', [
'<b>Captopril</b> 25-50 mg orally or under the tongue',
'<b>Nifedipine</b> (10 mg tablet) 10 mg orally',
'<b>Propranolol</b> 40 mg orally',
'<b>Glyceryl trinitrate</b> 0.5 mg under the tongue',
'<b>Magnesium sulfate</b> 1-5 g IV slowly over 5 minutes',
'<b>Furosemide</b> 20-60 mg IV',
'<b>Bendazole</b> 30-40 mg IV',
'<b>Urapidil</b> 10-50 mg IV slowly under BP control',
'<b>If these fail:</b> moxonidine 0.2-0.4 mg orally, or clonidine 0.075-0.15 mg orally',
], ORANGE))
E.append(sp(3))
E.append(card('Send to hospital (emergency admission) when:', [
'The crisis is <b>complicated</b>, or',
'The crisis is uncomplicated but <b>symptoms get worse</b> and cannot be reversed in the outpatient clinic.',
], RED))
E.append(sp(3))
E.append(P('Emergency conditions: BP targets (hospital team)', h2))
E.append(P('Mean BP = (SBP - DBP) / 3 + DBP. IV drugs mentioned in the protocol: urapidil, sodium nitroprusside, glyceryl trinitrate, metoprolol, furosemide, enalapril, diazepam (for convulsions), verapamil.', small))
E.append(sp(1.5))
E.append(tbl([
['Emergency', 'BP target'],
['Malignant hypertension with acute renal failure', 'Lower mean BP by <b>20-25%</b> over several hours'],
['Hypertensive encephalopathy', 'Lower mean BP by no more than <b>20-25%</b> over several hours'],
['Acute coronary syndrome', 'SBP quickly under <b>140</b>'],
['Acute pulmonary oedema', 'SBP quickly under <b>140</b>'],
['Acute aortic dissection / aortic rupture', 'SBP under <b>120</b> and heart rate under <b>60</b>'],
['Ischaemic stroke', 'No lowering if under 220/120 in the first 24 h. With thrombolysis: keep under <b>180/105</b> for 24 h or more'],
['Intracerebral haemorrhage', 'Within 6 h: to about 140/90, but not more than 60 mmHg SBP fall. After 6 h with SBP over 220: slow lowering to under 180 over hours'],
], [70 * mm, 110 * mm], RED))
E.append(PageBreak())
# ---- STOP 11 ----
E += banner(11, 'Follow-up schedule', 'Who gets seen when, and what to check each time. Done by GPs / therapists in the polyclinic.')
E.append(card('Every visit (all groups)', [
'Complaints and history; are they <b>really</b> taking the BP pills (and other drugs)?',
'Review <b>home BP readings</b>; physical exam; BMI and waist.',
'Adjust treatment if needed. <b>First review: 3 months</b> after starting the first drug.',
], BLUE))
E.append(sp(3))
fu = [
['', 'Group 1<br/>Grade I, NO organ damage', 'Group 2<br/>Grade I-III WITH organ damage, no associated conditions', 'Group 3<br/>Associated conditions, diabetes, resistant or secondary HTN'],
['<b>After 3 months</b>', 'Visit as above', 'Visit as above', 'Visit as above'],
['<b>At least once a year</b>',
'Urinalysis, glucose, total cholesterol, ECG (Sokolov-Lyon, Cornell)',
'Glucose, potassium, full lipids, creatinine/eGFR, uric acid, urinalysis, albuminuria, ECG',
'Same as Group 2'],
['<b>At least every 2 years</b>',
'Potassium, LDL, HDL, TG, creatinine/eGFR, uric acid, albuminuria',
'(done yearly)', '(done yearly)'],
['<b>If needed</b>',
'Echo (LV mass), carotid ultrasound, ABPM, Holter',
'Echo (LV mass), carotid ultrasound',
'Echo, carotid ultrasound, ABPM, Holter, kidney ultrasound + renal artery duplex + leg arteries'],
['<b>Refer to</b>', '-', '-',
'Cardiologist, nephrologist, ophthalmologist, neurologist, endocrinologist, vascular/endovascular surgeon'],
]
E.append(tbl(fu, [28 * mm, 46 * mm, 55 * mm, 51 * mm], PURPLE, first_bold=False))
E.append(sp(4))
E.append(solid('Before the patient leaves your room: 8-point check', [
'BP measured correctly and written down (both arms on first visit)',
'Diagnosis confirmed: 2 visits, or home / ABPM when in doubt',
'Grade, risk group and diagnosis wording written',
'Baseline blood, urine, ECG and echo ordered',
'Lifestyle advice given, with numbers (salt, alcohol, exercise, weight)',
'Pill plan clear: combination first, dose and timing explained, side effects discussed',
'Home BP technique taught; diary given; next visit date set',
'Danger signs explained: severe headache, chest pain, breathlessness, weakness, speech or vision change -> come to emergency',
], NAVY))
E.append(sp(4))
E.append(P('<b>About this handbook.</b> Built only from the attached Belarus Ministry of Health protocol (Decision No. 38, 25 April 2026). Administrative, legal and hospital-level details were left out on purpose. The protocol\'s Appendix 9 (treatment algorithm) appears blank in the supplied file, so the drug ladder on Stop 7 was rebuilt from the protocol\'s text (clauses 19-26). The risk matrix and doses were typed from the supplied text; please verify against the original protocol and your local formulary. This guide supports learning and does not replace senior supervision.', small))
doc = BaseDocTemplate('/tmp/workspace/htn-handbook/Hypertension_OPD_Handbook.pdf', pagesize=A4,
leftMargin=15 * mm, rightMargin=15 * mm, topMargin=14 * mm, bottomMargin=18 * mm,
title='Hypertension OPD Survival Guide', author='Orris')
fr = Frame(15 * mm, 18 * mm, W, PH - 32 * mm, id='f', leftPadding=0, rightPadding=0, topPadding=0, bottomPadding=0)
doc.addPageTemplates([PageTemplate(id='cover', frames=[fr], onPage=cover, autoNextPageTemplate='later'),
PageTemplate(id='later', frames=[fr], onPage=later)])
doc.build(E)
print('built')
cd /tmp/workspace/htn-handbook && python3 build.py 2>&1 | tail -20 && python3 -c " import pdfplumber with pdfplumber.open('Hypertension_OPD_Handbook.pdf') as p: print(len(p.pages)) for i,pg in enumerate(p.pages): pg.to_image(resolution=60).save('pg%02d.png'%(i+1)) "
cd /tmp/workspace/htn-handbook && python3 -c " import pdfplumber from PIL import Image with pdfplumber.open('Hypertension_OPD_Handbook.pdf') as p: ims=[pg.to_image(resolution=62).original.convert('RGB') for pg in p.pages] w,h=ims[0].size for k in range(2): sheet=Image.new('RGB',(w*4,h*2),'white') for j,im in enumerate(ims[k*8:(k+1)*8]): sheet.paste(im,((j%4)*w,(j//4)*h)) sheet.save('sheet%d.png'%(k+1)) print(w,h) "
cd /tmp/workspace/htn-handbook && python3 - <<'EOF' s=open('build.py').read() s=s.replace(""" c.saveState() c.setFillAlpha(0.16)""",""" c.saveState() cp = c.beginPath() cp.roundRect(0, 0, W, self.height, 8) c.clipPath(cp, stroke=0, fill=0) c.setFillAlpha(0.16)""") s=s.replace("from reportlab.platypus import (","from reportlab.platypus import (CondPageBreak, ") s=s.replace("%s</font> %s' % (color","%s</font> %s' % (color") # conditional breaks s=s.replace("E.append(PageBreak())\n\nE.append(P('Resistant hypertension","E.append(CondPageBreak(110 * mm))\n\nE.append(P('Resistant hypertension") s=s.replace("E.append(PageBreak())\n\n# ---- STOP 7 ----","E.append(CondPageBreak(150 * mm))\n\n# ---- STOP 7 ----") s=s.replace("E.append(sp(4))\n\n# ---- STOP 8 ----","E.append(PageBreak())\n\n# ---- STOP 8 ----") s=s.replace("E.append(PageBreak())\nE.append(drugtab('Diuretics'","E.append(drugtab('Diuretics'") open('build.py','w').write(s) EOF grep -n "CondPageBreak\|PageBreak())" build.py | head -40
cd /tmp/workspace/htn-handbook && python3 build.py 2>&1 | tail -5 && python3 -c " import pdfplumber from PIL import Image with pdfplumber.open('Hypertension_OPD_Handbook.pdf') as p: n=len(p.pages); print(n) ims=[pg.to_image(resolution=62).original.convert('RGB') for pg in p.pages] w,h=ims[0].size sheet=Image.new('RGB',(w*5,h*3),'white') for j,im in enumerate(ims[1:16]): sheet.paste(im,((j%5)*w,(j//5)*h)) sheet.save('sheetB.png') "
cd /tmp/workspace/htn-handbook && python3 - <<'EOF' s=open('build.py').read() s=s.replace(""" c.setFillAlpha(0.16) c.setFillColor(colors.white) c.circle(W - 14 * mm""",""" c.setFillColor(tint(self.color, 0.0) if False else colors.Color(min(1, self.color.red + 0.12), min(1, self.color.green + 0.12), min(1, self.color.blue + 0.12))) c.circle(W - 14 * mm""") open('build.py','w').write(s) EOF python3 build.py && python3 -c " import pdfplumber with pdfplumber.open('Hypertension_OPD_Handbook.pdf') as p: print(len(p.pages)) p.pages[9].to_image(resolution=75).save('chk.png') "
Hypertension OPD Handbook
PDF Document · PDF
Please prepare a audio file explaining this in a very easy and creative and logical interesting clinical way and elaborate the explanation wherever required.
which espeak espeak-ng ffmpeg flite festival lame sox 2>&1; pip list 2>/dev/null | grep -i -E "tts|pyttsx|gtts|edge|pydub|soundfile|scipy|torch|onnx|piper|kokoro|numpy|pocketsphinx|TTS"; timeout 8 curl -sI https://translate.google.com | head -1; timeout 8 curl -sI https://speech.platform.bing.com | head -1; ls /tmp/skills
~/htn-handbook/narration.py
SECTIONS = {}
SECTIONS['00_intro'] = """
Welcome, doctor. Today we are going to walk through your very first day in the outpatient department, and the one disease you will meet in almost every room: high blood pressure, also called arterial hypertension.
Here is the good news. You do not need years of experience to handle this well. You need a route. Think of it as a journey with eleven stops. At every stop I will tell you what to do, and, more importantly, why you do it. When you understand the why, you will never need to memorise anything.
This talk follows the Belarus Ministry of Health clinical protocol, Decision number thirty eight, April twenty twenty six, which you shared with me. I have kept only what a new OPD doctor really needs. Here are the eleven stops. One: measure the pressure properly. Two: confirm the diagnosis. Three: grade it. Four: history, examination and tests. Five: hunt for secondary causes. Six: calculate the risk and write the diagnosis. Seven: the treatment plan. Eight: the drug doses. Nine: protecting the whole patient beyond the pressure. Ten: hypertensive crisis. And eleven: follow up.
Ready? Let us begin.
"""
SECTIONS['01_measure'] = """
Stop one. Measure the pressure like a professional.
First, a golden rule from the protocol. Every patient, at every visit, for any complaint, must have their blood pressure measured and written in the record. A patient may come with knee pain or a cough. You still measure. That is how hypertension is caught early, because it is a silent disease. Most people feel completely fine until the damage is done.
Why does technique matter so much? Because everything else, the diagnosis, the drugs, the follow up, depends on one number. If that number is wrong, every decision after it is wrong.
So let us prepare the patient. For thirty minutes before the measurement, no food, no tea or coffee, no smoking, and no exercise. Each of these pushes the pressure up temporarily. Then the patient sits quietly for five minutes, with the back supported, legs uncrossed and feet flat on the floor. Crossed legs and a hanging leg can raise the reading. No talking during the measurement, because talking also raises the pressure. The arm rests on the table, with the sleeve off, not rolled up tight like a tourniquet.
Now the cuff. Choose the right size for the upper arm. A cuff that is too small will give you a falsely high reading, and a very common mistake in obese patients. The middle of the cuff should sit at the level of the heart. If the arm hangs low, the column of blood adds pressure and the reading is falsely high. The lower edge of the cuff goes two to three centimetres above the elbow crease, with the bladder centred over the brachial artery. It should be snug, with space for about one finger.
Now the technique. On the first visit, measure both arms. Use the arm with the higher reading from then on. If the systolic pressure differs between arms by more than ten, repeat it to confirm. If a difference of more than twenty persists, think of peripheral arterial disease and investigate further.
If you use the stethoscope method, the first Korotkoff sound is the systolic pressure, and the disappearance, the fifth sound, is the diastolic pressure. Deflate slowly, about two millimetres of mercury per second. Faster than that and you will miss the true values.
Take at least two readings, one to two minutes apart. If they differ by more than ten, take a third. Then record the average of the last two. Also count the heart rate, because a resting rate above eighty is itself a risk marker.
One more skill: the standing pressure. In a patient on treatment, especially above sixty, with diabetes, Parkinson's disease or dementia, ask yourself whether the pills are dropping the pressure too much when they stand. Measure sitting, then standing. If the systolic falls by twenty or more, or the diastolic by ten or more, within three minutes, that is postural hypotension. Imagine an elderly patient who faints in the bathroom because we treated too aggressively. This simple check prevents it.
"""
SECTIONS['02_confirm'] = """
Stop two. Is it really hypertension?
Do not label a patient on a single reading. Blood pressure naturally bounces up and down, with anxiety, pain, a rushed walk to the clinic.
Here is the definition. Hypertension is a chronic, stable rise in office pressure: systolic one hundred and forty or more, and or diastolic ninety or more.
How do we confirm it? Take at least two readings, at two separate visits, one to two weeks apart. There is one shortcut. If the pressure is above one eighty over one ten, and you also see signs of organ damage, such as a thick left ventricle, vascular damage or kidney damage, you can make the diagnosis at a single visit. The reason is simple: when the organs are already suffering, waiting is not safe.
Now here is something that confuses many beginners. The number depends on where you measure it. In the office, hypertension starts at one forty over ninety. At home, the cut off is one thirty five over eighty five, because home readings run a little lower than office ones. With twenty four hour monitoring, called ABPM, the daytime average limit is one thirty five over eighty five, the night average limit is one twenty over seventy, and the full day average limit is one thirty over eighty.
Two special patterns are worth remembering. The first is white coat hypertension. The pressure is high in the clinic, over one forty over ninety at several visits, but normal at home or on ABPM. The doctor's coat itself is raising the pressure. The second is masked hypertension, the opposite. The office pressure looks normal but the home or ABPM readings are high. Picture a taxi driver who is calm in your room but tense all day on the road. You would never catch him with a clinic reading alone. In both patterns, if there are signs of organ damage, the patient is treated as having true hypertension.
Who needs the twenty four hour monitor? Mainly patients where you suspect white coat or masked hypertension, for example grade one hypertension in someone with low risk, or office readings that differ a lot from home readings. It is also used when you want to separate true resistant hypertension from pseudo resistant, in patients with dizziness, falls or fainting where you suspect low pressure, and in special groups such as sleep apnoea, diabetes, kidney disease, glaucoma and pregnancy. If you are not sure, ask your senior.
How does ABPM work? It records for twenty four hours, every fifteen to thirty minutes while awake, and every thirty to sixty minutes during sleep. You need at least seventy percent valid readings. If there are fewer than twenty readings awake or fewer than seven at night, the test is repeated.
Home monitoring needs teaching. The patient measures for seven days, twice a day, morning and evening, before meals and before taking the pills, at least two readings each time. The first day is thrown away, and you average the rest. Use an automatic upper arm device, and if it has no memory, the patient writes a diary. Home readings are wonderful for checking whether treatment works, and you should ask for them in the week before the next visit.
"""
SECTIONS['03_grade'] = """
Stop three. Grade the pressure.
Think of a traffic light. Below one twenty over eighty is optimal. One twenty to one twenty nine over eighty to eighty four is normal. One thirty to one thirty nine over eighty five to eighty nine is high normal. Then comes hypertension. Grade one is one forty to one fifty nine, or ninety to ninety nine. Grade two is one sixty to one seventy nine, or one hundred to one hundred and nine. Grade three is one eighty or more, or one hundred and ten or more.
There are also two special types. Isolated systolic hypertension is a systolic of one forty or more with a diastolic under ninety, common in the elderly. Isolated diastolic hypertension is the reverse. Each is graded by its raised number.
The golden rule: if the systolic and diastolic fall in different grades, you take the higher grade. So one sixty eight over ninety two is grade two. And one fifty over one ten is grade three, because the diastolic is the higher category. Grading is most accurate in newly diagnosed patients, or patients who are not yet on treatment, because medicines change the numbers.
"""
SECTIONS['04_history'] = """
Stop four. History, examination and tests.
Now you become a detective. You are asking two big questions. What is driving this pressure? And what has it already damaged?
Start with the pressure story. When was hypertension found, and for how long? What were the earlier readings? Which drugs has the patient taken, both for pressure and for other things? Any side effects, like cough or swelling of the ankles, in the past? And the most important question, which many beginners forget: are they actually taking the tablets? Poor adherence is one of the commonest reasons for uncontrolled blood pressure.
Then ask about risk factors. Smoking, alcohol, too much salt, poor diet, little exercise, overweight, poor sleep, and also stress, anxiety, depression or loneliness. Ask about other diseases: previous heart attack, heart failure, stroke or mini stroke, diabetes, kidney disease. And ask about the family: hypertension, early heart disease, high cholesterol, diabetes, kidney disease. Finally, ask about symptoms of hypertension and of other diseases, and look for clinical signs of organ damage.
Examine and measure. Take the blood pressure and heart rate, then weight, height, body mass index and waist circumference. Obesity is a body mass index above thirty, or a waist above one hundred and two centimetres in men and eighty eight in women.
Now the tests. For every new patient, the protocol asks for a standard set. Blood: a full blood count, fasting glucose, the lipid profile with total cholesterol, LDL, HDL and triglycerides, then potassium, sodium, uric acid and creatinine with the estimated filtration rate, and liver tests, bilirubin and ALT. Urine: a general urine test with sediment, and albuminuria, ideally the albumin to creatinine ratio. If you cannot measure albumin, at least check for protein. Heart: a twelve lead ECG and an echocardiogram.
Why this list? Think of three questions. Is the kidney the cause or the victim? That is creatinine, filtration rate and urine albumin. Is there a hidden partner disease, like diabetes or high cholesterol? That is glucose and lipids. And has the heart already thickened? That is the ECG and echo. Potassium is checked because low potassium can hint at hormonal causes, and because many of our drugs change it.
Then there are tests only when there is a reason. Fundoscopy for grade two or three hypertension, or hypertension with diabetes, to look for retinal damage. A neurologist or endocrinologist to rule out secondary causes. A twenty four hour ECG for rhythm problems. A stress test, on bicycle or treadmill, to look for coronary disease. A carotid ultrasound when there is a neck bruit, a previous mini stroke or stroke, or atherosclerosis elsewhere. A kidney ultrasound with renal artery flow when the filtration rate is low, albumin is in the urine, or you suspect a secondary cause. A brain scan if there are neurological symptoms, and angiography of the renal arteries if renovascular hypertension is suspected.
"""
SECTIONS['05_secondary'] = """
Stop five. Do not miss secondary hypertension.
About most patients, we say essential or primary hypertension, which simply means no single cause is found. But in some patients, a specific problem is pushing the pressure up, and fixing it may cure or greatly improve the hypertension. That is why it is worth a moment of thought.
The protocol groups the causes by organ. The kidneys: chronic glomerulonephritis, chronic pyelonephritis, diabetic kidney disease, polycystic kidneys, and others. The renal arteries: atherosclerosis, or fibromuscular dysplasia in a younger person. The hormones: primary aldosteronism, Cushing's syndrome, phaeochromocytoma, thyroid and parathyroid diseases, and acromegaly. The big arteries: coarctation of the aorta, where the arm pressure is higher than the leg pressure. And the nervous system and sleep: brain tumours, raised intracranial pressure, and one very common cause, obstructive sleep apnoea. Ask about snoring and daytime sleepiness.
There is one more group that every OPD doctor must always check, the medicines. Hormonal contraceptives, steroids, anti inflammatory painkillers like ibuprofen and diclofenac, decongestants and other sympathomimetics, cyclosporine, erythropoietin, cocaine, and some cancer drugs can all raise the pressure. A patient on regular painkillers for arthritis whose blood pressure will not settle is a classic. Always ask for the full medicine list.
When should you suspect a secondary cause? When the pressure is hard to control, when the kidney tests or urine are abnormal, or when something in the picture is unusual. Then you bring in the endocrinologist or neurologist, and order the kidney ultrasound with renal artery flow.
"""
SECTIONS['06_risk'] = """
Stop six. Total risk, and how to write your diagnosis.
Here is an important idea. We do not treat a number. We treat a person's chance of having a heart attack, stroke or cardiovascular death in the next ten years. Two patients with the same pressure of one fifty over ninety five can have very different risk. One is a healthy twenty eight year old. The other is a sixty year old smoking diabetic man with high cholesterol. You will treat them very differently.
To estimate risk, count three things.
First, risk factors. Male sex. Age over fifty five in men and over sixty five in women. Smoking, current or past. Dyslipidaemia: total cholesterol above four point nine, LDL above three, low HDL, or triglycerides above one point seven. Raised fasting glucose, obesity, a big waist, uric acid above three hundred and sixty, early heart disease in the family, early menopause before forty five, and a resting heart rate above eighty.
Second, silent organ damage. A stiff artery, shown by a pulse pressure above sixty. A thickened left ventricle on the ECG or echo. Plaques in arteries. Kidney disease stage three. Albumin in the urine. And severe damage in the retina.
Third, associated clinical conditions, meaning established disease: stroke or mini stroke, coronary disease, atrial fibrillation, heart failure, significant plaque, advanced kidney disease stage four or five, and diabetes.
Now combine them with the grade. The protocol gives a matrix. The rule of thumb is this. A patient with no other risk factors is at no, low, medium or high risk, rising with the grade of hypertension. One to two risk factors push you one level up. Three or more risk factors, or silent organ damage, push you to high risk or very high risk. And anyone with an established cardiovascular disease, advanced kidney disease or diabetes is automatically very high risk, even if the hypertension is only grade one. That last point is worth remembering, because a diabetic patient with a pressure of one forty five over ninety five is not mild. They are very high risk.
How do you write the diagnosis? In this order. One: the grade of hypertension. Two: the risk group. Three: the main risk factors, like obesity or dyslipidaemia. Four: associated clinical conditions and diabetes. For example: arterial hypertension grade two, risk three, obesity grade two with a body mass index of thirty six. Or: arterial hypertension grade one, risk four, uncomplicated hypertensive crisis stopped, type two diabetes compensated, hypertriglyceridaemia. In the examples, risk three seems to mean high and risk four very high, but ask your senior which wording your hospital prefers.
"""
SECTIONS['07_treat'] = """
Stop seven. The treatment plan.
What are we really trying to do? Not just lower a number. The goal is to reduce heart attacks, strokes, heart failure and kidney failure. That means we correct every modifiable risk factor, prevent or slow organ damage, find and treat coronary disease, kidney disease and diabetes, and bring the pressure down to the target.
The target is a two step idea. First, get below one forty over ninety. Then, if the patient tolerates it well, go to one thirty over eighty or lower, but never below one twenty over seventy. Why not lower? Because too low a pressure causes dizziness and falls, and in some patients reduces blood flow to vital organs.
Lifestyle is for every patient, whatever the grade. This is not optional advice. It lowers pressure, makes drugs work better and cuts risk. Stop smoking, including e cigarettes. Aim for a body mass index of twenty to twenty five, and a waist below ninety four centimetres in men and eighty in women. Salt under five grams a day, about one level teaspoon, and remember hidden salt in pickles, bread and processed food. Alcohol below ten standard drinks a week, though none is best. A diet low in saturated fat, rich in whole grains and fibre, with at least two hundred grams of vegetables and two hundred grams of fruit every day, and less sugary drinks and red meat. And exercise: at least one hundred and fifty minutes a week of moderate aerobic activity, like brisk walking, or seventy five minutes of vigorous activity. Where available, send the patient to the School for Patients with Hypertension.
Now the drugs, as a ladder.
Step one. Start with a combination of two drugs. This is a big change from older teaching, where we started with one drug and slowly added. The protocol prefers a two drug start, because most patients need two drugs anyway, and the combination gives better control faster. The pair is an ACE inhibitor or an ARB, together with either a long acting calcium channel blocker or a thiazide or thiazide like diuretic. And wherever possible use a fixed dose single pill, because fewer tablets mean better adherence. Do not combine an ACE inhibitor with an ARB.
There are exceptions where one drug is enough: low risk grade one hypertension with pressure under one fifty over ninety five, patients aged eighty or older, and frail elderly patients.
Step two. If the office pressure is still above one forty over ninety on two drugs, move to three: an ACE inhibitor or ARB, plus a long acting calcium channel blocker, plus a thiazide or thiazide like diuretic, again preferably in one pill.
Where do beta blockers fit? They are not the usual first pair, but they are added when there is a special reason: angina, a previous heart attack, heart failure, or when you want to control a fast heart rate, above eighty beats a minute.
If the main drugs fail or are not tolerated, extra classes can be used: spironolactone type drugs, loop diuretics, alpha blockers, and centrally acting drugs.
Step three is resistant hypertension. This means the pressure stays at one forty over ninety or more despite lifestyle measures and optimal or maximum tolerated doses of three or more drugs including a diuretic, in a patient who really takes them, once secondary causes are excluded. First confirm it with home or twenty four hour monitoring, and check for pseudo resistance: poor adherence, wrong technique, a stiff calcified artery, drugs that raise pressure, sleep apnoea. Then strengthen the diuretic. Increase the dose, or change hydrochlorothiazide to chlorthalidone or indapamide, and or add torasemide ten milligrams a day, especially if there is kidney disease or fluid retention. Next add spironolactone, twenty five to fifty milligrams a day, with checks of potassium and kidney function. If spironolactone is not tolerated, eplerenone can be used after a medical council decision, since it is off label. After that, an alpha blocker, a beta blocker, or a central drug like moxonidine or clonidine. In selected patients, renal denervation may be done in specialised cardiac centres.
A word on safety. Off label use, meaning outside the drug leaflet, needs a medical consultation. Never decide it alone.
"""
SECTIONS['08_doses'] = """
Stop eight. Drug doses, in plain language.
You do not need to memorise every number today, because the handbook has a cheat sheet. But let me give you the anchor doses, so you feel at home. All are oral, mostly once daily, and for each drug I will give the start dose and the maximum.
ACE inhibitors. Enalapril: start with two and a half to five milligrams, usual twenty, maximum forty. Lisinopril: start two and a half to five, usual twenty, maximum eighty. Ramipril: start two and a half, usual five to ten, maximum ten. Perindopril: start two to two and a half, usual four to eight, maximum eight to ten.
ARBs. Losartan: start fifty, maximum one hundred. Telmisartan: start forty, maximum eighty. Valsartan: start eighty, maximum three hundred and twenty. Candesartan: start eight, maximum thirty two. Irbesartan: start seventy five, maximum three hundred. Olmesartan: start ten, maximum forty.
Calcium channel blockers. Amlodipine is the one you will use most: start two and a half, usual five, maximum ten.
Diuretics. Hydrochlorothiazide: twelve and a half to twenty five. Indapamide slow release: one and a half. Chlorthalidone: start twelve and a half to twenty five, maximum fifty. Spironolactone: start twenty five, maximum fifty.
Beta blockers. Bisoprolol: start two and a half, usual five, maximum twenty. Metoprolol: start twenty five, maximum two hundred. Nebivolol: start two and a half, usual five, maximum ten. Carvedilol: start six point two five to twelve point five, maximum fifty.
One special note. Methyldopa is only for pregnancy and breast feeding. And as a general teaching point, not from this protocol, ACE inhibitors and ARBs should not be used in pregnancy, so always ask women of child bearing age about it.
Always start low in elderly patients and in patients with kidney problems, and check the leaflet and your hospital formulary before prescribing.
"""
SECTIONS['09_beyond'] = """
Stop nine. Protect the whole patient, not only the pressure.
Hypertension rarely travels alone. The same arteries that suffer from high pressure also suffer from cholesterol and clotting. So we look at three more areas.
First, cholesterol. Statins are the drug of choice, with ezetimibe and others as options. The aim is to lower LDL cholesterol by more than fifty percent from baseline, and reach a target that depends on risk. Very high risk: LDL below one point four millimoles per litre. A patient with cardiovascular disease who has had a second event within two years: below one point zero. High risk: below one point eight. Moderate risk: below two point six. Low risk: below three point zero. See how the higher the risk, the lower the target. That is the same logic as before.
Second, aspirin. Give low dose aspirin, seventy five to one hundred milligrams a day, to patients who already have atherosclerotic disease: previous heart attack, ischaemic stroke or mini stroke, or peripheral artery disease. If aspirin is not tolerated, use a P2Y12 inhibitor. But, and this is a key point, do not give aspirin for primary prevention in a hypertensive patient with no atherosclerotic disease. The benefit is not proven, and the bleeding risk is real. Many new doctors give aspirin to everybody. Please don't.
Third, uric acid. If the patient has hypertension and high uric acid, treat it. The target is below three hundred micromoles per litre in high or very high risk patients with at least two of: hypertension, diabetes, dyslipidaemia, recent stroke or heart attack, or kidney disease. In others, below three hundred and sixty. The drug is allopurinol, or febuxostat if allopurinol is not tolerated, with uric acid checked twice a year, a low purine diet and regular exercise.
"""
SECTIONS['10_crisis'] = """
Stop ten. Hypertensive crisis.
Picture this: a patient walks in with a pressure of two hundred over one twenty and a terrible headache. Do you panic? No. You ask one question. Is any organ being damaged right now?
A hypertensive crisis is a sudden, significant rise in blood pressure with symptoms, needing controlled reduction. It comes in two types.
An uncomplicated crisis has no acute organ damage. Here the rule is: slowly. Lower the pressure by no more than twenty five percent in the first two hours, and then reach the normal range within twenty four hours. Dropping it too fast can cause a stroke or heart ischaemia, because the brain and heart have adapted to high pressure. The protocol lists several options, and you choose just one. Captopril twenty five to fifty milligrams by mouth or under the tongue. Nifedipine ten milligrams by mouth. Propranolol forty milligrams. Glyceryl trinitrate half a milligram under the tongue. Or intravenous options such as magnesium sulfate, furosemide, bendazole or urapidil. If those fail, moxonidine or clonidine by mouth. After the crisis, arrange ambulatory monitoring and restart or intensify the regular treatment.
A complicated crisis has acute, life threatening organ damage. Examples are a hypertensive brain problem with confusion or seizures, an acute heart attack, acute pulmonary oedema, an aortic dissection, stroke or brain haemorrhage, and malignant hypertension with kidney failure. These patients need emergency hospital admission and intravenous drugs, with targets that depend on the condition. In short: in heart attack and pulmonary oedema, the systolic is brought below one forty quickly. In aortic dissection it must come below one twenty with a heart rate under sixty. In ischaemic stroke, you usually do not lower pressure in the first day unless it is above two twenty over one twenty, or the patient is getting thrombolysis, when you keep it under one eighty over one oh five. In encephalopathy and malignant hypertension the fall is no more than twenty to twenty five percent over several hours.
You also send to hospital an uncomplicated crisis if the symptoms are getting worse and cannot be reversed in the clinic. As the OPD doctor, your job is to recognise the complicated crisis early and send the patient. You do not need to manage infusions.
"""
SECTIONS['11_followup'] = """
Stop eleven. Follow up, and your final checklist.
Treatment is a long relationship, not a single prescription. The first review is three months after starting the first drug. At each visit, ask about complaints, check whether the patient really takes the medicines, review the home readings, examine, and calculate body mass index and waist. Adjust treatment if needed.
Then the schedule depends on the patient group.
Group one: grade one hypertension with no organ damage. Once a year check urine, glucose, total cholesterol and an ECG. Every two years check potassium, the full lipids, creatinine with filtration rate, uric acid and urine albumin.
Group two: hypertension of any grade with organ damage but no established disease. Once a year check glucose, potassium, the full lipids, creatinine and filtration rate, uric acid, urine and albumin, and an ECG. Echo and carotid ultrasound when needed.
Group three: patients with established disease, diabetes, resistant hypertension or secondary hypertension. The same yearly checks, plus when needed echo, carotid ultrasound, ambulatory monitoring, Holter, kidney ultrasound with renal artery duplex, and the lower limb arteries, and referral to cardiology, nephrology, ophthalmology, neurology, endocrinology or vascular surgery.
Before the patient leaves your room, run this eight point check in your mind. One: was the pressure measured correctly and written down? Two: is the diagnosis confirmed, with home or ambulatory monitoring if in doubt? Three: did I write the grade, risk group and full diagnosis? Four: did I order the baseline blood, urine, ECG and echo? Five: did I give lifestyle advice with real numbers? Six: is the pill plan clear, with combination first, and have I explained side effects? Seven: did I teach home measurement and fix the next visit? Eight: does the patient know the danger signs, a severe headache, chest pain, breathlessness, weakness, speech or vision change, which mean come straight to emergency?
"""
SECTIONS['12_outro'] = """
Let me close with the five lines I would stick on the wall of your consulting room.
Measure correctly, every patient, every visit. Confirm before you label. Grade the pressure, then judge the total risk. Start with two drugs, move to three, and always check the adherence before you add anything. And in a crisis, ask whether an organ is being damaged right now. If yes, hospital.
This audio is a learning aid based on the protocol you gave me. Please check the exact doses against the original document and your local formulary, and always work under your senior's guidance. You are going to do very well. Good luck with your first OPD, doctor.
"""
~/htn-handbook/make_audio.py
import os, re, sys, time
from gtts import gTTS
sys.path.insert(0, '/tmp/workspace/htn-handbook')
from narration import SECTIONS
out = '/tmp/workspace/htn-handbook/audio_parts'
os.makedirs(out, exist_ok=True)
final = '/tmp/workspace/htn-handbook/Hypertension_OPD_Audio_Guide.mp3'
parts = []
for k in sorted(SECTIONS):
fn = os.path.join(out, k + '.mp3')
text = re.sub(r'\s+', ' ', SECTIONS[k]).strip()
if not os.path.exists(fn):
for attempt in range(4):
try:
gTTS(text=text, lang='en', tld='co.in', slow=False).save(fn)
break
except Exception as e:
print('retry', k, e)
time.sleep(3)
print('done', k, os.path.getsize(fn), flush=True)
parts.append(fn)
with open(final, 'wb') as f:
for p in parts:
f.write(open(p, 'rb').read())
print('FINAL', os.path.getsize(final))
cd /tmp/workspace/htn-handbook && nohup python3 make_audio.py > audio.log 2>&1 & sleep 100; cat /tmp/workspace/htn-handbook/audio.log
sleep 90; cat /tmp/workspace/htn-handbook/audio.log
sleep 110; cat /tmp/workspace/htn-handbook/audio.log
sleep 45; cat /tmp/workspace/htn-handbook/audio.log | tail -4; ls -la /tmp/workspace/htn-handbook/*.mp3; python3 -c " from mutagen.mp3 import MP3 a=MP3('/tmp/workspace/htn-handbook/Hypertension_OPD_Audio_Guide.mp3');print(a.info.length/60)" 2>&1 | tail -2
Hypertension OPD Audio Guide
MP3 File · MP3