Blood transfusion and important things to know in blood bank and transfusion for a doctor
| Blood Group | Can Receive pRBCs From | Can Receive Plasma From |
|---|---|---|
| O | O only (universal donor RBC) | O, A, B, AB |
| A | A, O | A, AB |
| B | B, O | B, AB |
| AB | A, B, O, AB (universal recipient) | AB only (universal donor plasma) |
| Patient Population | Hemoglobin Threshold |
|---|---|
| Stable, non-cardiac ICU | 7 g/dL (restrictive) |
| Stable surgical patients | 7-8 g/dL |
| GI bleeding (no CAD) | 8 g/dL |
| Known/symptomatic CAD | >9 g/dL (liberal threshold) |
| Portal hypertension bleeding | 7-8 g/dL (avoid overtransfusion - worsens portal pressure) |
| Symptomatic anemia (any Hb) | Transfuse based on symptoms |
| Reaction | Risk |
|---|---|
| Febrile nonhemolytic (FNHTR) | ~1 : 1,100 |
| Allergic (mild) | ~1 : 100-1,200 |
| Allergic (severe/anaphylaxis) | ~1 : 20,000-50,000 |
| Acute hemolytic (AHTR) | ~1 : 6,000-110,000 |
| TACO | ~1 : 9,000 |
| Delayed hemolytic (DHTR) | ~1 : 3,000-32,000 |
| TRALI | ~1 : 5,000-140,000 |
| Bacterial sepsis (RBC) | ~1 : 250,000 |
| Bacterial sepsis (platelets) | ~1 : 25,000 |
| Fatal hemolytic | ~1 : 100,000-600,000 |
| TA-GvHD | <1 : 10,000,000 |
| Pathogen | Residual Risk |
|---|---|
| HIV | ~1 : 1,800,000 |
| HCV | ~1 : 1,600,000 |
| HBV | ~1 : 300,000-1,500,000 |
| HTLV | ~1 : 3,300,000 |
| CMV | Rare (leukoreduction protective) |
| Bacterial (platelets) | ~1 : 25,000 |
| Bacterial (RBCs) | ~1 : 250,000 |
| Product | Storage Temperature | Shelf Life | Notes |
|---|---|---|---|
| pRBC | 1-6°C | 42 days | Additive solution (ADSOL/AS-1) |
| FFP | -18°C or below | 1 year | Thawed: 24h (or 5 days as "thawed plasma") |
| Cryoprecipitate | -18°C or below | 1 year | Thawed: 6h (pooled); 4h (single) |
| Platelets | 20-24°C (agitated) | 5 days | Room temp - highest bacterial risk |
| Granulocytes | 20-24°C | 24 hours | Must irradiate |
| Whole blood | 1-6°C | 21-35 days | Depending on anticoagulant |
Create a summary PDF with blood transfusion protocols and reaction flowcharts
ls -la /tmp/workspace/ 2>/dev/null || echo "workspace empty or missing"
mkdir -p /tmp/workspace/blood-transfusion-pdf && echo "done"
~/blood-transfusion-pdf/generate_pdf.py
#!/usr/bin/env python3
"""
Blood Transfusion Protocols & Reaction Flowcharts - Clinical Reference PDF
Generated with ReportLab
"""
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import mm, cm
from reportlab.lib import colors
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
PageBreak, HRFlowable, KeepTogether
)
from reportlab.platypus.flowables import Flowable
from reportlab.graphics.shapes import Drawing, Rect, String, Line, Polygon, Arrow
from reportlab.graphics import renderPDF
import os
OUTPUT_PATH = "/tmp/workspace/blood-transfusion-pdf/Blood_Transfusion_Protocols.pdf"
# ─── Color Palette ────────────────────────────────────────────────────────────
DEEP_RED = colors.HexColor("#8B0000")
MED_RED = colors.HexColor("#C0392B")
LIGHT_RED = colors.HexColor("#FADBD8")
DARK_BLUE = colors.HexColor("#1A3C5E")
MED_BLUE = colors.HexColor("#2E86C1")
LIGHT_BLUE = colors.HexColor("#D6EAF8")
TEAL = colors.HexColor("#117A65")
LIGHT_TEAL = colors.HexColor("#D1F2EB")
ORANGE = colors.HexColor("#D35400")
LIGHT_ORANGE = colors.HexColor("#FAE5D3")
YELLOW_BG = colors.HexColor("#FEF9E7")
DARK_GREY = colors.HexColor("#2C3E50")
MID_GREY = colors.HexColor("#7F8C8D")
LIGHT_GREY = colors.HexColor("#ECF0F1")
WHITE = colors.white
BLACK = colors.black
GOLD = colors.HexColor("#F39C12")
W, H = A4 # 595.27 x 841.89 pts
# ─── Styles ───────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()
def make_style(name, parent='Normal', **kwargs):
return ParagraphStyle(name, parent=styles[parent], **kwargs)
TITLE_STYLE = make_style('DocTitle', fontSize=24, textColor=WHITE,
fontName='Helvetica-Bold', alignment=TA_CENTER, leading=30)
SUBTITLE_STYLE = make_style('DocSubtitle', fontSize=11, textColor=colors.HexColor("#BDC3C7"),
fontName='Helvetica', alignment=TA_CENTER, leading=16)
SECTION_STYLE = make_style('SectionHead', fontSize=13, textColor=WHITE,
fontName='Helvetica-Bold', alignment=TA_LEFT, leading=18)
BODY_STYLE = make_style('Body', fontSize=8.5, textColor=DARK_GREY,
fontName='Helvetica', leading=13, spaceAfter=4)
BODY_BOLD = make_style('BodyBold', fontSize=8.5, textColor=DARK_GREY,
fontName='Helvetica-Bold', leading=13)
SMALL_STYLE = make_style('Small', fontSize=7.5, textColor=MID_GREY,
fontName='Helvetica', leading=11)
TABLE_HDR = make_style('TblHdr', fontSize=8, textColor=WHITE,
fontName='Helvetica-Bold', alignment=TA_CENTER, leading=11)
TABLE_CELL = make_style('TblCell', fontSize=8, textColor=DARK_GREY,
fontName='Helvetica', leading=11)
TABLE_CELL_BOLD = make_style('TblCellBold', fontSize=8, textColor=DARK_GREY,
fontName='Helvetica-Bold', leading=11)
TABLE_CELL_CENTER = make_style('TblCellC', fontSize=8, textColor=DARK_GREY,
fontName='Helvetica', alignment=TA_CENTER, leading=11)
WARNING_STYLE = make_style('Warning', fontSize=8.5, textColor=MED_RED,
fontName='Helvetica-Bold', leading=13)
FLOW_TEXT = make_style('FlowText', fontSize=8, textColor=DARK_GREY,
fontName='Helvetica', alignment=TA_CENTER, leading=11)
FLOW_TEXT_BOLD = make_style('FlowTextBold', fontSize=8, textColor=WHITE,
fontName='Helvetica-Bold', alignment=TA_CENTER, leading=11)
CAPTION = make_style('Caption', fontSize=7.5, textColor=MID_GREY,
fontName='Helvetica-Oblique', alignment=TA_CENTER, leading=10)
# ─── Header/Footer ────────────────────────────────────────────────────────────
def on_page(canvas, doc):
canvas.saveState()
# Footer bar
canvas.setFillColor(DARK_BLUE)
canvas.rect(0, 0, W, 18*mm, fill=1, stroke=0)
canvas.setFillColor(WHITE)
canvas.setFont('Helvetica', 7.5)
canvas.drawString(15*mm, 6*mm, "Blood Transfusion Protocols & Reference | Clinical Summary")
canvas.drawRightString(W - 15*mm, 6*mm, f"Page {doc.page}")
canvas.setFillColor(GOLD)
canvas.rect(0, 17*mm, W, 1.5*mm, fill=1, stroke=0)
canvas.restoreState()
def on_first_page(canvas, doc):
on_page(canvas, doc)
# ─── Custom Flowables ─────────────────────────────────────────────────────────
class SectionHeader(Flowable):
def __init__(self, text, color=DARK_BLUE, width=None):
super().__init__()
self.text = text
self.color = color
self.width = width or (W - 30*mm)
self.height = 22*mm
def draw(self):
c = self.canv
c.setFillColor(self.color)
c.roundRect(0, 2*mm, self.width, 16*mm, 3*mm, fill=1, stroke=0)
# Accent strip
c.setFillColor(GOLD)
c.rect(0, 2*mm, 4*mm, 16*mm, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont('Helvetica-Bold', 12)
c.drawString(8*mm, 8.5*mm, self.text)
def wrap(self, avW, avH):
return self.width, self.height
class BoxedNote(Flowable):
def __init__(self, text, bg=LIGHT_RED, border=MED_RED, icon="!", width=None):
super().__init__()
self.text = text
self.bg = bg
self.border = border
self.icon = icon
self.width = width or (W - 30*mm)
self.height = 18*mm
def draw(self):
c = self.canv
c.setFillColor(self.bg)
c.setStrokeColor(self.border)
c.setLineWidth(1.5)
c.roundRect(0, 0, self.width, 14*mm, 2*mm, fill=1, stroke=1)
# Icon circle
c.setFillColor(self.border)
c.circle(7*mm, 7*mm, 4*mm, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont('Helvetica-Bold', 9)
c.drawCentredString(7*mm, 5*mm, self.icon)
# Text
c.setFillColor(DARK_GREY)
c.setFont('Helvetica-Bold', 8)
c.drawString(14*mm, 5.5*mm, self.text)
def wrap(self, avW, avH):
return self.width, self.height
class FlowchartBox(Flowable):
"""Renders a complete flowchart as a drawing"""
def __init__(self, drawing, width, height):
super().__init__()
self._drawing = drawing
self.width = width
self.height = height
def draw(self):
renderPDF.draw(self._drawing, self.canv, 0, 0)
def wrap(self, avW, avH):
return self.width, self.height
# ─── Helper: draw flowchart box ───────────────────────────────────────────────
def fc_rect(d, x, y, w, h, text, fill=DARK_BLUE, text_color=WHITE, font_size=8, radius=4):
d.add(Rect(x, y, w, h, rx=radius, ry=radius, fillColor=fill,
strokeColor=colors.HexColor("#BDC3C7"), strokeWidth=0.8))
lines = text.split('\n')
total_h = len(lines) * (font_size + 2)
start_y = y + h/2 + total_h/2 - font_size
for i, line in enumerate(lines):
d.add(String(x + w/2, start_y - i*(font_size+2), line,
fontName='Helvetica-Bold' if i == 0 else 'Helvetica',
fontSize=font_size, fillColor=text_color,
textAnchor='middle'))
def fc_diamond(d, cx, cy, w, h, text, fill=ORANGE, text_color=WHITE, font_size=7.5):
points = [cx, cy+h/2, cx+w/2, cy, cx, cy-h/2, cx-w/2, cy]
d.add(Polygon(points, fillColor=fill, strokeColor=colors.HexColor("#BDC3C7"), strokeWidth=0.8))
lines = text.split('\n')
for i, line in enumerate(lines):
d.add(String(cx, cy + (len(lines)-1)*(font_size+1)/2 - i*(font_size+1),
line, fontName='Helvetica-Bold', fontSize=font_size,
fillColor=text_color, textAnchor='middle'))
def fc_arrow(d, x1, y1, x2, y2, color=DARK_GREY, label='', label_side='right'):
d.add(Line(x1, y1, x2, y2, strokeColor=color, strokeWidth=1.2))
# Arrowhead
if y2 < y1: # downward
d.add(Polygon([x2, y2, x2-4, y2+7, x2+4, y2+7], fillColor=color, strokeWidth=0))
elif y2 > y1: # upward
d.add(Polygon([x2, y2, x2-4, y2-7, x2+4, y2-7], fillColor=color, strokeWidth=0))
elif x2 > x1: # right
d.add(Polygon([x2, y2, x2-7, y2+4, x2-7, y2-4], fillColor=color, strokeWidth=0))
elif x2 < x1: # left
d.add(Polygon([x2, y2, x2+7, y2+4, x2+7, y2-4], fillColor=color, strokeWidth=0))
if label:
lx = (x1+x2)/2 + (5 if label_side=='right' else -5)
ly = (y1+y2)/2
d.add(String(lx, ly, label, fontName='Helvetica', fontSize=7,
fillColor=ORANGE, textAnchor='start' if label_side=='right' else 'end'))
def fc_label(d, x, y, text, color=TEAL, font_size=7):
d.add(String(x, y, text, fontName='Helvetica-Bold', fontSize=font_size,
fillColor=color, textAnchor='middle'))
# ─── Build Flowchart: Transfusion Reaction Management ─────────────────────────
def build_reaction_flowchart():
dw, dh = 480, 560
d = Drawing(dw, dh)
d.add(Rect(0, 0, dw, dh, fillColor=colors.HexColor("#F8FBFF"), strokeColor=LIGHT_GREY, strokeWidth=1))
cx = dw / 2
bw, bh = 200, 28
# Start
fc_rect(d, cx-bw/2, dh-50, bw, bh, "SUSPECT TRANSFUSION REACTION\n(fever, chills, pain, dyspnea, hemoglobinuria)",
fill=MED_RED, font_size=7.5)
fc_arrow(d, cx, dh-50, cx, dh-50-18)
# Step 1
y1 = dh - 110
fc_rect(d, cx-bw/2, y1, bw, bh, "STEP 1: STOP TRANSFUSION IMMEDIATELY\nKeep IV line open with 0.9% NaCl",
fill=DARK_BLUE, font_size=7.5)
fc_arrow(d, cx, y1, cx, y1-18)
# Step 2
y2 = dh - 170
fc_rect(d, cx-bw/2, y2, bw, bh, "STEP 2: CLERICAL CHECK\nVerify patient ID, unit label, compatibility tag",
fill=DARK_BLUE, font_size=7.5)
fc_arrow(d, cx, y2, cx, y2-18)
# Step 3
y3 = dh - 230
fc_rect(d, cx-bw/2, y3, bw, bh, "STEP 3: NOTIFY BLOOD BANK & PHYSICIAN\nSend unit + tubing back to blood bank",
fill=DARK_BLUE, font_size=7.5)
fc_arrow(d, cx, y3, cx, y3-18)
# Step 4 - Labs
y4 = dh - 290
fc_rect(d, cx-bw/2, y4, bw, bh, "STEP 4: SEND SAMPLES\nPost-Tx blood (EDTA+clot), urine sample",
fill=TEAL, font_size=7.5)
fc_arrow(d, cx, y4, cx, y4-25)
# Diamond - hemolysis?
y5 = dh - 348
fc_diamond(d, cx, y5, 160, 38, "Evidence of\nHEMOLYSIS?", fill=ORANGE, font_size=8)
# YES branch (left)
fc_arrow(d, cx-80, y5, cx-80-50, y5, label='YES', label_side='left')
fc_rect(d, cx-80-50-90, y5-14, 90, 55, "HEMOLYTIC RXN\n- Supportive care\n- Maintain UO\n- Treat DIC\n- Repeat ABO/DAT\n- Report to FDA",
fill=MED_RED, font_size=7)
# NO branch (right)
fc_arrow(d, cx+80, y5, cx+80+50, y5, label='NO', label_side='right')
fc_rect(d, cx+80+50, y5-14, 90, 40, "NON-HEMOLYTIC\n- Antipyretics\n- Antihistamine\n- May restart", fill=TEAL, font_size=7)
# Bottom diamond - type of non-hemolytic
fc_arrow(d, cx, y5-19, cx, y5-19-20)
y6 = y5 - 75
fc_diamond(d, cx, y6, 170, 38, "TRALI or TACO?", fill=DARK_BLUE, font_size=8)
# TRALI
fc_arrow(d, cx-85, y6, cx-85-40, y6, label='TRALI', label_side='left')
fc_rect(d, cx-85-40-90, y6-14, 90, 40, "TRALI:\n- O2/ventilation\n- NO diuretics\n- Supportive",
fill=MED_BLUE, font_size=7)
# TACO
fc_arrow(d, cx+85, y6, cx+85+40, y6, label='TACO', label_side='right')
fc_rect(d, cx+85+40, y6-14, 90, 40, "TACO:\n- O2 + sit upright\n- Furosemide\n- Slow/stop Tx",
fill=TEAL, font_size=7)
# Bottom note
d.add(Rect(10, 8, dw-20, 22, fillColor=LIGHT_RED, strokeColor=MED_RED, strokeWidth=0.8, rx=3, ry=3))
d.add(String(dw/2, 15, "⚠ Fatalities must be reported to FDA by phone ASAP + written report within 7 days",
fontName='Helvetica-Bold', fontSize=7.5, fillColor=MED_RED, textAnchor='middle'))
return FlowchartBox(d, dw, dh)
# ─── Build Flowchart: pRBC Transfusion Decision ───────────────────────────────
def build_prbc_flowchart():
dw, dh = 480, 420
d = Drawing(dw, dh)
d.add(Rect(0, 0, dw, dh, fillColor=colors.HexColor("#F8FBFF"), strokeColor=LIGHT_GREY, strokeWidth=1))
cx = dw / 2
bw, bh = 200, 28
# Start
fc_rect(d, cx-bw/2, dh-50, bw, bh, "PATIENT REQUIRES BLOOD TRANSFUSION\nCheck Hb, clinical status, consent",
fill=DARK_BLUE, font_size=7.5)
fc_arrow(d, cx, dh-50, cx, dh-50-18)
# Emergency?
y1 = dh - 115
fc_diamond(d, cx, y1, 180, 40, "LIFE-THREATENING\nHEMORRHAGE?", fill=MED_RED, font_size=8)
# YES - emergency
fc_arrow(d, cx+90, y1, cx+90+40, y1, label='YES', label_side='right')
fc_rect(d, cx+90+40, y1-14, 95, 40, "EMERGENCY:\nGroup O pRBCs\n+ Group AB plasma\n(pre-crossmatch)",
fill=MED_RED, font_size=7)
# NO - stable
fc_arrow(d, cx, y1-20, cx, y1-20-18, label='NO')
y2 = dh - 198
fc_rect(d, cx-bw/2, y2, bw, bh, "TYPE & SCREEN / CROSSMATCH\nCheck ABO/Rh, antibody screen",
fill=TEAL, font_size=7.5)
fc_arrow(d, cx, y2, cx, y2-18)
# Hb threshold decision
y3 = dh - 265
fc_diamond(d, cx, y3, 190, 50, "Hb THRESHOLD\n(see table)", fill=ORANGE, font_size=8)
# Branches
x_left = cx - 140
x_right = cx + 140
# Hb < 7 (general)
fc_arrow(d, x_left, y3+10, x_left, y3+10-15)
fc_rect(d, x_left-70, y3+10-15-35, 140, 40,
"Hb < 7 g/dL\nStable patient\n→ Transfuse 1 unit\nRecheck Hb",
fill=DARK_BLUE, font_size=7)
# Hb < 8 (post-op/CAD)
fc_arrow(d, cx, y3-25, cx, y3-25-15)
fc_rect(d, cx-70, y3-25-15-35, 140, 40,
"Hb < 8 g/dL\nCAD / surgical\n→ Transfuse 1 unit\nRecheck Hb",
fill=MED_BLUE, font_size=7)
# Symptoms
fc_arrow(d, x_right, y3+10, x_right, y3+10-15)
fc_rect(d, x_right-70, y3+10-15-35, 140, 40,
"Symptomatic anemia\n(any Hb level)\n→ Transfuse\nbased on symptoms",
fill=TEAL, font_size=7)
# Bottom note
d.add(Rect(10, 8, dw-20, 22, fillColor=LIGHT_BLUE, strokeColor=MED_BLUE, strokeWidth=0.8, rx=3, ry=3))
d.add(String(dw/2, 15,
"One unit at a time strategy: transfuse 1 unit, reassess Hb and symptoms before next unit",
fontName='Helvetica-Bold', fontSize=7.5, fillColor=DARK_BLUE, textAnchor='middle'))
return FlowchartBox(d, dw, dh)
# ─── Build Flowchart: Massive Transfusion Protocol ───────────────────────────
def build_mtp_flowchart():
dw, dh = 480, 340
d = Drawing(dw, dh)
d.add(Rect(0, 0, dw, dh, fillColor=colors.HexColor("#FFF8F8"), strokeColor=LIGHT_GREY, strokeWidth=1))
cx = dw / 2
# Title bar
d.add(Rect(10, dh-38, dw-20, 28, fillColor=MED_RED, rx=4, ry=4, strokeWidth=0))
d.add(String(cx, dh-28, "MASSIVE TRANSFUSION PROTOCOL (MTP)",
fontName='Helvetica-Bold', fontSize=10, fillColor=WHITE, textAnchor='middle'))
# Trigger box
d.add(Rect(cx-100, dh-85, 200, 32, fillColor=LIGHT_RED, strokeColor=MED_RED, strokeWidth=1, rx=3))
d.add(String(cx, dh-63, "TRIGGER: ≥10 pRBC/24h OR 1 blood volume/24h",
fontName='Helvetica-Bold', fontSize=7.5, fillColor=MED_RED, textAnchor='middle'))
d.add(String(cx, dh-74, "Massive hemorrhage (trauma, OB, surgical)",
fontName='Helvetica', fontSize=7.5, fillColor=DARK_GREY, textAnchor='middle'))
fc_arrow(d, cx, dh-85, cx, dh-85-12)
# MTP Pack
pack_y = dh - 165
d.add(Rect(cx-160, pack_y, 320, 52, fillColor=DARK_BLUE, rx=4, ry=4, strokeWidth=0))
d.add(String(cx, pack_y+40, "MTP PACK (1:1:1 RATIO)",
fontName='Helvetica-Bold', fontSize=9, fillColor=GOLD, textAnchor='middle'))
d.add(String(cx, pack_y+26, "6 units pRBC + 6 units FFP + 1 apheresis platelet",
fontName='Helvetica-Bold', fontSize=8.5, fillColor=WHITE, textAnchor='middle'))
d.add(String(cx, pack_y+14, "Consider: Cryoprecipitate (if fibrinogen <150 mg/dL) | TXA (tranexamic acid)",
fontName='Helvetica', fontSize=7.5, fillColor=colors.HexColor("#AED6F1"), textAnchor='middle'))
fc_arrow(d, cx, pack_y, cx, pack_y-14)
# Complications row
comp_y = pack_y - 75
comps = [
("HYPOTHERMIA", "Use blood warmers", DARK_BLUE),
("HYPOCALCEMIA", "Monitor & replace\nionized Ca²⁺", TEAL),
("COAGULOPATHY", "Balance ratios\nAvoid pRBC-only", MED_RED),
("HYPERKALEMIA", "Monitor in renal\nfailure/neonates", ORANGE),
]
n = len(comps)
box_w = (dw - 40) / n
for i, (title, note, col) in enumerate(comps):
bx = 20 + i * box_w
d.add(Rect(bx, comp_y, box_w-6, 50, fillColor=col, rx=3, ry=3, strokeWidth=0))
d.add(String(bx+box_w/2-3, comp_y+37, title, fontName='Helvetica-Bold',
fontSize=7, fillColor=WHITE, textAnchor='middle'))
for j, line in enumerate(note.split('\n')):
d.add(String(bx+box_w/2-3, comp_y+24-j*10, line, fontName='Helvetica',
fontSize=7, fillColor=colors.HexColor("#D5D8DC"), textAnchor='middle'))
# Bottom
d.add(Rect(10, 8, dw-20, 20, fillColor=YELLOW_BG, strokeColor=GOLD, strokeWidth=0.8, rx=3))
d.add(String(cx, 14, "Reassess after each pack | Communicate clearly with blood bank | Activate/deactivate MTP explicitly",
fontName='Helvetica-Bold', fontSize=7.5, fillColor=DARK_GREY, textAnchor='middle'))
return FlowchartBox(d, dw, dh)
# ─── Build Summary Compatibility Table ───────────────────────────────────────
def build_abo_table():
header = [Paragraph(t, TABLE_HDR) for t in
["Blood Group", "ABO Antibodies", "RBC Donors", "Plasma Donors", "Rh Note"]]
rows = [
["O", "Anti-A, Anti-B", "O only\n(Universal RBC donor)", "O, A, B, AB", "O neg = universal\npRBC donor"],
["A", "Anti-B", "A, O", "A, AB", ""],
["B", "Anti-A", "B, O", "B, AB", ""],
["AB", "None", "A, B, O, AB\n(Universal recipient)", "AB only\n(Universal plasma)", "AB = universal\nplasma donor"],
]
bgs = [LIGHT_BLUE, LIGHT_GREY, LIGHT_BLUE, LIGHT_GREY]
data = [header]
for i, row in enumerate(rows):
data.append([Paragraph(str(c), TABLE_CELL_BOLD if i == 0 or i == 3 else TABLE_CELL) for c in row])
tbl = Table(data, colWidths=[28*mm, 35*mm, 45*mm, 38*mm, 38*mm])
tbl.setStyle(TableStyle([
('BACKGROUND', (0, 0), (-1, 0), DARK_BLUE),
('ROWBACKGROUNDS', (0, 1), (-1, -1), [LIGHT_BLUE, LIGHT_GREY, LIGHT_BLUE, colors.HexColor("#E8F8F5")]),
('GRID', (0, 0), (-1, -1), 0.5, colors.HexColor("#BDC3C7")),
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
('TOPPADDING', (0, 0), (-1, -1), 4),
('BOTTOMPADDING', (0, 0), (-1, -1), 4),
('LEFTPADDING', (0, 0), (-1, -1), 5),
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
('ROWBACKGROUNDS', (0, 4), (-1, 4), [colors.HexColor("#E8F8F5")]),
]))
return tbl
def build_products_table():
header = [Paragraph(t, TABLE_HDR) for t in
["Blood Product", "Contents", "Storage", "Shelf Life", "Main Indications", "ABO Compat"]]
rows = [
["pRBC", "Red cells in additive solution", "1–6°C", "42 days",
"Anemia, acute blood loss,\nsickle cell, hemolysis", "Required (major)"],
["FFP", "All coag factors, fibrinogen,\nvWF, protein C/S", "–18°C frozen\nThawed: 1–6°C", "1 year frozen\n24h thawed",
"Multiple factor deficiency,\nwarfarin reversal (if no PCC),\nMTP, TTP", "Preferred (minor)"],
["Cryoprecipitate", "Fibrinogen, vWF,\nFactor VIII, XIII", "–18°C frozen", "1 year frozen\n6h pooled thawed",
"Hypofibrinogenemia (<150 mg/dL)\nOB hemorrhage, DIC, cardiac surgery", "Not required (adults)"],
["Platelets", "Platelets in plasma or\nadditive solution", "20–24°C\n(agitated)", "5 days",
"Plt <10K (prophylactic)\nPlt <50K (procedure)\nActive bleeding with thrombocytopenia", "Preferred"],
["Granulocytes", "WBC concentrate", "20–24°C", "24 hours",
"Severe neutropenia with\nlife-threatening infection", "Required;\nMUST irradiate"],
]
data = [header]
for row in rows:
data.append([Paragraph(str(c), TABLE_CELL) for c in row])
tbl = Table(data, colWidths=[28*mm, 38*mm, 24*mm, 22*mm, 47*mm, 25*mm])
tbl.setStyle(TableStyle([
('BACKGROUND', (0, 0), (-1, 0), TEAL),
('ROWBACKGROUNDS', (0, 1), (-1, -1), [LIGHT_TEAL, WHITE, LIGHT_TEAL, WHITE, LIGHT_TEAL]),
('GRID', (0, 0), (-1, -1), 0.5, colors.HexColor("#BDC3C7")),
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
('TOPPADDING', (0, 0), (-1, -1), 4),
('BOTTOMPADDING', (0, 0), (-1, -1), 4),
('LEFTPADDING', (0, 0), (-1, -1), 4),
('FONTNAME', (0, 1), (0, -1), 'Helvetica-Bold'),
]))
return tbl
def build_reactions_table():
header = [Paragraph(t, TABLE_HDR) for t in
["Reaction", "Timing", "Key Features", "Risk/Unit", "Management"]]
rows = [
["Febrile Nonhemolytic\n(FNHTR)", "During / ≤4h",
"Fever ≥1°C rise\nChills, rigors\nNO hemolysis",
"~1:1,100", "Stop Tx → rule out hemolysis\nAntipyretics → restart if OK\nPrevent: leukoreduction"],
["Allergic (mild)", "During / ≤4h",
"Urticaria, pruritus\nFlushing",
"~1:100–1,200", "Antihistamine\nMay restart (slower rate)\nPrevent: antihistamine premedication"],
["Anaphylaxis\n(IgA deficient)", "During / ≤4h",
"Hypotension, stridor\nBronchospasm, shock",
"~1:20,000–50,000", "STOP. Epinephrine\nH1-blocker, corticosteroids\nIgA-deficient or washed units"],
["Acute Hemolytic\n(AHTR)", "During / ≤24h",
"Fever, back pain\nHemoglobinuria\nHypotension, DIC",
"~1:6,000–110,000\nFatal: ~1:100,000", "STOP immediately\nSaline, maintain urine output\nTreat DIC\nRepeat ABO/DAT\nFDA report"],
["TRALI", "≤6h post-Tx",
"Hypoxia, bilateral CXR\ninfiltrates\nNO hypertension",
"~1:5,000–140,000", "Supportive, O2, ventilate\nNO diuretics\nUsually resolves 48–96h"],
["TACO", "During / ≤12h",
"Hypertension\nDyspnea, orthopnea\nElevated BNP",
"~1:9,000", "Slow/stop Tx, sit upright\nO2, furosemide\nBNP/NT-proBNP confirms"],
["Delayed Hemolytic\n(DHTR)", "5–14 days",
"Unexpected Hb fall\nMixed-field DAT\nBilirubinemia",
"~1:3,000–32,000", "Identify antibody\nAntigen-negative units\nUsually self-limiting"],
["Bacterial Sepsis", "During / ≤1h",
"High fever, rigors\nHypotension, shock",
"Plt: ~1:25,000\nRBC: ~1:250,000", "STOP, blood cultures\nBroad-spectrum antibiotics\nICU support"],
["TA-GvHD", "8–10 days",
"Fever, rash, diarrhea\nPancytopenia (BM failure)",
"<1:10,000,000\nFatality ~90%", "Supportive (no effective Tx)\nPREVENT with irradiation"],
]
data = [header]
bgs = []
for row in rows:
data.append([Paragraph(str(c), TABLE_CELL) for c in row])
row_bgs = [LIGHT_BLUE, WHITE, LIGHT_RED, colors.HexColor("#FDEDEC"),
LIGHT_ORANGE, LIGHT_TEAL, LIGHT_BLUE, colors.HexColor("#FDEDEC"), LIGHT_ORANGE]
tbl = Table(data, colWidths=[32*mm, 20*mm, 44*mm, 28*mm, 54*mm])
tbl.setStyle(TableStyle([
('BACKGROUND', (0, 0), (-1, 0), MED_RED),
('ROWBACKGROUNDS', (0, 1), (-1, -1), [LIGHT_BLUE, WHITE, LIGHT_RED, colors.HexColor("#FDEDEC"),
LIGHT_ORANGE, LIGHT_TEAL, LIGHT_BLUE,
colors.HexColor("#FDEDEC"), LIGHT_ORANGE]),
('GRID', (0, 0), (-1, -1), 0.5, colors.HexColor("#BDC3C7")),
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
('TOPPADDING', (0, 0), (-1, -1), 4),
('BOTTOMPADDING', (0, 0), (-1, -1), 4),
('LEFTPADDING', (0, 0), (-1, -1), 4),
('FONTNAME', (0, 1), (0, -1), 'Helvetica-Bold'),
('FONTNAME', (0, 4), (0, 5), 'Helvetica-Bold'),
]))
return tbl
def build_thresholds_table():
header = [Paragraph(t, TABLE_HDR) for t in
["Patient Population", "Hb Threshold (g/dL)", "Notes"]]
rows = [
["Stable, non-cardiac ICU", "< 7 g/dL", "Restrictive strategy; lower mortality vs liberal"],
["Stable surgical patients", "< 7–8 g/dL", "Permit Hb 7-8 in most elective surgery"],
["GI bleeding (no CAD)", "< 8 g/dL", "Restrictive reduces rebleeding & mortality"],
["Portal HTN bleeding", "7–8 g/dL", "Avoid overtransfusion — worsens portal pressure"],
["Known/symptomatic CAD", "> 9 g/dL target", "Liberal threshold; cardiac ischemia must resolve"],
["Symptomatic anemia (any)", "Based on symptoms", "Dyspnea, angina, presyncope = transfuse"],
["Platelets - prophylaxis", "< 10,000/µL", "< 20K with fever; < 50K before procedure"],
["Neurosurgery / eye Sx", "Plt < 100,000/µL", "Higher platelet threshold"],
["FFP (active bleeding)", "INR > 1.5–2.0", "Only with active bleeding; not for routine correction"],
["Cryoprecipitate", "Fibrinogen < 150–200 mg/dL", "With active bleeding; obstetric threshold may be higher"],
]
data = [header]
for row in rows:
data.append([Paragraph(str(c), TABLE_CELL_BOLD if 'Plt' in row[0] or row[0].startswith('Stable') else TABLE_CELL) for c in row])
tbl = Table(data, colWidths=[55*mm, 40*mm, 89*mm])
tbl.setStyle(TableStyle([
('BACKGROUND', (0, 0), (-1, 0), DARK_BLUE),
('ROWBACKGROUNDS', (0, 1), (-1, -1), [LIGHT_BLUE, WHITE] * 6),
('GRID', (0, 0), (-1, -1), 0.5, colors.HexColor("#BDC3C7")),
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
('TOPPADDING', (0, 0), (-1, -1), 4),
('BOTTOMPADDING', (0, 0), (-1, -1), 4),
('LEFTPADDING', (0, 0), (-1, -1), 5),
('FONTNAME', (0, 1), (0, -1), 'Helvetica-Bold'),
('BACKGROUND', (0, 7), (-1, 7), colors.HexColor("#D5EFF7")),
('BACKGROUND', (0, 8), (-1, 8), colors.HexColor("#FEF9E7")),
('BACKGROUND', (0, 9), (-1, 9), colors.HexColor("#FEF9E7")),
]))
return tbl
def build_modifications_table():
header = [Paragraph(t, TABLE_HDR) for t in
["Modification", "Process", "Prevents / Achieves", "Key Indications"]]
rows = [
["Leukoreduction\n(Leukodepleted)", "Filter removes WBCs\n(standard in most blood banks)",
"Prevents FNHTR, reduces CMV\ntransmission, reduces HLA\nalloimmunization",
"All cellular products (standard)\nHLA-sensitized patients\nChronic transfusion"],
["Irradiation\n(Gamma / X-ray)", "Inactivates donor T-lymphocytes",
"Prevents TA-GvHD\n(fatality ~90%)",
"Immunocompromised (HSCT, congenital\nimmunodeficiency), directed (family)\ndonations, HLA-matched platelets,\nfludarabine/cladribine therapy"],
["CMV-Negative\n(Seronegative)", "Donors tested CMV IgG negative",
"Prevents CMV transmission in\nhigh-risk patients",
"CMV-neg immunocompromised patients\nCMV-neg pregnant women\n(Leukoreduced blood is CMV-safe equivalent)"],
["Washed RBC/Plt", "Saline washing removes\nplasma proteins",
"Removes IgA, allergens\nReduces allergic reactions",
"IgA-deficient patients with anti-IgA\nSevere recurrent allergic reactions"],
["Irradiated +\nLeukoreduced", "Both modifications applied",
"Prevents both TA-GvHD\nand CMV/FNHTR",
"All immunocompromised patients\nIntrauterine transfusions (IUT)"],
]
data = [header]
for row in rows:
data.append([Paragraph(str(c), TABLE_CELL) for c in row])
tbl = Table(data, colWidths=[33*mm, 42*mm, 45*mm, 64*mm])
tbl.setStyle(TableStyle([
('BACKGROUND', (0, 0), (-1, 0), colors.HexColor("#6C3483")),
('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.HexColor("#F5EEF8"), WHITE,
colors.HexColor("#F5EEF8"), WHITE,
colors.HexColor("#EAF2FF")]),
('GRID', (0, 0), (-1, -1), 0.5, colors.HexColor("#BDC3C7")),
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
('TOPPADDING', (0, 0), (-1, -1), 4),
('BOTTOMPADDING', (0, 0), (-1, -1), 4),
('LEFTPADDING', (0, 0), (-1, -1), 5),
('FONTNAME', (0, 1), (0, -1), 'Helvetica-Bold'),
]))
return tbl
# ─── Cover Page ───────────────────────────────────────────────────────────────
class CoverPage(Flowable):
def __init__(self):
super().__init__()
self.width = W - 30*mm
self.height = 120*mm
def draw(self):
c = self.canv
# Background gradient simulation
c.setFillColor(DARK_BLUE)
c.roundRect(0, 0, self.width, self.height, 8*mm, fill=1, stroke=0)
# Decorative red strip
c.setFillColor(MED_RED)
c.rect(0, self.height - 18*mm, self.width, 18*mm, fill=1, stroke=0)
# Red circle decoration
c.setFillColor(colors.HexColor("#3D6B99"))
c.circle(self.width - 20*mm, 20*mm, 30*mm, fill=1, stroke=0)
c.setFillColor(colors.HexColor("#2A5580"))
c.circle(self.width - 15*mm, 15*mm, 18*mm, fill=1, stroke=0)
# Gold accent line
c.setFillColor(GOLD)
c.rect(12*mm, self.height - 18*mm - 3*mm, self.width - 24*mm, 3*mm, fill=1, stroke=0)
# Title
c.setFillColor(WHITE)
c.setFont('Helvetica-Bold', 22)
c.drawString(14*mm, self.height - 13*mm, "Blood Transfusion Protocols")
c.setFont('Helvetica-Bold', 16)
c.setFillColor(GOLD)
c.drawString(14*mm, self.height - 38*mm, "Clinical Reference & Reaction Flowcharts")
c.setFillColor(colors.HexColor("#AED6F1"))
c.setFont('Helvetica', 10)
c.drawString(14*mm, self.height - 52*mm, "Blood Products | Transfusion Thresholds | ABO Compatibility")
c.drawString(14*mm, self.height - 63*mm, "Transfusion Reactions | Massive Transfusion Protocol | Special Modifications")
# Bottom
c.setFillColor(colors.HexColor("#AED6F1"))
c.setFont('Helvetica', 9)
c.drawString(14*mm, 12*mm, "Based on: Tietz Lab Medicine 7e | Goldman-Cecil Medicine | Miller's Anesthesia 10e | Quick Compendium 5e")
c.setFillColor(GOLD)
c.drawRightString(self.width - 8*mm, 12*mm, "July 2026")
def wrap(self, avW, avH):
return self.width, self.height
# ─── Build Document ───────────────────────────────────────────────────────────
def build_pdf():
doc = SimpleDocTemplate(
OUTPUT_PATH,
pagesize=A4,
leftMargin=15*mm, rightMargin=15*mm,
topMargin=15*mm, bottomMargin=22*mm,
title="Blood Transfusion Protocols & Reference",
author="Orris Medical Assistant",
subject="Blood Transfusion Clinical Reference"
)
story = []
S = Spacer
# ── Cover ──
story.append(CoverPage())
story.append(S(1, 8*mm))
# TOC-like summary box
toc_data = [
[Paragraph("📋 Contents", ParagraphStyle('toc_hdr', fontName='Helvetica-Bold',
fontSize=10, textColor=DARK_BLUE, leading=14))],
[Paragraph(
"<font color='#2E86C1'>1.</font> Blood Products & Storage "
"<font color='#2E86C1'>2.</font> ABO/Rh Compatibility "
"<font color='#2E86C1'>3.</font> Transfusion Thresholds<br/>"
"<font color='#2E86C1'>4.</font> pRBC Decision Flowchart "
"<font color='#2E86C1'>5.</font> Transfusion Reaction Flowchart "
"<font color='#2E86C1'>6.</font> Reaction Reference Table<br/>"
"<font color='#2E86C1'>7.</font> Massive Transfusion Protocol "
"<font color='#2E86C1'>8.</font> Special Blood Modifications "
"<font color='#2E86C1'>9.</font> Key Tests & Concepts",
ParagraphStyle('toc_body', fontName='Helvetica', fontSize=8.5,
textColor=DARK_GREY, leading=14)
)]
]
toc_tbl = Table(toc_data, colWidths=[W - 30*mm])
toc_tbl.setStyle(TableStyle([
('BACKGROUND', (0, 0), (-1, -1), LIGHT_BLUE),
('BOX', (0, 0), (-1, -1), 1.5, MED_BLUE),
('TOPPADDING', (0, 0), (-1, -1), 6),
('BOTTOMPADDING', (0, 0), (-1, -1), 6),
('LEFTPADDING', (0, 0), (-1, -1), 10),
]))
story.append(toc_tbl)
story.append(PageBreak())
# ── Section 1: Blood Products ──
story.append(SectionHeader("1 Blood Products & Storage", TEAL))
story.append(S(1, 3*mm))
story.append(build_products_table())
story.append(S(1, 3*mm))
story.append(Paragraph(
"<b>Key Points:</b> Each pRBC unit raises Hb by ~1 g/dL in an average adult. Platelets are stored at room temperature "
"on an agitator — highest bacterial contamination risk. FFP must be ABO compatible (plasma contains antibodies). "
"Cryoprecipitate is the preferred fibrinogen source; NOT interchangeable with FFP.",
BODY_STYLE))
story.append(S(1, 5*mm))
# ── Section 2: ABO Compatibility ──
story.append(SectionHeader("2 ABO & Rh Compatibility", DARK_BLUE))
story.append(S(1, 3*mm))
story.append(build_abo_table())
story.append(S(1, 3*mm))
story.append(BoxedNote(
"EMERGENCY: Group O pRBCs (O-negative for females of childbearing potential) + Group AB plasma before type known",
bg=LIGHT_BLUE, border=MED_BLUE, icon="E"))
story.append(S(1, 3*mm))
story.append(Paragraph(
"<b>Rh(D):</b> D-negative females of childbearing age MUST receive D-negative pRBCs to prevent anti-D formation "
"(risk ~10–20% per D+ unit), which can cause haemolytic disease of the fetus/newborn (HDN). "
"Males and post-menopausal females can receive D+ components if D- is scarce. "
"<b>Group A plasma</b> is increasingly used in MTP due to scarcity of AB (only 4% of population).",
BODY_STYLE))
story.append(S(1, 5*mm))
# ── Section 3: Thresholds ──
story.append(SectionHeader("3 Evidence-Based Transfusion Thresholds", DARK_BLUE))
story.append(S(1, 3*mm))
story.append(build_thresholds_table())
story.append(S(1, 3*mm))
story.append(Paragraph(
"<b>Restrictive strategy (Hb threshold 7–8 g/dL)</b> is associated with lower all-cause mortality and rebleeding "
"rates in RCTs for GI bleeding. <b>One-unit-at-a-time</b> transfusion with reassessment is recommended. "
"Healthy volunteers can tolerate Hb as low as 5.1 g/dL short-term.",
BODY_STYLE))
story.append(PageBreak())
# ── Section 4: pRBC Decision Flowchart ──
story.append(SectionHeader("4 pRBC Transfusion Decision Flowchart", DARK_BLUE))
story.append(S(1, 4*mm))
story.append(build_prbc_flowchart())
story.append(S(1, 2*mm))
story.append(Paragraph(
"Fig. 1 - pRBC transfusion decision algorithm. In life-threatening hemorrhage, use uncrossmatched "
"group O blood while type and screen is being performed. For stable patients, base decision on Hb thresholds above.",
CAPTION))
story.append(S(1, 5*mm))
# Pretransfusion testing box
story.append(SectionHeader("5 Pretransfusion Testing", TEAL))
story.append(S(1, 3*mm))
pre_data = [
[Paragraph("Test", TABLE_HDR), Paragraph("What It Does", TABLE_HDR),
Paragraph("When Valid", TABLE_HDR), Paragraph("Key Point", TABLE_HDR)],
[Paragraph("Type & Screen\n(T&S)", TABLE_CELL_BOLD),
Paragraph("ABO/D typing + antibody screen\n(unexpected alloantibodies)", TABLE_CELL),
Paragraph("No recent Tx/pregnancy:\nup to 1 month\nRecent Tx/pregnancy:\n3 days only", TABLE_CELL),
Paragraph("Most hemolytic reactions = clerical/\nID errors, not testing failures", TABLE_CELL)],
[Paragraph("Type & Crossmatch", TABLE_CELL_BOLD),
Paragraph("T&S + select and reserve\nspecific RBC units for patient", TABLE_CELL),
Paragraph("Same as T&S", TABLE_CELL),
Paragraph("Electronic crossmatch if 2 concordant\nABO types & negative antibody screen", TABLE_CELL)],
[Paragraph("DAT\n(Direct Coombs)", TABLE_CELL_BOLD),
Paragraph("Detects Ab/complement bound\nto patient's own RBCs", TABLE_CELL),
Paragraph("Any time", TABLE_CELL),
Paragraph("Positive in: AIHA, AHTR, HDN,\ndrug-induced hemolysis", TABLE_CELL)],
[Paragraph("IAT\n(Indirect Coombs)", TABLE_CELL_BOLD),
Paragraph("Detects free antibodies in\npatient serum vs donor RBCs", TABLE_CELL),
Paragraph("Any time", TABLE_CELL),
Paragraph("Used for antibody screening,\nidentification, and crossmatch", TABLE_CELL)],
]
pre_tbl = Table(pre_data, colWidths=[30*mm, 52*mm, 38*mm, 64*mm])
pre_tbl.setStyle(TableStyle([
('BACKGROUND', (0, 0), (-1, 0), TEAL),
('ROWBACKGROUNDS', (0, 1), (-1, -1), [LIGHT_TEAL, WHITE, LIGHT_TEAL, WHITE]),
('GRID', (0, 0), (-1, -1), 0.5, colors.HexColor("#BDC3C7")),
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
('TOPPADDING', (0, 0), (-1, -1), 4),
('BOTTOMPADDING', (0, 0), (-1, -1), 4),
('LEFTPADDING', (0, 0), (-1, -1), 5),
]))
story.append(pre_tbl)
story.append(PageBreak())
# ── Section 6: Reaction Flowchart ──
story.append(SectionHeader("6 Transfusion Reaction Management Flowchart", MED_RED))
story.append(S(1, 4*mm))
story.append(build_reaction_flowchart())
story.append(S(1, 2*mm))
story.append(Paragraph(
"Fig. 2 - Algorithm for managing suspected transfusion reactions. "
"TRALI = non-cardiogenic pulmonary edema (no diuretics). TACO = cardiogenic overload (give diuretics). "
"Key distinction: TACO has hypertension + elevated BNP; TRALI has hypotension/normotension, no BNP rise.",
CAPTION))
story.append(PageBreak())
# ── Section 7: Reactions Table ──
story.append(SectionHeader("7 Transfusion Reactions Reference Table", MED_RED))
story.append(S(1, 3*mm))
story.append(build_reactions_table())
story.append(S(1, 3*mm))
story.append(Paragraph(
"<b>Most common reaction:</b> FNHTR (~1:1,100). "
"<b>Most dangerous acute reaction:</b> AHTR (usually ABO incompatibility from clerical error). "
"<b>Leading cause of transfusion death (USA):</b> TRALI. "
"<b>Highest preventable mortality:</b> TA-GvHD (prevent with irradiation).",
BODY_STYLE))
story.append(PageBreak())
# ── Section 8: MTP ──
story.append(SectionHeader("8 Massive Transfusion Protocol (MTP)", MED_RED))
story.append(S(1, 4*mm))
story.append(build_mtp_flowchart())
story.append(S(1, 3*mm))
story.append(Paragraph(
"Fig. 3 - Massive Transfusion Protocol overview. The 1:1:1 ratio (FFP:Plt:pRBC) is supported by the "
"PROPPR trial. Communicate clearly with blood bank — activate and deactivate MTP explicitly. "
"Monitor: iCa²⁺ (citrate chelation), K⁺ (stored RBC), temperature (use blood warmers), and coagulation.",
CAPTION))
story.append(S(1, 5*mm))
# Lethal triad box
triad_data = [
[Paragraph("THE LETHAL TRIAD OF MASSIVE HEMORRHAGE", ParagraphStyle('triad_hdr',
fontName='Helvetica-Bold', fontSize=10, textColor=WHITE, alignment=TA_CENTER))],
[Table([[
Paragraph("HYPOTHERMIA\n< 35°C\n→ Use blood warmers\nwarm IV fluids\nBair Hugger blankets",
ParagraphStyle('triad', fontName='Helvetica-Bold', fontSize=8.5,
textColor=DARK_BLUE, alignment=TA_CENTER, leading=13)),
Paragraph("ACIDOSIS\npH < 7.35\n→ Balanced MTP\nBuffer with bicarb\nif persistent",
ParagraphStyle('triad', fontName='Helvetica-Bold', fontSize=8.5,
textColor=DARK_BLUE, alignment=TA_CENTER, leading=13)),
Paragraph("COAGULOPATHY\nDilutional / consumptive\n→ Balanced ratio\nAvoid pRBC-only\nCheck TEG/ROTEM",
ParagraphStyle('triad', fontName='Helvetica-Bold', fontSize=8.5,
textColor=DARK_BLUE, alignment=TA_CENTER, leading=13)),
], colWidths=[52*mm, 52*mm, 52*mm])],
]
triad_tbl = Table(triad_data, colWidths=[W - 30*mm])
triad_tbl.setStyle(TableStyle([
('BACKGROUND', (0, 0), (0, 0), MED_RED),
('BACKGROUND', (0, 1), (0, 1), LIGHT_ORANGE),
('BOX', (0, 0), (-1, -1), 1.5, MED_RED),
('TOPPADDING', (0, 0), (-1, -1), 6),
('BOTTOMPADDING', (0, 0), (-1, -1), 6),
('LEFTPADDING', (0, 0), (-1, -1), 6),
]))
story.append(triad_tbl)
story.append(PageBreak())
# ── Section 9: Special Modifications ──
story.append(SectionHeader("9 Special Blood Product Modifications", colors.HexColor("#6C3483")))
story.append(S(1, 3*mm))
story.append(build_modifications_table())
story.append(S(1, 5*mm))
# ── Section 10: Key Tests & Concepts ──
story.append(SectionHeader("10 Key Blood Bank Concepts for Clinicians", DARK_BLUE))
story.append(S(1, 3*mm))
concepts = [
("Platelet Refractoriness",
"Failure to achieve expected platelet increment. Check CCI at 1 hour post-transfusion: "
"CCI < 5,000–7,500 = immune cause (HLA alloantibodies most common). "
"CCI normal at 1h but drops later = non-immune (DIC, sepsis, splenomegaly). "
"Immune: provide HLA-matched apheresis platelets."),
("Delayed Serologic Transfusion Reaction (DSTR)",
"New alloantibody detected after transfusion WITHOUT hemolysis. More common than DHTR (3:1 ratio). "
"Most commonly implicated antigens: Kidd (Jk), E, c (Rh), Kell, Duffy. "
"Kidd antibodies can cause severe intravascular hemolysis."),
("IgA Deficiency & Anaphylaxis",
"Affects 1 in 700 people. Patients with anti-IgA antibodies are at risk for anaphylaxis to blood products. "
"Test for IgA deficiency in any patient with severe allergic transfusion reaction. "
"Management: IgA-deficient donor units OR washed products."),
("Autologous Transfusion Options",
"1) Preoperative autologous donation (PAD): donate weeks before elective surgery. "
"2) Acute normovolemic hemodilution (ANH): remove blood at surgery start, replace with crystalloid, reinfuse later. "
"3) Cell salvage: contraindicated in cancer surgery and infected fields."),
("Transfusion in Jehovah's Witnesses",
"Refuse allogeneic blood on religious grounds. Alternatives: cell salvage (if accepted in closed circuit), "
"EPO + iron preoperatively, fibrin glue, minimize iatrogenic blood loss. "
"Always obtain informed refusal. Ethics committee for unconscious patients."),
("Hyperhemolysis in Sickle Cell Disease",
"Rare but life-threatening: both donor AND patient's own cells are destroyed post-transfusion. "
"Paradox: further transfusion may worsen hemolysis. Use extended phenotype-matched RBCs. "
"Exchange transfusion (apheresis) preferred for stroke prevention/treatment."),
]
for title, text in concepts:
cdata = [[
Paragraph(title, TABLE_CELL_BOLD),
Paragraph(text, TABLE_CELL),
]]
ctbl = Table(cdata, colWidths=[45*mm, 139*mm])
ctbl.setStyle(TableStyle([
('BACKGROUND', (0, 0), (0, 0), LIGHT_BLUE),
('BACKGROUND', (1, 0), (1, 0), WHITE),
('BOX', (0, 0), (-1, -1), 0.5, colors.HexColor("#BDC3C7")),
('INNERGRID', (0, 0), (-1, -1), 0.5, colors.HexColor("#BDC3C7")),
('TOPPADDING', (0, 0), (-1, -1), 5),
('BOTTOMPADDING', (0, 0), (-1, -1), 5),
('LEFTPADDING', (0, 0), (-1, -1), 6),
('VALIGN', (0, 0), (-1, -1), 'TOP'),
]))
story.append(ctbl)
story.append(S(1, 1*mm))
story.append(S(1, 6*mm))
# ── Final Reference Box ──
final_data = [[
Paragraph(
"<b>Sources & References</b><br/>"
"Tietz Textbook of Laboratory Medicine, 7th Edition (Chapters 92, 93) | "
"Goldman-Cecil Medicine, International Edition (Chapter 162) | "
"Quick Compendium of Clinical Pathology, 5th Edition (Chapter 2.8) | "
"Miller's Anesthesia, 10th Edition (Chapter 45) | "
"Rosen's Emergency Medicine, 10th Edition",
ParagraphStyle('refs', fontName='Helvetica', fontSize=7.5, textColor=MID_GREY,
leading=12, alignment=TA_CENTER)
)
]]
final_tbl = Table(final_data, colWidths=[W - 30*mm])
final_tbl.setStyle(TableStyle([
('BACKGROUND', (0, 0), (-1, -1), LIGHT_GREY),
('BOX', (0, 0), (-1, -1), 1, MID_GREY),
('TOPPADDING', (0, 0), (-1, -1), 8),
('BOTTOMPADDING', (0, 0), (-1, -1), 8),
('LEFTPADDING', (0, 0), (-1, -1), 10),
]))
story.append(final_tbl)
doc.build(story, onFirstPage=on_first_page, onLaterPages=on_page)
print(f"PDF created: {OUTPUT_PATH}")
return OUTPUT_PATH
if __name__ == '__main__':
build_pdf()
cd /tmp/workspace/blood-transfusion-pdf && python3 generate_pdf.py 2>&1
~/blood-transfusion-pdf/generate_pdf.py
Successfully replaced 1 instance(s) of the string in '/tmp/workspace/blood-transfusion-pdf/generate_pdf.py'
cd /tmp/workspace/blood-transfusion-pdf && python3 generate_pdf.py 2>&1
~/blood-transfusion-pdf/generate_pdf.py
Successfully replaced 1 instance(s) of the string in '/tmp/workspace/blood-transfusion-pdf/generate_pdf.py'
cd /tmp/workspace/blood-transfusion-pdf && python3 generate_pdf.py 2>&1
ls -lh /tmp/workspace/blood-transfusion-pdf/Blood_Transfusion_Protocols.pdf
Blood Transfusion Protocols
PDF Document · PDF
| Section | Content |
|---|---|
| Cover | Title page with color design and source references |
| 1 - Blood Products & Storage | Full table: pRBC, FFP, Cryo, Platelets, Granulocytes - contents, storage, shelf life, indications, ABO compatibility |
| 2 - ABO/Rh Compatibility | Complete compatibility matrix for pRBCs and plasma; Rh rules; emergency transfusion guide |
| 3 - Transfusion Thresholds | Evidence-based Hb triggers by patient population; platelet/FFP/cryo thresholds |
| 4 - pRBC Decision Flowchart | Step-by-step algorithm: emergency vs stable, T&S, Hb threshold branches |
| 5 - Pretransfusion Testing | T&S, crossmatch, DAT, IAT - what each test does, validity period, key points |
| 6 - Reaction Management Flowchart | Full color coded algorithm: STOP → clerical check → labs → hemolysis? → TRALI vs TACO branches; FDA reporting rule |
| 7 - Reactions Reference Table | All 9 major reactions with timing, features, risk per unit, and management |
| 8 - Massive Transfusion Protocol | MTP trigger, 1:1:1 pack composition, Lethal Triad box (hypothermia/acidosis/coagulopathy) |
| 9 - Special Modifications | Leukoreduction, irradiation, CMV-negative, washed - indications for each |
| 10 - Key Concepts | Platelet refractoriness, DHTR/DSTR, IgA deficiency, autologous options, Jehovah's Witnesses, hyperhemolysis in SCD |