Bacteria and culture media pdf on this please
mkdir -p /home/daytona/workspace/bacteria-culture-media && ls /home/daytona/work…
~/bacteria-culture-media/generate_pdf.py
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm
from reportlab.lib import colors
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, KeepTogether
)
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
OUTPUT = "/home/daytona/workspace/bacteria-culture-media/Bacteria_and_Culture_Media.pdf"
doc = SimpleDocTemplate(
OUTPUT, pagesize=A4,
leftMargin=2*cm, rightMargin=2*cm,
topMargin=2.5*cm, bottomMargin=2.5*cm
)
styles = getSampleStyleSheet()
# Custom styles
title_style = ParagraphStyle(
"Title", parent=styles["Title"],
fontSize=22, textColor=colors.HexColor("#1a3a5c"),
spaceAfter=6, alignment=TA_CENTER, fontName="Helvetica-Bold"
)
subtitle_style = ParagraphStyle(
"Subtitle", parent=styles["Normal"],
fontSize=11, textColor=colors.HexColor("#4a4a4a"),
spaceAfter=20, alignment=TA_CENTER, fontName="Helvetica-Oblique"
)
h1_style = ParagraphStyle(
"H1", parent=styles["Heading1"],
fontSize=15, textColor=colors.white,
backColor=colors.HexColor("#1a3a5c"),
spaceAfter=8, spaceBefore=16,
fontName="Helvetica-Bold", leftIndent=0,
borderPad=6
)
h2_style = ParagraphStyle(
"H2", parent=styles["Heading2"],
fontSize=12, textColor=colors.HexColor("#1a3a5c"),
spaceAfter=6, spaceBefore=12,
fontName="Helvetica-Bold",
borderWidth=0, borderColor=colors.HexColor("#1a3a5c"),
leftIndent=0
)
body_style = ParagraphStyle(
"Body", parent=styles["Normal"],
fontSize=10, leading=15, alignment=TA_JUSTIFY,
spaceAfter=8, fontName="Helvetica"
)
bullet_style = ParagraphStyle(
"Bullet", parent=styles["Normal"],
fontSize=10, leading=14,
spaceAfter=4, leftIndent=18, bulletIndent=8,
fontName="Helvetica"
)
note_style = ParagraphStyle(
"Note", parent=styles["Normal"],
fontSize=9, textColor=colors.HexColor("#555555"),
backColor=colors.HexColor("#f0f4f8"),
borderPad=6, leading=13, leftIndent=8, rightIndent=8,
fontName="Helvetica-Oblique", spaceAfter=10
)
source_style = ParagraphStyle(
"Source", parent=styles["Normal"],
fontSize=8, textColor=colors.HexColor("#888888"),
alignment=TA_CENTER, spaceAfter=4
)
def h1(text):
return Paragraph(f" {text}", h1_style)
def h2(text):
return Paragraph(text, h2_style)
def body(text):
return Paragraph(text, body_style)
def bullet(text):
return Paragraph(f"• {text}", bullet_style)
def note(text):
return Paragraph(text, note_style)
def spacer(h=0.3):
return Spacer(1, h*cm)
def hr():
return HRFlowable(width="100%", thickness=0.5, color=colors.HexColor("#cccccc"), spaceAfter=6)
story = []
# ─── TITLE PAGE ────────────────────────────────────────────────────────────────
story.append(spacer(3))
story.append(Paragraph("Bacteria and Culture Media", title_style))
story.append(Paragraph("A Comprehensive Clinical Microbiology Reference", subtitle_style))
story.append(hr())
story.append(Paragraph("Sources: Medical Microbiology 9e | Sherris & Ryan's Medical Microbiology 8th Ed | Tietz Laboratory Medicine 7th Ed", source_style))
story.append(spacer(1.5))
# ─── SECTION 1: INTRODUCTION TO BACTERIA ──────────────────────────────────────
story.append(h1("1. Introduction to Bacteria"))
story.append(body(
"Bacteria are prokaryotic microorganisms that are of fundamental importance in human health and disease. "
"They are classified by a variety of characteristics including morphology (shape and arrangement), staining "
"properties (Gram-positive, Gram-negative, acid-fast), oxygen requirements, metabolic activities, and "
"antigenic properties. Understanding these characteristics is essential for selecting the appropriate culture "
"media and laboratory methods for their isolation and identification."
))
story.append(h2("1.1 Classification by Morphology"))
story.append(bullet("<b>Cocci:</b> Spherical bacteria. Arrangements include diplococci (pairs), streptococci (chains), staphylococci (clusters), and tetrads."))
story.append(bullet("<b>Bacilli (Rods):</b> Cylindrical or rod-shaped bacteria. May be single, in pairs (diplobacilli), or chains (streptobacilli). Examples: E. coli, Salmonella."))
story.append(bullet("<b>Spirilla / Spirochetes:</b> Spiral-shaped bacteria. Spirochetes have a flexible cell wall and move by axial filaments. Example: Treponema pallidum."))
story.append(bullet("<b>Vibrios:</b> Comma-shaped curved rods. Example: Vibrio cholerae."))
story.append(bullet("<b>Pleomorphic bacteria:</b> Organisms that vary widely in shape, e.g., Corynebacterium, Mycoplasma."))
story.append(h2("1.2 Gram Staining"))
story.append(body(
"The Gram stain is the single most important and widely used stain in clinical microbiology. "
"It divides bacteria into two large groups based on the structure of their cell walls:"
))
story.append(bullet("<b>Gram-positive:</b> Thick peptidoglycan layer retains crystal violet-iodine complex after decolorization - stain purple. Examples: Staphylococcus, Streptococcus, Clostridium."))
story.append(bullet("<b>Gram-negative:</b> Thin peptidoglycan layer with an outer membrane - lose crystal violet and take up safranin counterstain - stain pink/red. Examples: E. coli, Neisseria, Pseudomonas."))
story.append(note(
"Clinical Relevance: Gram stain result guides empiric antibiotic therapy before culture results are available. "
"Gram-positive organisms are targeted by beta-lactams, vancomycin, and related agents; "
"Gram-negatives require agents that penetrate the outer membrane (e.g., aminoglycosides, fluoroquinolones, carbapenems)."
))
story.append(h2("1.3 Other Important Stains"))
story.append(bullet("<b>Acid-Fast (Ziehl-Neelsen / Kinyoun):</b> Detects Mycobacterium species (M. tuberculosis, M. leprae) and Nocardia. The waxy mycolic acid cell wall resists decolorization with acid-alcohol."))
story.append(bullet("<b>Fluorescent stains:</b> Auramine-rhodamine for acid-fast organisms; acridine orange for bacteria/fungi in low-density specimens; calcofluor white for fungal chitin."))
story.append(bullet("<b>Fluorescent antibody stains:</b> Specific antibodies labeled with fluorescent dyes for rapid detection and identification."))
story.append(bullet("<b>Endospore stain (Schaeffer-Fulton):</b> Detects spores in Bacillus and Clostridium species."))
story.append(h2("1.4 Oxygen Requirements"))
oxy_data = [
["Category", "Description", "Examples"],
["Obligate aerobes", "Require O₂ for growth", "Mycobacterium, Pseudomonas"],
["Obligate anaerobes", "Cannot tolerate O₂; killed by it", "Bacteroides, Clostridium, Fusobacterium"],
["Facultative anaerobes", "Grow with or without O₂", "E. coli, Staphylococcus, Enterococcus"],
["Microaerophiles", "Require reduced O₂ (5-10%) and elevated CO₂", "Campylobacter, Helicobacter"],
["Aerotolerant anaerobes", "Grow in presence of O₂ but don't use it", "Lactobacillus, Streptococcus"],
["Capnophiles", "Require elevated CO₂ (5-10%)", "Neisseria gonorrhoeae, Haemophilus"],
]
oxy_table = Table(oxy_data, colWidths=[4.5*cm, 7*cm, 5.5*cm])
oxy_table.setStyle(TableStyle([
("BACKGROUND", (0, 0), (-1, 0), colors.HexColor("#1a3a5c")),
("TEXTCOLOR", (0, 0), (-1, 0), colors.white),
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
("FONTSIZE", (0, 0), (-1, -1), 9),
("ROWBACKGROUNDS", (0, 1), (-1, -1), [colors.HexColor("#f5f9ff"), colors.white]),
("GRID", (0, 0), (-1, -1), 0.4, colors.HexColor("#cccccc")),
("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
("TOPPADDING", (0, 0), (-1, -1), 5),
("BOTTOMPADDING", (0, 0), (-1, -1), 5),
("LEFTPADDING", (0, 0), (-1, -1), 6),
]))
story.append(oxy_table)
story.append(spacer())
# ─── SECTION 2: IN VITRO CULTURE ──────────────────────────────────────────────
story.append(h1("2. In Vitro Culture"))
story.append(body(
"The success of culture methods is defined by the biology of the organism, the site of the infection, the patient's "
"immune response, and the quality of the culture media. The goal of culturing bacteria is to support growth, "
"isolate individual colonies, and permit identification of clinically relevant organisms."
))
story.append(body(
"Relatively few laboratories prepare their own media today. Most media are produced by large commercial companies. "
"Dehydrated formulations of most media are available for specialized testing. Key considerations include:"
))
story.append(bullet("Medium must support growth of the target organism (enrichment requirements)"))
story.append(bullet("Medium may need to suppress normal flora (selectivity)"))
story.append(bullet("Patient immunity may suppress pathogen numbers - requiring sensitive techniques"))
story.append(bullet("Septic patients often have <1 organism/mL blood - requiring large-volume inoculation into enrichment broths"))
story.append(bullet("Obligate intracellular pathogens (Chlamydia, Rickettsia) require living cell cultures"))
story.append(spacer(0.5))
# ─── SECTION 3: TYPES OF CULTURE MEDIA ───────────────────────────────────────
story.append(h1("3. Types of Culture Media"))
story.append(body(
"Culture media are classified into four main categories: (1) Enriched/Nonselective, (2) Selective, "
"(3) Differential, and (4) Specialized media. In practice, many media combine two or more of these properties."
))
# 3.1 Enriched/Nonselective
story.append(h2("3.1 Enriched Nonselective Media"))
story.append(body(
"These media support the growth of most organisms without fastidious growth requirements. They are made with "
"enzymatic or acid digests of animal or plant products (muscle, milk, soybean). The digest provides polypeptides, "
"amino acids, trace metals, coenzymes, and growth factors."
))
non_sel_data = [
["Medium", "Composition / Key Features", "Uses"],
["Blood Agar (BA)", "Basal medium (tryptic soy, BHI, or Brucella base) + 5% sheep/horse/rabbit blood", "Recovery of most bacteria and fungi; detection of hemolysis (alpha, beta, gamma)"],
["Chocolate Agar (CHOC)", "Heated blood agar - hemoglobin released; turns brown", "Fastidious organisms: Haemophilus, Neisseria gonorrhoeae, HACEK group"],
["Mueller-Hinton Agar (MHA)", "Beef extract, casein hydrolysate, starch, divalent cations (Ca²⁺, Mg²⁺)", "Standard for antimicrobial susceptibility testing (AST/disk diffusion, MIC)"],
["Thioglycolate Broth (THIO)", "Casein digest, glucose, yeast extract, cysteine, sodium thioglycolate ± hemin + Vit K", "Enrichment of aerobic and anaerobic bacteria; anaerobic recovery"],
["Sabouraud Dextrose Agar (SDA)", "Casein + animal tissue digests, glucose (2-4%), pH 5.6", "Primary isolation of fungi; modified with antibiotics to suppress bacteria"],
["Tryptic Soy Broth (TSB)", "Enzymatic digest of casein and soybean meal, NaCl", "General enrichment broth; blood culture base"],
["Brain-Heart Infusion (BHI)", "Brain and heart infusion, peptone, glucose, NaCl", "Fastidious bacteria; fungal isolation when agar is added"],
]
non_sel_table = Table(non_sel_data, colWidths=[4*cm, 6.5*cm, 6.5*cm])
non_sel_table.setStyle(TableStyle([
("BACKGROUND", (0, 0), (-1, 0), colors.HexColor("#2e7d32")),
("TEXTCOLOR", (0, 0), (-1, 0), colors.white),
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
("FONTSIZE", (0, 0), (-1, -1), 8.5),
("ROWBACKGROUNDS", (0, 1), (-1, -1), [colors.HexColor("#f1f8f1"), colors.white]),
("GRID", (0, 0), (-1, -1), 0.4, colors.HexColor("#cccccc")),
("VALIGN", (0, 0), (-1, -1), "TOP"),
("TOPPADDING", (0, 0), (-1, -1), 5),
("BOTTOMPADDING", (0, 0), (-1, -1), 5),
("LEFTPADDING", (0, 0), (-1, -1), 5),
("WORDWRAP", (0, 0), (-1, -1), True),
]))
story.append(non_sel_table)
story.append(spacer())
# Hemolysis box
story.append(h2("3.1.1 Hemolysis Patterns on Blood Agar"))
hem_data = [
["Type", "Pattern", "Organisms"],
["Alpha (α) hemolysis", "Partial / Green hemolysis - methemoglobin formation; green halo around colony", "Streptococcus pneumoniae, viridans Streptococci"],
["Beta (β) hemolysis", "Complete lysis of RBCs - clear zone around colony", "Group A Strep (S. pyogenes), Group B Strep (S. agalactiae), S. aureus, Listeria"],
["Gamma (γ) hemolysis", "No hemolysis - no change around colony", "Enterococcus faecalis, S. epidermidis"],
]
hem_table = Table(hem_data, colWidths=[3.5*cm, 6.5*cm, 7*cm])
hem_table.setStyle(TableStyle([
("BACKGROUND", (0, 0), (-1, 0), colors.HexColor("#7b1fa2")),
("TEXTCOLOR", (0, 0), (-1, 0), colors.white),
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
("FONTSIZE", (0, 0), (-1, -1), 9),
("ROWBACKGROUNDS", (0, 1), (-1, -1), [colors.HexColor("#fdf3ff"), colors.white]),
("GRID", (0, 0), (-1, -1), 0.4, colors.HexColor("#cccccc")),
("VALIGN", (0, 0), (-1, -1), "TOP"),
("TOPPADDING", (0, 0), (-1, -1), 5),
("BOTTOMPADDING", (0, 0), (-1, -1), 5),
("LEFTPADDING", (0, 0), (-1, -1), 5),
]))
story.append(hem_table)
story.append(spacer())
# 3.2 Selective Media
story.append(h2("3.2 Selective Media"))
story.append(body(
"Selective media contain dyes, chemical additives, bile salts, or antimicrobial agents to inhibit contaminating "
"flora while permitting growth of the target organism. They are especially important when isolating pathogens "
"from sites with abundant normal flora (stool, respiratory tract, genital tract)."
))
sel_data = [
["Medium", "Inhibitory Agents", "Target Organism(s)"],
["MacConkey Agar (MAC)", "Crystal violet, bile salts", "Gram-negative Enterobacteriaceae; inhibits Gram-positives"],
["Mannitol Salt Agar (MSA)", "7.5% NaCl", "Staphylococci; S. aureus ferments mannitol (yellow)"],
["Hektoen Enteric (HE)", "Bile salts, bromthymol blue, acid fuchsin", "Salmonella and Shigella from stool"],
["Xylose-Lysine Deoxycholate (XLD)", "Sodium deoxycholate; xylose, lysine, lactose, sucrose; phenol red", "Salmonella (black colonies - H₂S); Shigella (red/pink)"],
["Thayer-Martin (Modified)", "Vancomycin, colistin, nystatin, trimethoprim", "Neisseria gonorrhoeae and N. meningitidis from genital/throat"],
["Lowenstein-Jensen (LJ)", "Malachite green, egg base", "Mycobacterium tuberculosis and other mycobacteria"],
["Middlebrook 7H10/7H11", "OADC supplement; selective antibiotics if needed", "Mycobacteria - better than LJ for colony count and sensitivity"],
["Campylobacter (CAMPY) Agar", "Vancomycin, trimethoprim, polymyxin B, amphotericin, cephalothin", "Campylobacter jejuni/coli (incubate 42°C, microaerophilic)"],
["Cefsulodin-Irgasan-Novobiocin (CIN)", "Cefsulodin, Irgasan, novobiocin", "Yersinia enterocolitica (bull's-eye colonies)"],
["Bordet-Gengou (BG) Agar", "Potato starch, glycerol, sheep blood", "Bordetella pertussis (whooping cough)"],
["TCBS Agar", "Thiosulfate, citrate, bile salts, sucrose; bromthymol blue", "Vibrio cholerae (yellow) and V. parahaemolyticus (blue-green)"],
]
sel_table = Table(sel_data, colWidths=[4.5*cm, 5.5*cm, 7*cm])
sel_table.setStyle(TableStyle([
("BACKGROUND", (0, 0), (-1, 0), colors.HexColor("#c62828")),
("TEXTCOLOR", (0, 0), (-1, 0), colors.white),
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
("FONTSIZE", (0, 0), (-1, -1), 8.5),
("ROWBACKGROUNDS", (0, 1), (-1, -1), [colors.HexColor("#fff5f5"), colors.white]),
("GRID", (0, 0), (-1, -1), 0.4, colors.HexColor("#cccccc")),
("VALIGN", (0, 0), (-1, -1), "TOP"),
("TOPPADDING", (0, 0), (-1, -1), 5),
("BOTTOMPADDING", (0, 0), (-1, -1), 5),
("LEFTPADDING", (0, 0), (-1, -1), 5),
]))
story.append(sel_table)
story.append(spacer())
# 3.3 Differential Media
story.append(h2("3.3 Differential / Indicator Media"))
story.append(body(
"Differential media contain pH indicators, chromogenic substrates, or other indicators to demonstrate metabolic "
"properties of organisms - allowing different bacteria to be distinguished by their colony appearance or color change. "
"Many media are both selective AND differential."
))
diff_data = [
["Medium", "Differential Principle", "Interpretation"],
["MacConkey Agar (MAC)", "Lactose + neutral red indicator", "Lactose fermenters: pink/red (E. coli, Klebsiella). Non-fermenters: colorless (Salmonella, Shigella, Pseudomonas)"],
["Eosin Methylene Blue (EMB)", "Eosin Y + methylene blue; lactose/sucrose", "E. coli: metallic green sheen. Klebsiella: mucoid dark pink. Non-fermenters: colorless"],
["Triple Sugar Iron (TSI) Agar", "Glucose, lactose, sucrose + H₂S indicator (ferrous sulfate) + phenol red", "Used to differentiate Enterobacteriaceae by fermentation pattern and H₂S production"],
["Blood Agar (BA)", "5% sheep blood + RBCs", "Hemolysis pattern differentiates Streptococci (α, β, γ)"],
["CHROMAgar", "Chromogenic enzyme substrates", "Different species produce different colony colors (E. coli: pink; Staph aureus: mauve)"],
["Bile Esculin Agar", "Bile, esculin, ferric citrate", "Enterococci and Group D Strep hydrolyze esculin - black precipitate"],
]
diff_table = Table(diff_data, colWidths=[4*cm, 5.5*cm, 7.5*cm])
diff_table.setStyle(TableStyle([
("BACKGROUND", (0, 0), (-1, 0), colors.HexColor("#e65100")),
("TEXTCOLOR", (0, 0), (-1, 0), colors.white),
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
("FONTSIZE", (0, 0), (-1, -1), 8.5),
("ROWBACKGROUNDS", (0, 1), (-1, -1), [colors.HexColor("#fff8f0"), colors.white]),
("GRID", (0, 0), (-1, -1), 0.4, colors.HexColor("#cccccc")),
("VALIGN", (0, 0), (-1, -1), "TOP"),
("TOPPADDING", (0, 0), (-1, -1), 5),
("BOTTOMPADDING", (0, 0), (-1, -1), 5),
("LEFTPADDING", (0, 0), (-1, -1), 5),
]))
story.append(diff_table)
story.append(spacer())
# 3.4 Specialized Media
story.append(h2("3.4 Specialized / Enrichment Media"))
story.append(body(
"Specialized media are designed for fastidious organisms that require specific nutrients, growth factors, "
"or environmental conditions that are not present in standard media."
))
story.append(bullet("<b>Charcoal Cefoperazone Deoxycholate Agar (CCDA):</b> Selective for Campylobacter."))
story.append(bullet("<b>BCYE (Buffered Charcoal Yeast Extract):</b> Required for Legionella pneumophila - supplemented with iron and L-cysteine."))
story.append(bullet("<b>Regan-Lowe:</b> Selective enrichment medium for Bordetella pertussis."))
story.append(bullet("<b>Fletcher's / EMJH medium:</b> For Leptospira (semisolid, albumin-Tween 80 base)."))
story.append(bullet("<b>Loeffler's Serum Slope:</b> Corynebacterium diphtheriae (metachromatic granules visible)."))
story.append(bullet("<b>Tellurite media (Tinsdale):</b> C. diphtheriae produces black colonies with brown halo."))
story.append(bullet("<b>Cell culture (McCoy, HeLa):</b> Obligate intracellular bacteria: Chlamydia, Rickettsia, Coxiella."))
story.append(spacer(0.5))
# ─── SECTION 4: MASTER SUMMARY TABLE ─────────────────────────────────────────
story.append(h1("4. Master Reference Table - Culture Media"))
master_data = [
["Medium", "Type", "Target / Use", "Key Feature"],
["Blood Agar", "Enriched/Nonselective", "Most bacteria, fungi", "Hemolysis detection"],
["Chocolate Agar", "Enriched/Nonselective", "Haemophilus, Neisseria", "Heated blood - lysed RBCs release hemin (X factor) and NAD (V factor)"],
["Mueller-Hinton", "Nonselective", "AST/MIC testing", "Standardized divalent cations"],
["Thioglycolate broth", "Enriched", "Anaerobes, low-density organisms", "Low redox potential, O₂ gradient"],
["Sabouraud Dextrose", "Enriched/Selective", "Fungi", "Acidic pH (5.6) inhibits bacteria"],
["MacConkey", "Selective+Differential", "Gram-negative rods", "Lactose fermentation, bile salts"],
["EMB", "Selective+Differential", "Gram-negative rods", "Metallic sheen for E. coli"],
["MSA (Mannitol Salt)", "Selective+Differential", "Staphylococci", "7.5% NaCl selective; mannitol+phenol red differential"],
["XLD", "Selective+Differential", "Salmonella, Shigella", "Black = H₂S (Salmonella); red colonies"],
["Hektoen Enteric", "Selective+Differential", "Salmonella, Shigella", "Bile + dyes; H₂S production"],
["Thayer-Martin", "Selective", "N. gonorrhoeae, N. meningitidis", "VCN antibiotics + chocolate agar base"],
["TCBS", "Selective+Differential", "Vibrio spp.", "Sucrose fermentation color"],
["CIN Agar", "Selective+Differential", "Yersinia enterocolitica", "Bull's-eye colonies"],
["Campylobacter Agar", "Selective", "Campylobacter spp.", "42°C, microaerophilic incubation"],
["BCYE", "Enriched/Selective", "Legionella", "Fe + L-cysteine supplemented"],
["LJ Medium", "Selective", "Mycobacteria", "Malachite green; egg-based"],
["Middlebrook 7H10/11", "Selective", "Mycobacteria", "OADC; faster growth than LJ"],
["Regan-Lowe", "Selective/Enrichment", "Bordetella pertussis", "Charcoal + horse blood + cephalexin"],
["Bordet-Gengou", "Enriched", "Bordetella pertussis", "Potato glycerol blood agar"],
["Loeffler's Serum", "Enriched", "Corynebacterium diphtheriae", "Metachromatic granules"],
["Tinsdale Agar", "Selective+Differential", "C. diphtheriae", "Black colonies + brown halo (tellurite)"],
["TSI Agar", "Differential", "Enterobacteriaceae ID", "Sugar fermentation + H₂S"],
["Bile Esculin Agar", "Selective+Differential", "Enterococcus, Group D Strep", "Esculin hydrolysis - black precipitate"],
["CHROMAgar", "Chromogenic", "Various (MRSA, Candida, E. coli)", "Chromogenic substrates produce colored colonies"],
]
master_table = Table(master_data, colWidths=[4*cm, 3.5*cm, 4.5*cm, 5*cm])
master_table.setStyle(TableStyle([
("BACKGROUND", (0, 0), (-1, 0), colors.HexColor("#1a3a5c")),
("TEXTCOLOR", (0, 0), (-1, 0), colors.white),
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
("FONTSIZE", (0, 0), (-1, -1), 8),
("ROWBACKGROUNDS", (0, 1), (-1, -1), [colors.HexColor("#f0f4f8"), colors.white]),
("GRID", (0, 0), (-1, -1), 0.3, colors.HexColor("#aaaaaa")),
("VALIGN", (0, 0), (-1, -1), "TOP"),
("TOPPADDING", (0, 0), (-1, -1), 4),
("BOTTOMPADDING", (0, 0), (-1, -1), 4),
("LEFTPADDING", (0, 0), (-1, -1), 4),
]))
story.append(master_table)
story.append(spacer())
# ─── SECTION 5: INCUBATION CONDITIONS ────────────────────────────────────────
story.append(h1("5. Incubation Conditions"))
story.append(body(
"Correct incubation conditions are as important as selecting the right medium. Temperature, atmosphere, and "
"duration must match the requirements of the target organism."
))
inc_data = [
["Condition", "Temperature / Atmosphere", "Organisms"],
["Routine aerobic", "35-37°C, ambient air", "Most clinically significant bacteria"],
["CO₂ (Capnophilic)", "35-37°C, 5-10% CO₂ (candle jar or CO₂ incubator)", "Neisseria, Haemophilus, Streptococcus pneumoniae"],
["Anaerobic", "35-37°C, anaerobic chamber (N₂ + CO₂ + H₂, or pre-reduced media)", "Bacteroides, Clostridium, Fusobacterium, Peptostreptococcus"],
["Microaerophilic", "42°C, 5% O₂, 10% CO₂, 85% N₂", "Campylobacter jejuni/coli"],
["Room temperature", "22-25°C", "Yersinia pestis, some Listeria, Vibrio"],
["Mycobacterial culture", "35-37°C (MTB); 30-32°C (M. marinum)", "Mycobacterium tuberculosis and atypicals"],
["Extended incubation", "35-37°C, up to 6-8 weeks", "Brucella, Legionella, slow-growing mycobacteria"],
]
inc_table = Table(inc_data, colWidths=[4.5*cm, 5.5*cm, 7*cm])
inc_table.setStyle(TableStyle([
("BACKGROUND", (0, 0), (-1, 0), colors.HexColor("#00695c")),
("TEXTCOLOR", (0, 0), (-1, 0), colors.white),
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
("FONTSIZE", (0, 0), (-1, -1), 8.5),
("ROWBACKGROUNDS", (0, 1), (-1, -1), [colors.HexColor("#e8f5e9"), colors.white]),
("GRID", (0, 0), (-1, -1), 0.4, colors.HexColor("#cccccc")),
("VALIGN", (0, 0), (-1, -1), "TOP"),
("TOPPADDING", (0, 0), (-1, -1), 5),
("BOTTOMPADDING", (0, 0), (-1, -1), 5),
("LEFTPADDING", (0, 0), (-1, -1), 5),
]))
story.append(inc_table)
story.append(spacer())
# ─── SECTION 6: SPECIMEN PROCESSING ──────────────────────────────────────────
story.append(h1("6. Specimen Types and Recommended Media"))
story.append(body(
"Selection of culture media must match the specimen source, the likely pathogens, and the site of infection. "
"The following table summarizes routine clinical specimen processing."
))
spec_data = [
["Specimen", "Routine Media", "Special Media / Conditions"],
["Blood", "Aerobic + anaerobic BHI/TSB enrichment bottles", "Lysis-centrifugation (for fungi, Brucella, Legionella)"],
["Urine", "Blood agar + MacConkey (or CLED)", "Calibrated loop - quantitative culture (≥10⁵ CFU/mL)"],
["Stool / Feces", "Blood agar, MacConkey, HE or XLD, Campylobacter agar", "TCBS (Vibrio); CIN (Yersinia); enrichment broth (GN broth, selenite)"],
["Respiratory (sputum)", "Blood agar, chocolate agar, MacConkey", "Bordet-Gengou / Regan-Lowe (Bordetella); BCYE (Legionella)"],
["CSF", "Blood agar, chocolate agar, BHI broth", "Viral culture; fungal if indicated (India ink / Cryptococcus Ag)"],
["Wound / Tissue", "Blood agar, chocolate agar, anaerobic media, MacConkey", "Specific selective media depending on clinical context"],
["Genital (STI screen)", "Thayer-Martin / Modified TM (GC); Chocolate agar", "Selective media for Chlamydia (cell culture or NAAT preferred)"],
["Eye (Corneal scraping)", "Blood agar, chocolate agar, Sabouraud", "Direct inoculation at bedside on 'C' pattern"],
["AFB / TB", "LJ medium, Middlebrook 7H10/11, MGIT liquid broth", "Digestion-decontamination (NALC-NaOH) before processing"],
]
spec_table = Table(spec_data, colWidths=[3.5*cm, 5.5*cm, 8*cm])
spec_table.setStyle(TableStyle([
("BACKGROUND", (0, 0), (-1, 0), colors.HexColor("#4527a0")),
("TEXTCOLOR", (0, 0), (-1, 0), colors.white),
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
("FONTSIZE", (0, 0), (-1, -1), 8.5),
("ROWBACKGROUNDS", (0, 1), (-1, -1), [colors.HexColor("#f5f0ff"), colors.white]),
("GRID", (0, 0), (-1, -1), 0.4, colors.HexColor("#cccccc")),
("VALIGN", (0, 0), (-1, -1), "TOP"),
("TOPPADDING", (0, 0), (-1, -1), 5),
("BOTTOMPADDING", (0, 0), (-1, -1), 5),
("LEFTPADDING", (0, 0), (-1, -1), 5),
]))
story.append(spec_table)
story.append(spacer())
# ─── SECTION 7: KEY MNEMONICS ─────────────────────────────────────────────────
story.append(h1("7. High-Yield Mnemonics and Quick Facts"))
story.append(h2("7.1 Media Mnemonics"))
story.append(bullet("<b>MacConkey = MAC attack on Gram-negatives:</b> Bile salts + crystal violet kill Gram-positives. Pink = acid (lactose fermenters). Colorless = non-fermenter."))
story.append(bullet("<b>BCYE for Legionella:</b> 'Big Cities (L)ike Everything' - Legionella needs iron and L-cysteine (both absent in standard media - why it was missed for so long)."))
story.append(bullet("<b>Thayer-Martin for Gonococci / Meningococci:</b> 'VCN = Very Careful Neisseria' - Vancomycin, Colistin, Nystatin kill everything else."))
story.append(bullet("<b>TCBS for Vibrio:</b> 'The Cholera Broth System' - V. cholerae = yellow (sucrose+); V. parahaemolyticus = blue-green (sucrose-)."))
story.append(bullet("<b>LJ for Mycobacteria:</b> Malachite green suppresses most other organisms. Rough, buff-colored colonies of M. tuberculosis (Buff + rough = 'Buff Roughnecks')."))
story.append(h2("7.2 Hemolysis on Blood Agar"))
story.append(note(
"Alpha (partial, green) = 'Almost hemolysis' = Strep pneumoniae and viridans group\n"
"Beta (complete, clear) = 'Better hemolysis' = S. pyogenes (GAS), S. agalactiae (GBS), S. aureus\n"
"Gamma (none) = 'Gone - no hemolysis' = Enterococcus, coagulase-negative Staphylococci"
))
story.append(h2("7.3 Special Growth Requirements (High-Yield)"))
story.append(bullet("<b>X factor (hemin) + V factor (NAD):</b> Haemophilus influenzae requires BOTH (grows on chocolate agar only, not blood agar)"))
story.append(bullet("<b>Satellitism:</b> H. influenzae grows near S. aureus streak on blood agar (S. aureus hemolysis releases NAD)"))
story.append(bullet("<b>L-cysteine + iron:</b> Legionella (grows ONLY on BCYE)"))
story.append(bullet("<b>42°C microaerophilic:</b> Campylobacter (37°C gives poor growth)"))
story.append(bullet("<b>Obligate intracellular:</b> Chlamydia, Rickettsia, Coxiella, Ehrlichia - need living cell cultures (or NAAT for diagnosis)"))
story.append(bullet("<b>Mycobacteria:</b> Slow growing (2-8 weeks LJ); MGIT liquid broth faster (1-3 weeks)"))
story.append(spacer(0.5))
# ─── FOOTER / REFERENCES ──────────────────────────────────────────────────────
story.append(hr())
story.append(Paragraph(
"References: Murray PR et al. Medical Microbiology, 9th ed. Elsevier, 2021 | "
"Sherris & Ryan's Medical Microbiology, 8th ed. McGraw-Hill, 2022 | "
"Tietz Textbook of Laboratory Medicine, 7th ed. Elsevier, 2023",
note_style
))
story.append(Paragraph("Generated by Orris Medical Reference | July 2026", source_style))
# Build PDF
doc.build(story)
print(f"PDF generated: {OUTPUT}")
cd /home/daytona/workspace/bacteria-culture-media && python generate_pdf.py
ls -lh /home/daytona/workspace/bacteria-culture-media/Bacteria_and_Culture_Media…
Bacteria and Culture Media
PDF Document · PDF
Active and passive movements and joint mobility for physiotherapy students pdf please
~/bacteria-culture-media/generate_physio_pdf.py
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm
from reportlab.lib import colors
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, PageBreak, KeepTogether
)
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
OUTPUT = "/home/daytona/workspace/bacteria-culture-media/Active_Passive_Movements_Joint_Mobility.pdf"
doc = SimpleDocTemplate(
OUTPUT, pagesize=A4,
leftMargin=2*cm, rightMargin=2*cm,
topMargin=2.5*cm, bottomMargin=2.5*cm
)
styles = getSampleStyleSheet()
BLUE = colors.HexColor("#1a3a5c")
DBLUE = colors.HexColor("#0d2240")
TEAL = colors.HexColor("#006064")
GREEN = colors.HexColor("#2e7d32")
ORANGE = colors.HexColor("#e65100")
PURPLE = colors.HexColor("#4527a0")
RED = colors.HexColor("#b71c1c")
GREY = colors.HexColor("#37474f")
LGREY = colors.HexColor("#eceff1")
LBLUE = colors.HexColor("#e3f2fd")
LGREEN = colors.HexColor("#e8f5e9")
LPURP = colors.HexColor("#ede7f6")
def ps(name, **kw):
base = kw.pop("parent", styles["Normal"])
return ParagraphStyle(name, parent=base, **kw)
title_s = ps("T", fontSize=22, textColor=BLUE, alignment=TA_CENTER, fontName="Helvetica-Bold", spaceAfter=4)
sub_s = ps("S", fontSize=11, textColor=GREY, alignment=TA_CENTER, fontName="Helvetica-Oblique", spaceAfter=18)
h1_s = ps("H1", fontSize=14, textColor=colors.white, backColor=BLUE,
fontName="Helvetica-Bold", spaceAfter=8, spaceBefore=14, borderPad=6)
h2_s = ps("H2", fontSize=12, textColor=BLUE, fontName="Helvetica-Bold",
spaceAfter=5, spaceBefore=10, borderWidth=0, leftIndent=0)
h3_s = ps("H3", fontSize=10.5, textColor=TEAL, fontName="Helvetica-Bold",
spaceAfter=4, spaceBefore=7)
body_s = ps("B", fontSize=10, leading=15, alignment=TA_JUSTIFY,
spaceAfter=7, fontName="Helvetica")
bullet_s = ps("BL", fontSize=10, leading=14, spaceAfter=3,
leftIndent=16, bulletIndent=6, fontName="Helvetica")
note_s = ps("N", fontSize=9, textColor=GREY, backColor=colors.HexColor("#fff8e1"),
borderPad=5, leading=13, leftIndent=6, rightIndent=6,
fontName="Helvetica-Oblique", spaceAfter=8)
clinical_s= ps("C", fontSize=9.5, textColor=colors.HexColor("#1b5e20"),
backColor=LGREEN, borderPad=6, leading=14,
leftIndent=6, rightIndent=6, fontName="Helvetica", spaceAfter=8)
src_s = ps("SR", fontSize=8, textColor=colors.HexColor("#999999"),
alignment=TA_CENTER, spaceAfter=3)
def h1(t): return Paragraph(f" {t}", h1_s)
def h2(t): return Paragraph(t, h2_s)
def h3(t): return Paragraph(t, h3_s)
def body(t): return Paragraph(t, body_s)
def bul(t): return Paragraph(f"• {t}", bullet_s)
def note(t): return Paragraph(t, note_s)
def clinical(t): return Paragraph(t, clinical_s)
def sp(h=0.3): return Spacer(1, h*cm)
def hr(): return HRFlowable(width="100%", thickness=0.5, color=colors.HexColor("#cccccc"), spaceAfter=5)
def tbl(data, widths, header_color=BLUE, row_colors=(LBLUE, colors.white), fs=9):
t = Table(data, colWidths=widths)
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), header_color),
("TEXTCOLOR", (0,0), (-1,0), colors.white),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), fs),
("ROWBACKGROUNDS", (0,1), (-1,-1), list(row_colors)),
("GRID", (0,0), (-1,-1), 0.35, colors.HexColor("#bbbbbb")),
("VALIGN", (0,0), (-1,-1), "TOP"),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 5),
]))
return t
# ── BUILD ──────────────────────────────────────────────────────────────────────
story = []
# TITLE
story += [sp(2),
Paragraph("Active & Passive Movements", title_s),
Paragraph("and Joint Mobility", title_s),
Paragraph("A Clinical Reference for Physiotherapy Students", sub_s),
hr(),
Paragraph("Sources: Rheumatology 2e (Elsevier) | Campbell's Operative Orthopaedics 15e | Bailey & Love's Surgery 28e | Miller's Review of Orthopaedics 9e | Adams & Victor's Neurology 12e", src_s),
sp(1.2)
]
# ══════════════════════════════════════════════════════════════
story.append(h1("1. Foundations of Movement Assessment"))
story.append(body(
"Joint movement assessment is central to physiotherapy practice. It forms the basis for establishing a baseline, "
"monitoring progress, guiding treatment selection, and evaluating outcomes. Three fundamental methods are used "
"to examine joint movement:"
))
story += [
bul("<b>Active movement:</b> Movement initiated and sustained entirely by the patient using their own muscular effort."),
bul("<b>Passive movement:</b> Movement performed by the examiner (or gravity) without active muscular effort from the patient."),
bul("<b>Resisted (isometric) movement:</b> The patient attempts movement while the examiner provides a counterforce, isolating muscular and tendinous components."),
sp(0.3)
]
story.append(clinical(
"Clinical Pearl: Always establish the active range first. If active ROM is reduced, then assess passive ROM. "
"A greater passive range than active range suggests a muscular or neuromuscular problem. "
"Equal restriction in both planes suggests articular or capsular pathology. "
"Compare bilaterally whenever the condition is unilateral. (Rheumatology 2e)"
))
# ══════════════════════════════════════════════════════════════
story.append(h1("2. Active Movement"))
story.append(h2("2.1 Definition and Purpose"))
story.append(body(
"Active movement is any movement that the patient initiates and maintains through voluntary muscle contraction. "
"It requires an intact neuromuscular system: upper and lower motor neurons, neuromuscular junction, and "
"functioning musculotendinous units. During active movement assessment, the physiotherapist observes "
"quality, range, symmetry, and pain reproduction."
))
story.append(h2("2.2 What Active Movement Tests"))
ROM_active = [
["Component Tested", "Clinical Significance"],
["Voluntary muscle power", "Weakness suggests nerve, muscle, or tendon pathology"],
["Joint range (active)", "Reduced ROM narrows the differential (muscle vs. joint vs. bone)"],
["Pain provocation", "Pain at specific arcs points to impingement, bursitis, or tendinopathy"],
["Quality and coordination", "Jerky/substituted movement suggests neuromuscular dysfunction"],
["Trick movements", "Patients compensate for ruptures; e.g., toe flexors substitute for ruptured Achilles"],
["Symmetry", "Asymmetry is the most reliable indicator of abnormality"],
]
story.append(tbl(ROM_active, [5.5*cm, 11.5*cm], header_color=GREEN, row_colors=(LGREEN, colors.white)))
story.append(sp())
story.append(h2("2.3 Types of Active Exercise in Physiotherapy"))
story += [
bul("<b>Active free (AF) exercise:</b> Patient moves the limb through its full range with no external load or assistance. Used early in rehabilitation when muscle strength is 3/5 (movement against gravity)."),
bul("<b>Active assisted (AA) exercise:</b> Partially assisted by the therapist, sling, water, or mechanical device when strength is insufficient for full-range active movement (grade 2/5 or early recovery)."),
bul("<b>Active resisted (AR) exercise:</b> Patient moves against a load (manual, weight, resistance band). Used to build strength when grade ≥3/5."),
bul("<b>Isometric exercise:</b> Muscle contraction without joint movement. Used post-surgery or when movement is painful/contraindicated. Maintains strength and prevents atrophy."),
bul("<b>Isotonic exercise:</b> Constant load with varying speed. Concentric (muscle shortens) and eccentric (muscle lengthens) phases."),
bul("<b>Isokinetic exercise:</b> Constant angular velocity, variable resistance (dynamometer). Used for assessment and advanced rehabilitation."),
sp(0.3)
]
story.append(note(
"Gravity Pitfall: A limb lying flat on the couch extended is NOT proof that the extensor mechanism is intact. "
"Always confirm active straight leg raise (SLR) for knee extension assessment. Similarly, elbow extension should "
"be tested against gravity or resistance, not just in the resting position. (Bailey & Love's Surgery 28e)"
))
story.append(h2("2.4 Grading Muscle Strength - MRC Scale"))
mrc_data = [
["Grade", "Description", "Movement Capacity"],
["0 / 0", "No contraction", "Complete paralysis"],
["1 / Trace", "Flicker or trace of contraction visible/palpable", "No movement produced"],
["2 / Poor", "Full ROM with gravity eliminated (horizontal plane)", "Cannot lift against gravity"],
["3 / Fair", "Full ROM against gravity only", "No resistance tolerated"],
["4 / Good", "Full ROM against gravity + some resistance", "Reduced but present power"],
["4+ / Good+", "Full ROM against moderate to strong resistance", "Near-normal strength"],
["5 / Normal", "Full ROM against full resistance, normal strength", "Compared with contralateral side"],
]
story.append(tbl(mrc_data, [2.5*cm, 6*cm, 8.5*cm], header_color=TEAL, row_colors=(colors.HexColor("#e0f7fa"), colors.white)))
story.append(sp())
# ══════════════════════════════════════════════════════════════
story.append(h1("3. Passive Movement"))
story.append(h2("3.1 Definition and Purpose"))
story.append(body(
"Passive movement is produced entirely by an external force - the therapist, gravity, or a mechanical device - "
"without active muscle contraction from the patient. The patient must remain fully relaxed. "
"Passive movement tests joint integrity, capsular and ligamentous laxity, articular cartilage quality, "
"and the presence of intra-articular pathology."
))
story.append(body(
"Palpating the joint and periarticular structures during passive movement provides additional information "
"about pain, tenderness, and crepitus from the joint or tendon sheaths. (Rheumatology 2e)"
))
story.append(h2("3.2 What Passive Movement Tests"))
pass_data = [
["Assessment", "Positive Finding", "Interpretation"],
["Passive ROM vs Active ROM", "Passive > Active", "Muscular weakness, tendon rupture, or neuromuscular problem"],
["Passive ROM vs Active ROM", "Passive = Active (both restricted)", "Articular/capsular pathology (joint or bone)"],
["Pain at end of passive range", "Capsular stretch pain", "Synovitis, capsulitis, or end-stage OA"],
["Pain-free passive range", "Active movement painful, passive pain-free", "Contractile tissue (tendon/muscle) lesion"],
["Crepitus on passive movement", "Rough grating sensation", "Articular cartilage damage (OA, post-traumatic)"],
["Excess passive range (laxity)", "Hypermobility or instability", "Ligament laxity, rupture, or generalized hypermobility"],
["End-feel quality", "Normal vs. abnormal end-feel", "See Section 3.3 below"],
]
story.append(tbl(pass_data, [4.5*cm, 4.5*cm, 8*cm], header_color=ORANGE, row_colors=(colors.HexColor("#fff3e0"), colors.white)))
story.append(sp())
story.append(h2("3.3 End-Feel"))
story.append(body(
"End-feel is the quality of resistance perceived by the examiner at the end of passive joint range. "
"Cyriax first described this concept and it remains a cornerstone of manual therapy assessment. "
"End-feel helps differentiate articular, periarticular, and extra-articular pathology."
))
endfeel_data = [
["End-Feel Type", "Description / Quality", "Normal Example", "Abnormal Cause"],
["Bone-on-bone (hard)", "Abrupt, unyielding stop; no give", "Elbow extension", "OA, osteophytes, bony block"],
["Capsular (firm/leathery)", "Elastic, firm, leathery resistance", "Hip IR in extension, shoulder ER", "Capsular fibrosis, adhesive capsulitis"],
["Soft tissue approximation", "Soft, yielding, compression of soft tissue", "Knee flexion (calf-to-thigh)", "Obesity, muscle bulk"],
["Springy/rebound", "Springy rebound resistance, not hard", "(Normally not present)", "Meniscal lesion, loose body"],
["Empty end-feel", "No mechanical end - patient stops due to pain before tissue limit", "None (always abnormal)", "Acute inflammation, acute bursitis, fracture, serious pathology"],
["Spasm end-feel", "Sudden muscle spasm halts movement", "None (always abnormal)", "Acute joint inflammation, muscle tear"],
["Boggy/soft (abnormal)", "Spongy, boggy resistance before expected end of range", "None", "Joint effusion, synovitis, haemarthrosis"],
]
story.append(tbl(endfeel_data, [3.5*cm, 4.5*cm, 3.5*cm, 5.5*cm], header_color=PURPLE, row_colors=(LPURP, colors.white)))
story.append(sp())
story.append(note(
"Hard end-feel on passive stretch of a joint that previously had a soft/elastic end-feel is characteristic of "
"contracture. This is an unyielding resistance to assisted movement - distinct from normal bony end-feel "
"because it occurs before the expected anatomical limit of range. (Firestein & Kelley's Rheumatology)"
))
story.append(h2("3.4 Passive Mobilization Techniques"))
story.append(body(
"Passive mobilization is a hands-on physiotherapy intervention where the therapist applies forces to joints "
"to increase mobility, reduce pain, and restore normal arthrokinematics. It differs from passive assessment "
"in that it is applied as a treatment technique."
))
mob_data = [
["Technique", "Grade (Maitland)", "Amplitude", "Application"],
["Grade I", "Small amplitude", "At start of range", "Pain relief; acute conditions; irritable joints"],
["Grade II", "Large amplitude", "Into range, not at limit", "Pain relief; subacute; does not reach end-range"],
["Grade III", "Large amplitude", "Into resistance / end of range", "Stiffness + pain; reaches tissue resistance"],
["Grade IV", "Small amplitude", "At end of range, into resistance", "Stiffness dominant; stretches capsule/ligament"],
["Grade V (thrust / manipulation)", "High velocity, low amplitude (HVLAT)", "At end of range", "Restricted by physiotherapists with advanced training"],
]
story.append(tbl(mob_data, [2.5*cm, 3.5*cm, 3.5*cm, 7.5*cm], header_color=TEAL, row_colors=(colors.HexColor("#e0f7fa"), colors.white)))
story.append(sp())
story.append(h3("Accessory vs. Physiological Movements"))
story += [
bul("<b>Physiological movements:</b> Movements the patient can perform actively (flexion, extension, abduction, adduction, rotation). These are the classic ROM movements."),
bul("<b>Accessory movements (joint play):</b> Small gliding, rolling, spinning, or distraction movements that cannot be performed voluntarily. They occur within every physiological movement and must be present for full, pain-free ROM. Assessed and treated passively only."),
bul("<b>Kaltenborn grades:</b> Grade 0 = no movement (ankylosis); Grade I = tiny play, no separation; Grade II = normal joint play (taut); Grade III = excessive play (hypermobility/instability)."),
sp(0.3)
]
# ══════════════════════════════════════════════════════════════
story.append(h1("4. Joint Mobility and Range of Motion (ROM)"))
story.append(h2("4.1 Definitions"))
story += [
bul("<b>ROM (Range of Motion):</b> The arc of movement through which a joint can move, measured in degrees with a goniometer or inclinometer."),
bul("<b>Active ROM (AROM):</b> Range achieved through the patient's own muscle effort."),
bul("<b>Passive ROM (PROM):</b> Range achieved with external assistance; slightly greater than AROM in healthy subjects due to absence of active muscular tension."),
bul("<b>Functional ROM:</b> The range required for normal activities of daily living (ADL)."),
bul("<b>Hypomobility:</b> Reduced joint mobility relative to normal/contralateral side."),
bul("<b>Hypermobility:</b> Excessive joint mobility; may indicate ligament laxity, Ehlers-Danlos syndrome, or joint instability."),
sp(0.3)
]
story.append(h2("4.2 Normal ROM Reference Values (Major Joints)"))
story.append(body("Values below are approximate averages for adults. Always compare with the contralateral side as the primary reference."))
rom_data = [
["Joint", "Movement", "Normal ROM (degrees)", "Functional Minimum"],
["Shoulder", "Flexion", "0-180°", "~120° (reach overhead)"],
["Shoulder", "Extension", "0-60°", "~45°"],
["Shoulder", "Abduction", "0-180°", "~90°"],
["Shoulder", "Internal rotation", "0-70°", "~60°"],
["Shoulder", "External rotation", "0-90°", "~60°"],
["Elbow", "Flexion", "0-145°", "~30-130° (functional arc)"],
["Elbow", "Extension", "0° (full extension)", "0° needed for push/press"],
["Forearm", "Supination", "0-80°", "~50°"],
["Forearm", "Pronation", "0-80°", "~50°"],
["Wrist", "Flexion", "0-80°", "~10°"],
["Wrist", "Extension", "0-70°", "~35°"],
["Hip", "Flexion", "0-120°", "~110° (stair climbing)"],
["Hip", "Extension", "0-30°", "~10°"],
["Hip", "Abduction", "0-45°", "~15°"],
["Hip", "Internal rotation", "0-45°", "~15°"],
["Hip", "External rotation", "0-45°", "~20°"],
["Knee", "Flexion", "0-135°", "~105° (sitting; 110° stair)"],
["Knee", "Extension", "0° (full extension)", "0° required for ambulation"],
["Ankle", "Dorsiflexion", "0-20°", "~10° (level walking)"],
["Ankle", "Plantarflexion", "0-50°", "~20°"],
["Ankle", "Inversion", "0-35°", "—"],
["Ankle", "Eversion", "0-15°", "—"],
["Cervical spine", "Flexion/Extension", "~45° each", "—"],
["Cervical spine", "Rotation (each side)", "~60-80°", "—"],
["Cervical spine", "Lateral flexion (each)", "~45°", "—"],
["Lumbar spine", "Flexion", "~40-60°", "—"],
["Lumbar spine", "Extension", "~20-35°", "—"],
]
story.append(tbl(rom_data, [3*cm, 4*cm, 4.5*cm, 5.5*cm], header_color=BLUE, row_colors=(LBLUE, colors.white)))
story.append(sp())
story.append(h2("4.3 Measurement Tools"))
meas_data = [
["Tool", "Use / How", "Clinical Notes"],
["Universal Goniometer", "Aligns with bony landmarks; measures joint angle", "Gold standard for ROM; portable; requires skill for accuracy"],
["Inclinometer (bubble/digital)", "Gravity-based angle measurement; placed on limb segment", "Better for spine; less landmark-dependent"],
["Tape Measure", "Functional reach, fingertip-to-floor, finger-to-palm", "Quick; indirect; good for spine flexion screening"],
["Visual Estimation", "Therapist estimates ROM by eye", "Fast; less accurate; use only for screening"],
["Electrogoniometer", "Electronic sensor attached to joint; records dynamic ROM", "Research use; real-time movement analysis"],
["Motion Capture (Vicon / IMU)", "3D or sensor-based kinematic analysis", "Advanced lab/clinical use; high precision"],
]
story.append(tbl(meas_data, [4*cm, 5.5*cm, 7.5*cm], header_color=GREY, row_colors=(LGREY, colors.white)))
story.append(sp())
story.append(h2("4.4 Factors Affecting ROM"))
story += [
bul("<b>Age:</b> ROM decreases progressively with age (by approximately 6-25% per decade from middle age, depending on joint and direction)."),
bul("<b>Sex:</b> Females typically have greater ROM than males (increased ligament laxity and less muscle bulk)."),
bul("<b>Body type:</b> Excessive adipose tissue reduces ROM (soft tissue approximation end-feel at earlier range)."),
bul("<b>Joint structure:</b> Ball-and-socket (hip, shoulder) > Hinge (knee, elbow) > Pivot (atlantoaxial) in terms of ROM."),
bul("<b>Muscle extensibility:</b> Tight hamstrings reduce hip flexion with knee extended; tight hip flexors limit hip extension (Thomas test positive)."),
bul("<b>Pain:</b> Pain inhibits full-range movement (pain-limited ROM)."),
bul("<b>Muscle strength:</b> Weak muscles cannot complete full active ROM even if passive range is normal."),
bul("<b>Pathology:</b> Synovitis restricts all directions; periarticular lesions restrict specific planes."),
sp(0.3)
]
# ══════════════════════════════════════════════════════════════
story.append(h1("5. Clinical Interpretation of Movement Findings"))
story.append(h2("5.1 Cyriax Selective Tissue Tension Principles"))
story.append(body(
"James Cyriax developed a systematic framework for interpreting combined active, passive, and resisted "
"movement findings to identify which tissue is causing the problem."
))
cst_data = [
["Finding", "Likely Tissue at Fault", "Examples"],
["Active + passive restricted in same direction", "Articular (joint capsule/bone)", "Frozen shoulder (capsular pattern), OA"],
["Active restricted; passive full", "Contractile tissue (muscle/tendon)", "Muscle tear, tendinopathy, motor nerve lesion"],
["Passive restricted; active full", "Non-contractile tissue; capsule", "Ligament contracture, capsular adhesion"],
["Resisted movement painful; no ROM loss", "Contractile tissue (muscle/tendon)", "Tendinitis, partial muscle strain"],
["Resisted movement weak + painless", "Complete motor nerve lesion / full rupture", "Rotator cuff full-thickness tear, nerve palsy"],
["Resisted movement painful + weak", "Serious local pathology", "Fracture, severe muscle/tendon rupture, tumour"],
["All movements painful (capsular pattern)", "Synovitis of the joint", "Rheumatoid arthritis, acute infective arthritis"],
["Pain only at end range (passive)", "Periarticular / ligament strain", "Minor ligament sprain, early capsulitis"],
]
story.append(tbl(cst_data, [5*cm, 5.5*cm, 6.5*cm], header_color=RED, row_colors=(colors.HexColor("#ffebee"), colors.white)))
story.append(sp())
story.append(h2("5.2 Capsular Pattern"))
story.append(body(
"Each synovial joint demonstrates a characteristic proportional restriction of passive ROM in "
"capsular pathology (synovitis, capsulitis). Recognizing the capsular pattern helps confirm intra-articular pathology."
))
cap_data = [
["Joint", "Capsular Pattern (order of restriction)"],
["Shoulder (glenohumeral)", "ER > Abduction > IR (most to least restricted)"],
["Elbow", "Flexion > Extension (slight loss of extension; greater loss of flexion)"],
["Wrist", "Flexion = Extension (approximately equal)"],
["Hip", "IR > Flexion > Abduction > Extension > ER"],
["Knee", "Flexion >> Extension (flexion much more limited)"],
["Ankle (talocrural)", "Plantarflexion > Dorsiflexion"],
["Subtalar joint", "Inversion (varus) restricted"],
["Cervical spine", "Lateral flexion = Rotation > Extension"],
]
story.append(tbl(cap_data, [4.5*cm, 12.5*cm], header_color=TEAL, row_colors=(colors.HexColor("#e0f7fa"), colors.white)))
story.append(sp())
# ══════════════════════════════════════════════════════════════
story.append(h1("6. Continuous Passive Motion (CPM)"))
story.append(body(
"Continuous Passive Motion (CPM) is a mechanized post-operative rehabilitation tool that passively moves "
"a joint through a set ROM automatically and repetitively, without active patient muscle effort."
))
story += [
bul("<b>Mechanism:</b> CPM provides rhythmic, controlled passive movement that reduces post-operative stiffness, promotes synovial fluid circulation, and supports articular cartilage nutrition."),
bul("<b>Cartilage benefit:</b> Immobilization decreases proteoglycan/collagen ratio in cartilage. Continuous passive motion is believed to benefit cartilage healing. Four weeks of immobilization decreases this ratio; it returns to normal after 8 weeks of joint mobilization ('motion is lotion'). (Miller's Review of Orthopaedics 9e)"),
bul("<b>Common uses:</b> Post total knee arthroplasty (TKA), after ACL reconstruction, after intra-articular fracture ORIF, after arthroscopic procedures."),
bul("<b>Settings:</b> ROM is set incrementally (e.g., 0-40° initially, increasing by 10° per day). Speed is set at a comfortable rate."),
bul("<b>Evidence:</b> CPM reduces post-operative pain and stiffness in the short term; long-term functional benefit vs. standard physiotherapy is less clear."),
sp(0.3)
]
story.append(clinical(
"Post-operative principle: Active and active-assisted exercises for joint mobilization should be started "
"as soon as soft-tissue healing permits. The patient should be instructed in self-guided exercise and joint "
"mobility in addition to guided physical therapy to maximize functional return. Early motion is key - "
"especially important with intra-articular fractures where weight bearing may be delayed but motion is not. "
"(Campbell's Operative Orthopaedics 15e)"
))
# ══════════════════════════════════════════════════════════════
story.append(h1("7. Joint-by-Joint Movement Summary"))
story.append(h2("7.1 Shoulder Complex"))
story += [
bul("Active movements: Flexion, extension, abduction, adduction, medial/lateral rotation, horizontal adduction/abduction."),
bul("Passive overpressure applied at end of each active range to assess end-feel."),
bul("Painful arc (60-120° of abduction): Subacromial impingement or rotator cuff tendinopathy."),
bul("Capsular pattern: ER > Abduction > IR - suggests adhesive capsulitis (frozen shoulder), GH OA, or RA."),
bul("Special tests: Neer's, Hawkins-Kennedy (impingement); empty can (supraspinatus); Apprehension (anterior instability); Sulcus sign (inferior instability)."),
sp(0.2)
]
story.append(h2("7.2 Knee"))
story += [
bul("Active: Flexion, extension, tibial rotation (in flexion)."),
bul("Passive: Full extension (0°) and flexion tested; end-feel: soft tissue approximation (normal); springy/rebound = meniscal block."),
bul("Gravity pitfall: A straight-leg resting position does NOT confirm intact extensor mechanism. Confirm with active straight leg raise."),
bul("Capsular pattern: Flexion >> Extension."),
bul("Special tests: McMurray, Thessaly (meniscus); Lachman, anterior drawer (ACL); posterior drawer (PCL); valgus/varus stress (collateral ligaments)."),
sp(0.2)
]
story.append(h2("7.3 Hip"))
story += [
bul("Active: Flexion, extension, abduction, adduction, IR, ER (tested both in supine and prone)."),
bul("A hip with synovitis is painful when rotated passively to extremes; IR and abduction most commonly restricted first."),
bul("Capsular pattern: IR > Flexion > Abduction > Extension > ER."),
bul("Log-rolling test: Passive IR/ER in supine with hip extended - maximally sensitive for intra-articular irritability."),
bul("Thomas test (hip flexion contracture): Contralateral hip maximally flexed to flatten lumbar lordosis; ipsilateral hip inability to reach neutral = positive flexion contracture."),
bul("FADIR (Flexion-Adduction-IR): Anterior femoroacetabular impingement."),
bul("FABER (Flexion-Abduction-ER / Figure-4): Lateral/posterior impingement or SI joint pathology."),
sp(0.2)
]
story.append(h2("7.4 Ankle and Foot"))
story += [
bul("Active: Dorsiflexion, plantarflexion, inversion, eversion, toe flexion/extension."),
bul("Passive: Compare with contralateral. Increased passive DF = possible Achilles rupture."),
bul("Silverskiold test: DF with knee extended vs. flexed; greater DF with knee flexed = isolated gastrocnemius contracture (Achilles length is adequate, soleus normal)."),
bul("Thompson/Simmonds squeeze test: Squeeze calf with patient prone - plantarflexion = Achilles intact; no movement = ruptured Achilles."),
sp(0.2)
]
story.append(h2("7.5 Cervical Spine"))
story += [
bul("Active: Flexion, extension, lateral flexion (L/R), rotation (L/R). Normal rotation ~60-80° each."),
bul("Guided active movements: Examiner gently guides head to ensure maximum range is reached."),
bul("Neurological screening: Cervical spine problems are frequently associated with neurological symptoms - always check upper limb reflexes, sensation, and power."),
bul("Passive overpressure at end of each range to differentiate pain from stiffness."),
sp(0.2)
]
# ══════════════════════════════════════════════════════════════
story.append(h1("8. Physiotherapy Interventions for Mobility"))
story.append(h2("8.1 Manual Therapy"))
story += [
bul("<b>Joint mobilization (Maitland Grades I-IV):</b> Oscillatory passive movements to reduce pain and restore ROM. Selected grade depends on irritability and restriction."),
bul("<b>Manipulation (Grade V / HVLAT):</b> High velocity thrust; used by advanced-practice physiotherapists; most evidence for spinal pain."),
bul("<b>Muscle energy technique (MET):</b> Patient performs isometric contraction against therapist resistance, followed by passive stretch into new range. Uses post-isometric relaxation and reciprocal inhibition."),
bul("<b>Myofascial release:</b> Sustained pressure or stretching of fascia to release restrictions and improve mobility."),
bul("<b>Soft tissue massage:</b> Reduces muscle spasm, improves circulation, prepares tissues for mobilization."),
sp(0.2)
]
story.append(h2("8.2 Exercise Prescription"))
story += [
bul("<b>Stretching (static):</b> Sustained stretch held 15-60 seconds. Targets muscle extensibility and joint capsule. Optimal: 30-second hold, 3-5 repetitions, 2-3 sessions/day."),
bul("<b>Stretching (dynamic/ballistic):</b> Rhythmic controlled movements through range. Used in sport-specific warm-up; not recommended for acute patients."),
bul("<b>Progressive resistive exercise (PRE):</b> Strength training using graduated loads to improve power and endurance of muscles acting on hypomobile joints."),
bul("<b>Proprioceptive training:</b> Balance board, unstable surfaces, and perturbation training to retrain joint position sense (JPS) after injury or surgery."),
sp(0.2)
]
story.append(h2("8.3 Physical Agents in Mobility Rehabilitation"))
phys_data = [
["Agent", "Mechanism", "Indication in Joint Mobility"],
["Heat (thermotherapy)", "Increases tissue extensibility; reduces muscle spasm; increases collagen compliance", "Pre-stretch; joint stiffness; chronic capsulitis"],
["Ultrasound (therapeutic)", "Deep heating; increases collagen extensibility; possible anti-inflammatory effect", "Chronic joint/soft tissue stiffness; scar tissue"],
["TENS / Electrotherapy", "Pain modulation (gate control + endorphin release)", "Pain-limited ROM; enables active exercise"],
["Cryotherapy (cold)", "Reduces inflammation; decreases pain; controls acute effusion", "Post-exercise; acute inflammatory flares; post-op"],
["Hydrotherapy", "Buoyancy reduces joint loading; warmth enhances mobility; resistance enables exercise", "Active-assisted exercise; early weight-bearing rehabilitation"],
["Taping / Bracing", "Proprioceptive feedback; unloads painful structures; stabilizes hypermobile joints", "Patellofemoral pain; ankle instability; postural re-education"],
]
story.append(tbl(phys_data, [3.5*cm, 5.5*cm, 8*cm], header_color=GREY, row_colors=(LGREY, colors.white)))
story.append(sp())
# ══════════════════════════════════════════════════════════════
story.append(h1("9. Documentation and Outcome Measures"))
story.append(body(
"Accurate documentation of movement assessment is essential for communication, medico-legal purposes, "
"and monitoring rehabilitation progress."
))
story += [
bul("<b>Goniometric recording:</b> Record starting position, plane of movement, AROM, PROM, and pain during measurement. Use neutral-zero method (0° = anatomical position)."),
bul("<b>SOAP notes:</b> Subjective (patient report) | Objective (ROM, strength, end-feel) | Assessment (clinical interpretation) | Plan (treatment goals)."),
bul("<b>ICF framework:</b> Document impairment (reduced ROM), activity limitation (cannot reach overhead), and participation restriction (cannot work)."),
sp(0.3)
]
outcome_data = [
["Outcome Measure", "Joint / Region", "What it Measures"],
["DASH / QuickDASH", "Upper limb", "Function and disability of shoulder, arm, and hand"],
["ASES Score", "Shoulder", "Pain + function (patient and clinician components)"],
["Oxford Knee / KOOS", "Knee", "Pain, symptoms, function (knee-specific)"],
["HOOS / Harris Hip Score", "Hip", "Pain, function, and quality of life"],
["FAOS / FAAM", "Foot and ankle", "Function in ADL and sport"],
["NDI (Neck Disability Index)", "Cervical spine", "Pain and disability of neck conditions"],
["Oswestry Disability Index", "Lumbar spine", "Low back pain disability"],
["Beighton Score", "Whole body", "Generalized joint hypermobility screening (0-9)"],
]
story.append(tbl(outcome_data, [4.5*cm, 3.5*cm, 9*cm], header_color=BLUE, row_colors=(LBLUE, colors.white)))
story.append(sp())
# ══════════════════════════════════════════════════════════════
story.append(h1("10. Quick-Reference Summary"))
story.append(h2("Key Principles at a Glance"))
story += [
bul("<b>Always assess active first, then passive.</b> If active < passive = contractile tissue. If both restricted equally = articular."),
bul("<b>Compare bilaterally</b> - the contralateral side is the single best reference for normality."),
bul("<b>End-feel tells you WHY ROM is limited:</b> bony = OA or bony block; empty = serious pathology; springy = meniscal/loose body; boggy = effusion."),
bul("<b>Capsular pattern identifies intra-articular pathology</b> - confirm with multiple planes of passive ROM."),
bul("<b>Resisted movement isolates contractile tissue</b> - painful + weak = serious; painful + strong = tendinitis; painless + weak = complete rupture or nerve lesion."),
bul("<b>'Motion is lotion'</b> - early joint mobilization prevents cartilage degeneration, reduces adhesion formation, and promotes healing."),
bul("<b>Trick movements exist</b> - partial Achilles rupture still allows plantarflexion; partial quadriceps rupture may still allow gait. Confirm specific active resistance tests."),
bul("<b>CPM has a role</b> post-operatively to maintain cartilage nutrition and prevent stiffness, but must be combined with active exercise programs."),
sp(0.3)
]
# FOOTER
story.append(hr())
story.append(Paragraph(
"References: Rheumatology (Elsevier, 2022) | Campbell's Operative Orthopaedics 15e (Elsevier, 2026) | "
"Bailey & Love's Short Practice of Surgery 28e | Miller's Review of Orthopaedics 9e | "
"Adams & Victor's Principles of Neurology 12e | Firestein & Kelley's Textbook of Rheumatology",
note_s
))
story.append(Paragraph("Generated by Orris Medical Reference | July 2026", src_s))
doc.build(story)
print(f"PDF generated: {OUTPUT}")
cd /home/daytona/workspace/bacteria-culture-media && python generate_physio_pdf.…
ls -lh /home/daytona/workspace/bacteria-culture-media/Active_Passive_Movements_J…
Active Passive Movements Joint Mobility
PDF Document · PDF
General laboratory practices in biochemistry pdf on this please
~/bacteria-culture-media/generate_biochem_lab.py
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm
from reportlab.lib import colors
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, KeepTogether
)
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
OUTPUT = "/home/daytona/workspace/bacteria-culture-media/General_Laboratory_Practices_Biochemistry.pdf"
doc = SimpleDocTemplate(
OUTPUT, pagesize=A4,
leftMargin=2*cm, rightMargin=2*cm,
topMargin=2.5*cm, bottomMargin=2.5*cm
)
# ── Colours ────────────────────────────────────────────────────────────────────
NAVY = colors.HexColor("#0d2b4e")
TEAL = colors.HexColor("#006064")
GREEN = colors.HexColor("#2e7d32")
ORANGE = colors.HexColor("#bf360c")
PURPLE = colors.HexColor("#4a148c")
RED = colors.HexColor("#b71c1c")
GREY = colors.HexColor("#37474f")
BROWN = colors.HexColor("#4e342e")
LBLUE = colors.HexColor("#e3f2fd")
LGREEN = colors.HexColor("#e8f5e9")
LPURP = colors.HexColor("#f3e5f5")
LYELL = colors.HexColor("#fffde7")
LTEAL = colors.HexColor("#e0f7fa")
LGREY = colors.HexColor("#eceff1")
styles = getSampleStyleSheet()
def ps(name, **kw):
base = kw.pop("parent", styles["Normal"])
return ParagraphStyle(name, parent=base, **kw)
title_s = ps("T", fontSize=22, textColor=NAVY, alignment=TA_CENTER, fontName="Helvetica-Bold", spaceAfter=4)
sub_s = ps("S", fontSize=11, textColor=GREY, alignment=TA_CENTER, fontName="Helvetica-Oblique", spaceAfter=18)
h1_s = ps("H1", fontSize=14, textColor=colors.white, backColor=NAVY,
fontName="Helvetica-Bold", spaceAfter=8, spaceBefore=14, borderPad=6)
h2_s = ps("H2", fontSize=12, textColor=NAVY, fontName="Helvetica-Bold",
spaceAfter=5, spaceBefore=10)
h3_s = ps("H3", fontSize=10.5, textColor=TEAL, fontName="Helvetica-Bold",
spaceAfter=4, spaceBefore=7)
body_s = ps("B", fontSize=10, leading=15, alignment=TA_JUSTIFY,
spaceAfter=7, fontName="Helvetica")
bullet_s = ps("BL", fontSize=10, leading=14, spaceAfter=3,
leftIndent=16, bulletIndent=6, fontName="Helvetica")
note_s = ps("N", fontSize=9, textColor=GREY, backColor=LYELL,
borderPad=5, leading=13, leftIndent=6, rightIndent=6,
fontName="Helvetica-Oblique", spaceAfter=8)
clinical_s= ps("CL", fontSize=9.5, textColor=colors.HexColor("#1b5e20"),
backColor=LGREEN, borderPad=6, leading=14,
leftIndent=6, rightIndent=6, fontName="Helvetica", spaceAfter=8)
warn_s = ps("W", fontSize=9.5, textColor=colors.HexColor("#7f0000"),
backColor=colors.HexColor("#fff3f3"), borderPad=6, leading=14,
leftIndent=6, rightIndent=6, fontName="Helvetica", spaceAfter=8)
src_s = ps("SR", fontSize=8, textColor=colors.HexColor("#aaaaaa"),
alignment=TA_CENTER, spaceAfter=3)
def h1(t): return Paragraph(f" {t}", h1_s)
def h2(t): return Paragraph(t, h2_s)
def h3(t): return Paragraph(t, h3_s)
def body(t): return Paragraph(t, body_s)
def bul(t): return Paragraph(f"• {t}", bullet_s)
def note(t): return Paragraph(t, note_s)
def clinical(t): return Paragraph(t, clinical_s)
def warn(t): return Paragraph(t, warn_s)
def sp(h=0.3): return Spacer(1, h*cm)
def hr(): return HRFlowable(width="100%", thickness=0.5,
color=colors.HexColor("#cccccc"), spaceAfter=5)
def tbl(data, widths, hc=NAVY, rc=(LBLUE, colors.white), fs=9):
t = Table(data, colWidths=widths)
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), hc),
("TEXTCOLOR", (0,0), (-1,0), colors.white),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), fs),
("ROWBACKGROUNDS", (0,1), (-1,-1), list(rc)),
("GRID", (0,0), (-1,-1), 0.35, colors.HexColor("#bbbbbb")),
("VALIGN", (0,0), (-1,-1), "TOP"),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING",(0,0),(-1,-1), 5),
("LEFTPADDING",(0,0), (-1,-1), 5),
]))
return t
story = []
# ── TITLE ──────────────────────────────────────────────────────────────────────
story += [sp(2),
Paragraph("General Laboratory Practices", title_s),
Paragraph("in Biochemistry", title_s),
Paragraph("A Comprehensive Reference for Students", sub_s),
hr(),
Paragraph("Sources: Tietz Textbook of Laboratory Medicine 7e | Henry's Clinical Diagnosis & Management by Laboratory Methods | Quick Compendium of Clinical Pathology 5e | Basic Medical Biochemistry 6e", src_s),
sp(1.2),
]
# ══════════════════════════════════════════════════════════════════════════════
story.append(h1("1. Introduction to the Clinical Biochemistry Laboratory"))
story.append(body(
"The clinical biochemistry laboratory (also called clinical chemistry laboratory) is the section of the diagnostic "
"laboratory responsible for quantitative chemical analysis of body fluids - primarily blood (serum/plasma), urine, "
"CSF, and other biological samples. It provides approximately 70% of all objective clinical information used in "
"patient management decisions."
))
story.append(body(
"Good laboratory practice (GLP) is a set of principles that ensures the consistency, reliability, reproducibility, "
"and integrity of laboratory work. It covers every phase of the laboratory process: from patient preparation and "
"specimen collection, through analysis, to result reporting and interpretation."
))
story.append(h2("1.1 Three Phases of the Laboratory Process"))
phase_data = [
["Phase", "Definition", "Key Activities", "% of Total Errors"],
["Pre-analytical", "Everything before the sample is analysed", "Patient prep, specimen collection, labelling, transport, processing, storage", "~60-70%"],
["Analytical", "The actual measurement/testing process", "Instrument function, reagents, calibration, assay performance, QC", "~15-20%"],
["Post-analytical", "Everything after the result is produced", "Result reporting, reference intervals, interpretation, critical value notification", "~15-20%"],
]
story.append(tbl(phase_data, [3*cm, 4*cm, 6.5*cm, 3.5*cm], hc=NAVY, rc=(LBLUE, colors.white)))
story.append(sp())
story.append(note(
"Most laboratory errors occur in the pre-analytical phase (~60-70%). Reducing pre-analytical errors "
"has the greatest impact on overall laboratory quality. (Tietz Textbook of Laboratory Medicine 7e)"
))
# ══════════════════════════════════════════════════════════════════════════════
story.append(h1("2. Laboratory Safety"))
story.append(h2("2.1 General Safety Principles"))
story += [
bul("<b>Standard (Universal) Precautions:</b> Treat all patient specimens as potentially infectious. Wear gloves, lab coat, and eye protection at all times when handling specimens."),
bul("<b>Personal Protective Equipment (PPE):</b> Gloves (change between patients/specimens), lab coat/gown, eye shield/goggles, mask when aerosols possible."),
bul("<b>No eating, drinking, or applying cosmetics</b> in the laboratory. Never pipette by mouth."),
bul("<b>Hand hygiene:</b> Wash hands before and after glove use, after removing PPE, and before leaving the laboratory."),
bul("<b>Sharps safety:</b> Never recap needles by two-handed technique. Use safety-engineered needles. Dispose in puncture-resistant sharps containers."),
bul("<b>Biohazard waste disposal:</b> All clinical specimens, contaminated materials, and cultures must be disposed of in labelled biohazard bags/containers."),
bul("<b>Chemical safety:</b> Know the Safety Data Sheet (SDS) for every chemical used. Store acids and bases separately. Use fume hoods for volatile reagents."),
bul("<b>Fire safety:</b> Know the location of fire extinguishers, exits, and the RACE protocol (Rescue, Alarm, Contain, Extinguish/Evacuate)."),
sp(0.3),
]
story.append(h2("2.2 Biological Hazard (Biosafety) Levels"))
bsl_data = [
["BSL Level", "Risk", "Organisms", "Required Containment"],
["BSL-1", "Minimal risk", "Non-pathogenic E. coli, Bacillus subtilis", "Basic lab practices, no special containment"],
["BSL-2", "Moderate risk (most clinical labs)", "S. aureus, Salmonella, Hepatitis B/C, HIV", "Biosafety cabinet (BSC) Class II for aerosols; restricted access"],
["BSL-3", "Serious risk; potential aerosol transmission", "M. tuberculosis, Yersinia pestis, SARS-CoV-2 (some work)", "BSC Class II required; negative pressure room; respiratory protection"],
["BSL-4", "Lethal risk; no treatment available", "Ebola, Marburg, Lassa, Crimean-Congo haemorrhagic fever", "Full positive-pressure suit; completely isolated facility"],
]
story.append(tbl(bsl_data, [2.5*cm, 3*cm, 5*cm, 6.5*cm], hc=RED, rc=(colors.HexColor("#fff3f3"), colors.white)))
story.append(sp())
story.append(h2("2.3 Chemical Hazard Classes (GHS)"))
story += [
bul("<b>Flammable:</b> Methanol, ethanol, acetone, diethyl ether. Store in flammable cabinets. Avoid open flames."),
bul("<b>Corrosive:</b> Concentrated HCl, H₂SO₄, NaOH. Store in acid/base cabinets. Handle with appropriate gloves (nitrile for acids)."),
bul("<b>Toxic/Carcinogenic:</b> Formaldehyde, benzidine, chloroform, acrylamide. Use only in fume hood. Mercury-containing compounds require special disposal."),
bul("<b>Oxidising:</b> H₂O₂, KMnO₄, HNO₃. Separate from flammables and organics. Risk of fire/explosion."),
bul("<b>Radioactive:</b> ³H, ¹⁴C, ³²P, ¹²⁵I isotopes in radioimmunoassay (RIA). Requires radiation licence, shielding, and dosimetry monitoring."),
sp(0.3)
]
# ══════════════════════════════════════════════════════════════════════════════
story.append(h1("3. Laboratory Glassware and Equipment"))
story.append(h2("3.1 Common Glassware"))
glass_data = [
["Item", "Type", "Use", "Notes"],
["Beaker", "Class B (approximate)", "Mixing, heating, holding solutions", "Not for accurate volume measurement"],
["Conical (Erlenmeyer) Flask", "Class B", "Mixing, titrations, heating", "Wide base reduces spilling; not for accurate volume"],
["Round-bottom flask", "Class B", "Heating/distillation", "Distributes heat evenly; requires ring stand"],
["Volumetric flask", "Class A (accurate)", "Preparing exact-concentration standard solutions", "Never heat; single volume marking; most accurate for solutions"],
["Graduated cylinder", "Class B", "Approximate volume measurement", "More accurate than beakers; less accurate than volumetric flask"],
["Burette", "Class A", "Titrimetry - deliver precise variable volumes", "Read bottom of meniscus; rinse with solution before use"],
["Pipette (volumetric/transfer)", "Class A", "Deliver exact single volume", "Most accurate pipette; one mark only"],
["Pipette (graduated/Mohr)", "Class A/B", "Deliver variable volumes within its range", "Read to nearest graduation"],
["Test tube", "Borosilicate", "Small-volume reactions, sample containers", "Borosilicate (Pyrex) withstands temperature changes"],
["Cuvette", "Optical glass/quartz/plastic", "Spectrophotometry", "Quartz for UV; glass/plastic for visible range; 1 cm path length standard"],
["Watch glass", "N/A", "Covering beakers, evaporating small volumes, weighing", "Not heated directly"],
["Funnel", "N/A", "Liquid transfer, filtration (with filter paper)", "Buchner funnel for vacuum filtration"],
]
story.append(tbl(glass_data, [4*cm, 2.5*cm, 4.5*cm, 6*cm], hc=TEAL, rc=(LTEAL, colors.white)))
story.append(sp())
story.append(h2("3.2 Pipettes and Micropipettes"))
story.append(body(
"Accurate pipetting is fundamental to biochemistry. Errors in pipetting directly translate into errors in "
"analyte concentration and assay results."
))
story += [
bul("<b>Serological pipettes:</b> Graduated glass or plastic pipettes (1-50 mL). Used with a pipette filler/bulb. Calibrated to deliver (TD) or to contain (TC)."),
bul("<b>Pasteur pipettes:</b> Non-calibrated, for transferring liquids. Disposable or reusable glass. Not for volumetric work."),
bul("<b>Micropipettes (air-displacement):</b> Most common in biochemistry labs. Fixed or variable volume. Ranges: P2 (0.2-2 µL), P10, P20, P100, P200, P1000. Use correct tip size. Calibrated in µL."),
bul("<b>Positive-displacement pipettes:</b> For viscous, volatile, or corrosive liquids (blood, organic solvents). The plunger contacts the liquid directly - no air cushion."),
bul("<b>Repeat dispensers/Multipette:</b> Deliver equal aliquots multiple times from one aspiration. Reduces repetitive strain and contamination."),
sp(0.2)
]
story.append(h3("Micropipette Technique (Correct Method)"))
story += [
bul("1. Set the volume BEFORE attaching a tip. Never exceed the maximum or go below minimum volume."),
bul("2. Attach an appropriate tip firmly. Tip contamination is a major source of error."),
bul("3. Pre-wet tip: aspirate and dispense the solution once before making the actual transfer."),
bul("4. Hold pipette vertical (±20°) during aspiration. Angling increases aspirated volume."),
bul("5. Depress to the first stop, immerse tip 2-3 mm below surface, release plunger slowly."),
bul("6. To dispense: touch tip to container wall, depress to first stop, pause, then push to second stop (blow-out)."),
bul("7. Discard tip after each sample. Never share tips between specimens."),
sp(0.3)
]
story.append(note(
"Calibration: Micropipettes must be calibrated regularly (typically every 6-12 months or whenever accuracy is suspect). "
"Calibration is done gravimetrically - weigh the volume of distilled water dispensed (1 µL water = ~1 mg at 20°C). "
"Temperature, altitude, and user technique all affect accuracy."
))
story.append(h2("3.3 Analytical Balance"))
story += [
bul("Use for all weighing to ≥4 decimal places (0.0001 g = 0.1 mg precision)."),
bul("Zero (tare) balance before every weighing. Use weighing paper, boats, or pre-weighed containers."),
bul("<b>Never</b> weigh chemicals directly on the balance pan - use vessels or weighing paper."),
bul("Allow balance to warm up (usually 30 min) and level (check bubble level indicator) before use."),
bul("Calibrate with NIST-traceable weights. External calibration performed monthly; internal calibration daily."),
bul("Keep balance area clean, draft-free, and vibration-free."),
sp(0.3)
]
story.append(h2("3.4 Centrifuge"))
story += [
bul("Used to separate blood cells from serum/plasma, pellet precipitates, and separate phases."),
bul("<b>Speed:</b> Expressed as RPM or relative centrifugal force (RCF/g). RCF = 1.118 × 10⁻⁵ × r × N², where r = rotor radius (cm) and N = RPM."),
bul("Routine serum/plasma separation: 2000-3000 RCF for 10 minutes."),
bul("<b>Balance rotor:</b> Always balance opposite tubes by weight (not just volume) before centrifugation."),
bul("Refrigerated centrifuge (4°C) required for temperature-sensitive analytes (e.g., cryoglobulins, some hormones)."),
bul("Ultracentrifuge (100,000-500,000 g): For lipoprotein fractionation, organelle isolation, density gradient separations."),
sp(0.3)
]
story.append(h2("3.5 Spectrophotometer"))
story.append(body(
"The spectrophotometer is the cornerstone instrument in clinical biochemistry. It measures the absorbance or "
"transmittance of a solution at a specific wavelength, allowing quantification of analytes via Beer-Lambert Law."
))
story.append(body(
"<b>Beer-Lambert Law: A = ε × c × l</b> where A = absorbance (dimensionless), ε = molar absorptivity "
"(L·mol⁻¹·cm⁻¹), c = concentration (mol/L), l = path length (cm, usually 1 cm). "
"Absorbance is directly proportional to concentration within the linear range of the assay."
))
beer_data = [
["Term", "Symbol", "Definition"],
["Absorbance", "A", "A = log₁₀(I₀/I) = log₁₀(1/T). Dimensionless. Linear with concentration."],
["Transmittance", "T", "T = I/I₀. Fraction of light passing through. Range 0-1 (or 0-100%)."],
["Molar absorptivity", "ε", "Intrinsic property of the molecule at a given wavelength. L·mol⁻¹·cm⁻¹."],
["Path length", "l", "Distance light travels through the solution. Typically 1 cm."],
["Linear range", "—", "Concentration range over which A vs c is linear. Beyond this, Beer's Law fails (high concentrations, complex formation)."],
]
story.append(tbl(beer_data, [4*cm, 2.5*cm, 10.5*cm], hc=GREEN, rc=(LGREEN, colors.white)))
story.append(sp())
story += [
bul("<b>Wavelength selection:</b> Set to the absorption maximum (λmax) of the coloured compound to maximise sensitivity."),
bul("<b>Blanking:</b> Zero the instrument with a reagent blank (all reagents minus analyte) before measuring standards and samples."),
bul("<b>Standard curve:</b> Prepare multiple known concentrations (standards). Plot A vs concentration. Unknown concentration is read from the curve."),
bul("<b>Cuvette care:</b> Clean with distilled water before and after use. Do not scratch optical faces. Quartz cuvettes for UV (below 320 nm); glass or plastic for visible."),
sp(0.3)
]
# ══════════════════════════════════════════════════════════════════════════════
story.append(h1("4. Solutions, Concentrations, and Reagent Preparation"))
story.append(h2("4.1 Types of Concentration Expressions"))
conc_data = [
["Expression", "Symbol/Unit", "Definition", "Example"],
["Molarity", "M (mol/L)", "Moles of solute per litre of solution", "1 M NaCl = 58.44 g/L (MW NaCl = 58.44)"],
["Normality", "N (Eq/L)", "Equivalents of solute per litre. N = M × n (n = valence/H⁺ ions)", "1 N H₂SO₄ = 0.5 M (2 H⁺ per molecule)"],
["Molality", "m (mol/kg)", "Moles of solute per kg of solvent (not solution)", "Used for colligative properties"],
["Weight/volume %", "w/v %", "Grams of solute per 100 mL of solution", "5% glucose = 5 g/100 mL = 50 g/L"],
["Volume/volume %", "v/v %", "mL of solute per 100 mL of solution", "70% ethanol = 70 mL ethanol + 30 mL water"],
["Parts per million", "ppm", "mg of solute per litre (µg/mL)", "Used for trace elements, environmental samples"],
["Millimoles per litre", "mmol/L", "SI unit for most clinical chemistry analytes", "Serum sodium: 135-145 mmol/L"],
["Milliequivalents per litre", "mEq/L", "Traditional unit for electrolytes; mEq/L = mmol/L × valence", "Na⁺ (valence 1): 140 mEq/L = 140 mmol/L"],
]
story.append(tbl(conc_data, [3.5*cm, 2.5*cm, 5*cm, 6*cm], hc=PURPLE, rc=(LPURP, colors.white)))
story.append(sp())
story.append(h2("4.2 Preparing Standard Solutions"))
story += [
bul("<b>Primary standard:</b> Pure, stable, high-molecular-weight substance that can be accurately weighed. Used to prepare a solution of exact concentration. Examples: Na₂CO₃, KIO₃, oxalic acid."),
bul("<b>Secondary standard:</b> Solution whose concentration is determined by titration against a primary standard (standardised). Example: NaOH, HCl (absorb CO₂/moisture; cannot be primary standards)."),
bul("<b>Preparing a standard solution:</b> Weigh exactly, dissolve in small volume of solvent in a beaker, quantitatively transfer to a volumetric flask, make up to the mark with solvent, invert to mix."),
bul("<b>Dilution formula:</b> C₁V₁ = C₂V₂ (concentration₁ × volume₁ = concentration₂ × volume₂)."),
bul("<b>Serial dilution:</b> Sequential equal-ratio dilutions (e.g., 1:2, 1:4, 1:8). Used for standard curves and antibody titrations."),
sp(0.3)
]
story.append(h2("4.3 Water Quality in the Laboratory"))
water_data = [
["Grade", "Type", "Resistivity", "Uses"],
["Grade 1 (Type I)", "Ultrapure (MilliQ, nanopure)", ">18 MΩ·cm", "HPLC, mass spectrometry, critical molecular biology assays, elemental analysis"],
["Grade 2 (Type II)", "Deionised/distilled", ">1 MΩ·cm", "General laboratory reagent prep, buffer preparation, most analytical assays"],
["Grade 3 (Type III)", "Purified (reverse osmosis)", ">0.05 MΩ·cm", "Glassware washing, autoclave feed, general non-critical uses"],
["Tap water", "Unpurified", "Variable", "Cooling, non-contact applications only - NEVER for reagent preparation"],
]
story.append(tbl(water_data, [4*cm, 4*cm, 3.5*cm, 5.5*cm], hc=TEAL, rc=(LTEAL, colors.white)))
story.append(sp())
story.append(h2("4.4 Buffer Preparation"))
story += [
bul("<b>Buffer:</b> A solution that resists change in pH when small amounts of acid or base are added. Made from a weak acid and its conjugate base (or weak base and conjugate acid)."),
bul("<b>Henderson-Hasselbalch equation:</b> pH = pKa + log ([A⁻]/[HA]). Buffer is most effective within ±1 pH unit of pKa."),
bul("<b>Buffer capacity:</b> The ability to resist pH change. Maximal at pH = pKa. Increased by higher buffer concentration."),
bul("<b>Common lab buffers:</b> Phosphate (pKa 7.2; physiological pH); Tris (pKa 8.1; molecular biology); Citrate (pKa 3.1, 4.8, 6.4; enzyme assays); HEPES/MOPS (biological, non-toxic); Acetate (pKa 4.75; acidic pH range)."),
bul("<b>pH meter calibration:</b> Use two-point calibration with standard buffers (pH 4.0 and 7.0; or 7.0 and 10.0) before every use."),
sp(0.3)
]
# ══════════════════════════════════════════════════════════════════════════════
story.append(h1("5. Specimen Collection and Pre-Analytical Considerations"))
story.append(h2("5.1 Types of Blood Specimens"))
blood_data = [
["Specimen Type", "How Obtained", "Anticoagulant", "Uses in Biochemistry"],
["Serum", "Allow blood to clot (30 min); centrifuge; pipette clear supernatant", "None (clotted)", "Most biochemistry tests (enzymes, proteins, lipids, hormones, immunoassays)"],
["Plasma (EDTA)", "Collect in purple/lavender top; mix well; centrifuge", "EDTA (binds Ca²⁺ and Mg²⁺)", "Haematology, HbA1c, some coagulation studies - NOT for Ca²⁺ or Mg²⁺ assays"],
["Plasma (Li-heparin)", "Green top tube; mix; centrifuge immediately", "Lithium heparin (inhibits thrombin)", "Most chemistry tests; preferred when rapid result needed (no clot wait)"],
["Plasma (Na-citrate)", "Blue top; 9:1 blood:anticoagulant ratio; mix", "Sodium citrate (binds Ca²⁺)", "Coagulation tests (PT, APTT, fibrinogen) - CRITICAL to fill correctly"],
["Plasma (NaF/oxalate)", "Grey top; mix", "NaF (glycolysis inhibitor) + potassium oxalate", "Glucose and lactate only (NaF prevents glycolysis in vitro)"],
["Whole blood", "Collected into EDTA or heparin; NOT centrifuged", "EDTA or heparin", "Blood gases, whole blood glucose (POC), CBC"],
["Urine (random)", "Midstream clean catch in sterile container", "None", "Dipstick, microscopy, urine biochemistry (spot tests)"],
["Urine (24-hour)", "Collect all urine over 24h; measure total volume; send aliquot", "Preservative (HCl, boric acid, or refrigerate)", "Creatinine clearance, 24h protein, catecholamines, hormones"],
["CSF", "Lumbar puncture (physician)", "None", "CSF glucose, protein, electrophoresis, oligoclonal bands"],
]
story.append(tbl(blood_data, [3.5*cm, 4*cm, 3*cm, 6.5*cm], hc=GREEN, rc=(LGREEN, colors.white)))
story.append(sp())
story.append(h2("5.2 Vacutainer Tube Order of Draw"))
story.append(body(
"When multiple tubes are collected in a single venepuncture, the order of draw prevents cross-contamination "
"of additives between tubes."
))
order_data = [
["Order", "Colour (CLSI)", "Additive", "Use"],
["1st", "Yellow (sterile)", "SPS or sodium polyanethol sulphonate", "Blood cultures (sterile collection)"],
["2nd", "Light blue", "3.2% sodium citrate", "Coagulation studies (PT, APTT)"],
["3rd", "Red (plain) or gold", "None / clot activator ± gel", "Serum chemistry, immunology, serology"],
["4th", "Green", "Li-heparin ± gel", "Plasma chemistry, electrolytes"],
["5th", "Lavender/Purple", "EDTA", "Haematology (CBC, HbA1c, blood film)"],
["6th", "Pink", "EDTA (with transfusion info)", "Blood bank / type and screen"],
["7th", "Grey", "NaF / potassium oxalate", "Glucose, lactate"],
]
story.append(tbl(order_data, [1.5*cm, 3.5*cm, 4*cm, 8*cm], hc=BROWN, rc=(colors.HexColor("#fbe9e7"), colors.white)))
story.append(sp())
story.append(note(
"Mnemonic for order of draw: 'Stop! Light Blood Runs Green, Lavender Pretty Grey' "
"= Sterile/blood culture, Light blue, Blood red, Royal blue, Green, Lavender/Purple, Pink, Grey."
))
story.append(h2("5.3 Specimen Acceptability - Causes for Rejection"))
rej_data = [
["Rejection Cause", "Effect on Results", "Prevention"],
["Haemolysis (most common pre-analytical error)", "Increases K⁺, LDH, AST, ALT, Mg²⁺, phosphate; decreases Na⁺; spectrophotometric interference", "Gentle venepuncture; do not shake tube; avoid small gauge needles; process promptly"],
["Lipaemia (triglycerides >4 mmol/L)", "Turbidity causes spectrophotometric errors; volume displacement effect; falsely low Na⁺ (indirect ISE)", "Collect fasting sample; ultracentrifuge or lipid-clearing reagent if unavoidable"],
["Icterus (bilirubin >342 µmol/L)", "Optical interference in spectrophotometric assays; varies by wavelength and bilirubin type", "Flag result; some methods less susceptible; use bilirubin-blank correction"],
["Clotted sample (in anticoagulant tube)", "Clot may block instrument; results unreliable", "Mix immediately after collection (8-10 inversions gentle); adequate tube filling"],
["Insufficient volume", "Cannot perform all tests; dilution error", "Fill tube to correct volume (especially blue-top citrate)"],
["Wrong tube / wrong additive", "Additive interference (EDTA falsely lowers Ca²⁺/Mg²⁺)", "Verify tube-test compatibility before collection"],
["Mislabelled specimen", "Patient identification error; potentially life-threatening", "Two-identifier patient ID (name + DOB); bedside labelling; barcode systems"],
["Delayed transport / improper storage", "Glucose decreases (glycolysis); K⁺ increases (cell lysis); bilirubin degrades (light)", "Transport promptly; use appropriate preservatives; protect from light"],
["Incorrect patient preparation", "Fasting vs non-fasting affects glucose, triglycerides, LFTs", "Verify patient preparation instructions before collection"],
]
story.append(tbl(rej_data, [4*cm, 5*cm, 8*cm], hc=RED, rc=(colors.HexColor("#fff3f3"), colors.white), fs=8.5))
story.append(sp())
story.append(warn(
"Haemolysis is the most common pre-analytical error and the most common cause of sample rejection. "
"It may cause clinically relevant bias through spectrophotometric interference, sample dilution, and "
"release of intracellular components into the sample. Visual assessment alone is unreliable - "
"serum indices (automated HIL indices) should be used. (Tietz Laboratory Medicine 7e)"
))
story.append(h2("5.4 Timed Specimens and Special Collections"))
story += [
bul("<b>STAT specimens:</b> Analysed immediately; highest priority; ordered from emergency and critical care units."),
bul("<b>Trough level:</b> Drawn 30 minutes before the next dose of a drug. Reflects lowest (minimum) plasma concentration. Used for aminoglycosides, vancomycin, digoxin."),
bul("<b>Peak level:</b> Drawn at specified time after drug administration. Reflects maximum concentration. Time varies by drug and route (e.g., IV peak drawn 30 min post-infusion)."),
bul("<b>Fasting specimens:</b> Patient fasts 8-12 hours (water allowed). Required for: fasting glucose, full lipid profile (LDL calculation), insulin, C-peptide."),
bul("<b>2-hour post-prandial glucose:</b> Blood drawn exactly 2 hours after start of standard meal. Screens for impaired glucose tolerance and T2DM."),
bul("<b>GTT (Oral Glucose Tolerance Test):</b> Fasting glucose, then 75 g oral glucose load, with blood drawn at 0 h, 1 h, and 2 h."),
sp(0.3)
]
# ══════════════════════════════════════════════════════════════════════════════
story.append(h1("6. Quality Control and Quality Assurance"))
story.append(h2("6.1 Key Definitions"))
story += [
bul("<b>Accuracy:</b> How close a measured value is to the true (reference) value. Affected by systematic error (bias)."),
bul("<b>Precision:</b> How reproducible repeated measurements are (closeness to each other). Affected by random error. Expressed as CV (coefficient of variation)."),
bul("<b>Sensitivity (analytical):</b> Smallest detectable change in concentration. Expressed as limit of detection (LOD) or limit of quantification (LOQ)."),
bul("<b>Specificity (analytical):</b> Ability to measure only the intended analyte without interference from other substances."),
bul("<b>Linearity:</b> Range of concentrations over which the assay response is proportional to concentration (linear range)."),
bul("<b>CV (Coefficient of Variation):</b> CV% = (SD / Mean) × 100. Expresses precision as a percentage. Acceptable CV for most biochemistry assays: <5%."),
sp(0.3)
]
story.append(h2("6.2 Internal Quality Control (IQC)"))
story.append(body(
"Internal quality control involves running known-concentration control samples (commercially prepared or in-house) "
"alongside patient samples during each analytical run. Controls are at normal and abnormal/pathological levels."
))
story += [
bul("<b>Levey-Jennings (LJ) chart:</b> Control results plotted over time against a central line (mean) and warning/rejection limits (±1SD, ±2SD, ±3SD). Deviations reveal trends and shifts."),
bul("<b>Westgard Rules:</b> A set of statistical rules applied to LJ charts to decide when a run is 'in control' or when to reject results. Most commonly used: 1₃s, 2₂s, R₄s, 4₁s, 10x."),
bul("<b>Bias:</b> Systematic deviation from the true value. Causes: reagent lot change, calibration drift, instrument error."),
bul("<b>Drift:</b> Gradual change in results over time (seen as a trend on LJ chart). Causes: reagent deterioration, lamp aging."),
bul("<b>Shift:</b> Sudden change in the mean of control results. Causes: new reagent lot, new calibration, instrument service."),
sp(0.2)
]
westgard_data = [
["Westgard Rule", "Trigger", "Error Type", "Action"],
["1₂s (Warning)", "1 control outside ±2SD", "Warning only", "Increase vigilance; do not reject yet"],
["1₃s (Rejection)", "1 control outside ±3SD", "Random or systematic", "Reject run; investigate"],
["2₂s (Rejection)", "2 consecutive controls outside ±2SD on same side", "Systematic (bias)", "Reject; check calibration"],
["R₄s (Rejection)", "Range between two controls >4SD", "Random error", "Reject; check precision"],
["4₁s (Rejection)", "4 consecutive controls outside ±1SD on same side", "Systematic", "Reject; check bias"],
["10x (Rejection)", "10 consecutive controls on same side of mean", "Systematic (shift/trend)", "Reject; check drift"],
]
story.append(tbl(westgard_data, [3*cm, 4.5*cm, 3.5*cm, 6*cm], hc=ORANGE, rc=(colors.HexColor("#fff3e0"), colors.white)))
story.append(sp())
story.append(h2("6.3 External Quality Assurance (EQA) / Proficiency Testing"))
story += [
bul("EQA (also called proficiency testing, PT) compares a laboratory's results with those from other laboratories analysing the same samples."),
bul("Organised by national/international bodies (e.g., UKNEQAS in UK, CAP in USA, RCPA QAP in Australia)."),
bul("Unknown samples are sent to participating laboratories; results are compared against peer group or target value."),
bul("Performance expressed as: SDI (Standard Deviation Index) = (lab result - target) / peer group SD. |SDI| <2 = acceptable."),
bul("Persistent poor EQA performance triggers investigation, method review, staff retraining, or instrument service."),
sp(0.3)
]
story.append(h2("6.4 Reference Intervals"))
story.append(body(
"A reference interval (reference range) is the range of values expected in a healthy reference population. "
"By convention, it encompasses the central 95% of values in the reference population (mean ± 1.96 SD for Gaussian "
"distributions, or 2.5th-97.5th percentile)."
))
story += [
bul("<b>Establishing reference intervals:</b> Minimum 120 healthy individuals per subgroup (CLSI C28-A3). For verification of adopted intervals, test at least 20 individuals from the local population."),
bul("<b>Partitioning:</b> Separate reference intervals for age, sex, pregnancy, and sometimes ethnicity when clinically meaningful differences exist."),
bul("<b>Decision limits (clinical cutoffs):</b> Not the same as reference intervals. Decision limits are set based on clinical outcomes (e.g., glucose ≥7.0 mmol/L fasting = diabetes)."),
bul("<b>Critical values (panic values):</b> Results so abnormal they require immediate clinical notification. Example: K⁺ >6.5 mmol/L, glucose <2.2 mmol/L, pH <7.20."),
sp(0.3)
]
# ══════════════════════════════════════════════════════════════════════════════
story.append(h1("7. Key Analytical Techniques in Biochemistry"))
tech_data = [
["Technique", "Principle", "Applications"],
["Spectrophotometry (UV-Vis)", "Measure absorbance of light at specific wavelength; Beer-Lambert Law", "Enzyme assays (AST, ALT, LDH), glucose, proteins, bilirubin, uric acid"],
["Flame photometry", "Measure emission of light by metal ions excited in a flame", "Na⁺, K⁺, Li⁺ (now largely replaced by ISE)"],
["Ion-Selective Electrode (ISE)", "Potentiometric measurement using ion-selective membrane", "Na⁺, K⁺, Cl⁻, Ca²⁺, pH (blood gases) - standard for modern analysers"],
["Turbidimetry / Nephelometry", "Measure light scattering by particles in suspension", "CRP, serum proteins (albumin, immunoglobulins, complement)"],
["Immunoassay (ELISA, CLIA, ECLIA)", "Antigen-antibody binding + label detection (enzyme, chemiluminescent)", "Hormones (TSH, T4, insulin), tumour markers, troponin, therapeutic drug monitoring"],
["Electrophoresis", "Separation by charge and size in electric field", "Serum protein electrophoresis (SPEP), haemoglobin variants, isoenzymes"],
["HPLC (High-Performance Liquid Chromatography)", "Separation by interaction with stationary phase", "HbA1c, amino acids, therapeutic drugs, vitamins"],
["Electrochemistry (amperometry)", "Measure current generated by electrochemical reaction", "Blood glucose (glucose oxidase electrode), pO₂, pCO₂"],
["Atomic Absorption Spectroscopy (AAS)", "Measures absorption of light by free atoms in gas phase", "Trace elements: Pb, Cu, Zn, Fe, Cd, Se"],
["Mass Spectrometry (LC-MS/MS)", "Ionisation + mass-to-charge ratio separation", "Therapeutic drug monitoring, newborn screening, steroids, vitamins D/B12"],
]
story.append(tbl(tech_data, [4.5*cm, 5*cm, 7.5*cm], hc=NAVY, rc=(LBLUE, colors.white)))
story.append(sp())
# ══════════════════════════════════════════════════════════════════════════════
story.append(h1("8. Common Biochemistry Tests - Reference Values"))
story.append(body("Reference values shown are approximate adult ranges. Always use laboratory-specific reference intervals."))
ref_data = [
["Test", "Specimen", "Reference Range (Adult)", "Clinical Significance"],
["Glucose (fasting)", "Plasma (NaF)", "3.9-6.1 mmol/L", "Diabetes (≥7.0), hypoglycaemia (<2.8)"],
["HbA1c", "EDTA whole blood", "<42 mmol/mol (6.0%)", "Diabetes control; ≥48 mmol/mol = diabetic"],
["Urea", "Serum", "2.5-7.8 mmol/L", "Renal function; elevated in pre-renal AKI"],
["Creatinine", "Serum", "55-115 µmol/L (M); 45-90 (F)", "GFR estimation; renal function"],
["eGFR", "Calculated", ">60 mL/min/1.73m²", "CKD stages 1-5"],
["Uric acid", "Serum", "200-430 µmol/L (M); 140-360 (F)", "Gout; elevated in metabolic syndrome"],
["Sodium (Na⁺)", "Serum/plasma", "135-145 mmol/L", "Electrolyte balance; hypo/hypernatraemia"],
["Potassium (K⁺)", "Serum", "3.5-5.1 mmol/L", "Critical: <3.0 or >6.0 mmol/L"],
["Chloride (Cl⁻)", "Serum", "98-107 mmol/L", "Acid-base balance"],
["Bicarbonate (HCO₃⁻)", "Venous blood", "22-29 mmol/L", "Metabolic acid-base status"],
["Calcium (total)", "Serum", "2.20-2.65 mmol/L", "Hyper/hypocalcaemia; parathyroid disease"],
["Phosphate", "Serum (fasting)", "0.87-1.45 mmol/L", "Bone disease, renal failure"],
["Magnesium", "Serum", "0.75-1.05 mmol/L", "Hypomagnesaemia in malnutrition, diuretics"],
["Total protein", "Serum", "60-80 g/L", "Nutrition, liver, kidney, inflammatory diseases"],
["Albumin", "Serum", "35-52 g/L", "Nutritional status; liver function; inflammation"],
["Total bilirubin", "Serum", "<21 µmol/L", "Jaundice (>50 µmol/L visible jaundice)"],
["Direct (conjugated) bilirubin", "Serum", "<5 µmol/L", "Hepatocellular or cholestatic disease"],
["ALT", "Serum", "5-55 U/L", "Hepatocellular damage; most specific for liver"],
["AST", "Serum", "5-45 U/L", "Liver, muscle, cardiac damage"],
["ALP", "Serum", "40-150 U/L", "Cholestasis, bone disease, infiltrative liver disease"],
["GGT", "Serum", "10-70 U/L", "Cholestasis; alcohol; drug-induced liver disease"],
["LDH", "Serum", "140-280 U/L", "Non-specific tissue damage; haemolysis; malignancy"],
["CK (Creatine kinase)", "Serum", "<200 U/L (M); <150 (F)", "Muscle damage (CK-MM); MI (CK-MB); cerebral (CK-BB)"],
["Total cholesterol", "Serum (fasting)", "<5.0 mmol/L (desirable)", "Cardiovascular risk"],
["LDL cholesterol", "Serum (calculated)", "<3.0 mmol/L (target)", "Primary CV risk factor"],
["HDL cholesterol", "Serum", ">1.0 (M); >1.2 (F) mmol/L", "Protective; low HDL = increased CV risk"],
["Triglycerides", "Serum (fasting)", "<1.7 mmol/L", "Pancreatitis risk >11 mmol/L; metabolic syndrome"],
["TSH", "Serum", "0.4-4.5 mIU/L", "Thyroid function; most sensitive test"],
["Free T4", "Serum", "10-25 pmol/L", "Hypo/hyperthyroidism with TSH"],
["CRP", "Serum", "<5 mg/L (<1 mg/L for hsCRP)", "Inflammation, infection, cardiac risk (hsCRP)"],
]
story.append(tbl(ref_data, [4*cm, 2.5*cm, 3.5*cm, 7*cm], hc=GREY, rc=(LGREY, colors.white), fs=8.5))
story.append(sp())
# ══════════════════════════════════════════════════════════════════════════════
story.append(h1("9. Laboratory Calculations - Quick Reference"))
story.append(h2("9.1 Essential Formulas"))
calc_data = [
["Calculation", "Formula", "Notes"],
["Molarity (M)", "M = (mass in g) / (MW × volume in L)", "MW = molecular weight (g/mol)"],
["Dilution", "C₁V₁ = C₂V₂", "C = concentration; V = volume"],
["CV%", "CV% = (SD / Mean) × 100", "Precision measure; lower = more precise"],
["Absorbance", "A = ε × c × l", "Beer-Lambert Law; A = log(I₀/I)"],
["Unknown concentration", "C_unknown = (A_unknown / A_standard) × C_standard", "From standard curve or factor method"],
["Anion gap", "AG = [Na⁺] - ([Cl⁻] + [HCO₃⁻])", "Normal: 8-12 mmol/L; elevated = MUDPILES"],
["Corrected calcium", "= Total Ca + 0.02 × (40 - albumin g/L)", "Corrects for hypoalbuminaemia"],
["eGFR (CKD-EPI/MDRD)", "CKD-EPI equation using Cr, age, sex, race", "Standard clinical estimate of GFR"],
["Osmolality (calculated)", "2[Na] + [glucose] + [urea] (all mmol/L)", "Normal: 275-295 mOsmol/kg"],
["Osmolal gap", "Measured Osm − Calculated Osm", "Gap >10 suggests unmeasured osmoles (alcohols, toxins)"],
["LDL (Friedewald)", "LDL = Total Chol − HDL − (TG/2.2)", "Invalid if TG >4.5 mmol/L"],
["RCF (centrifuge)", "RCF (g) = 1.118 × 10⁻⁵ × r(cm) × RPM²", "Convert RPM to g-force"],
]
story.append(tbl(calc_data, [4.5*cm, 5.5*cm, 7*cm], hc=NAVY, rc=(LBLUE, colors.white)))
story.append(sp())
# ══════════════════════════════════════════════════════════════════════════════
story.append(h1("10. Documentation, Result Reporting and Ethics"))
story.append(h2("10.1 Laboratory Information System (LIS)"))
story += [
bul("The LIS tracks specimen from receipt, through testing, to result authorisation and reporting."),
bul("Unique specimen ID (accession number) links the specimen to the patient, test request, and result."),
bul("Bidirectional interfaces with analysers allow automatic result capture, reducing transcription errors."),
bul("Critical value notification must be documented: who was notified, at what time, by whom."),
sp(0.2)
]
story.append(h2("10.2 Result Authorisation and Reporting"))
story += [
bul("<b>Authorised verification:</b> Results must be reviewed and authorised (signed off) by a qualified person before release."),
bul("<b>Delta check:</b> Automated comparison of current result with previous result for the same patient. Large unexpected changes trigger a hold for review (possible mislabelling or genuine acute change)."),
bul("<b>Critical values:</b> Must be reported immediately by phone to the requesting clinician. The laboratory documents the call."),
bul("<b>Interpretive comments:</b> Added to clarify analytical interferences, limitations, or unexpected findings."),
sp(0.2)
]
story.append(h2("10.3 Laboratory Ethics and Confidentiality"))
story += [
bul("Patient results are confidential. Results must only be released to authorised requestors (clinicians, patients themselves)."),
bul("Follow local data protection legislation (e.g., GDPR in Europe, HIPAA in USA)."),
bul("Do not discuss patient results in public areas."),
bul("Report incidents (errors, near-misses) via the incident reporting system. No-blame culture promotes learning."),
bul("Maintain competency records, training logs, and SOP compliance documentation."),
sp(0.3)
]
# ── FOOTER ─────────────────────────────────────────────────────────────────────
story.append(hr())
story.append(Paragraph(
"References: Tietz Textbook of Laboratory Medicine, 7th ed. (Elsevier, 2023) | Henry's Clinical Diagnosis & Management by Laboratory Methods (Elsevier, 2022) | "
"Quick Compendium of Clinical Pathology, 5th ed. | Basic Medical Biochemistry: A Clinical Approach, 6th ed. (LWW, 2021)",
note_s
))
story.append(Paragraph("Generated by Orris Medical Reference | July 2026", src_s))
doc.build(story)
print(f"PDF generated: {OUTPUT}")
cd /home/daytona/workspace/bacteria-culture-media && python generate_biochem_lab…
ls -lh /home/daytona/workspace/bacteria-culture-media/General_Laboratory_Practic…
General Laboratory Practices Biochemistry
PDF Document · PDF