~/biochem-flowchart/create_pdf.py
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import mm, cm
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, HRFlowable, KeepTogether
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT
from reportlab.platypus.flowables import Flowable
import reportlab.graphics.shapes as shapes
from reportlab.graphics import renderPDF
from reportlab.graphics.shapes import Drawing, Rect, String, Line, Polygon
from reportlab.pdfbase import pdfmetrics
# ── Color palette ────────────────────────────────────────────────────────────
C_HEADER = colors.HexColor("#1a237e") # deep navy
C_HEADER_BG = colors.HexColor("#e8eaf6") # lavender tint
C_POS_BG = colors.HexColor("#e8f5e9") # light green
C_POS_TEXT = colors.HexColor("#1b5e20") # dark green
C_NEG_BG = colors.HexColor("#ffebee") # light red
C_NEG_TEXT = colors.HexColor("#b71c1c") # dark red
C_BOX_BORDER = colors.HexColor("#3949ab") # indigo
C_ACCENT = colors.HexColor("#f57f17") # amber for tips
C_ACCENT_BG = colors.HexColor("#fffde7") # cream
C_MNEM_BG = colors.HexColor("#e3f2fd") # light blue
C_MNEM_TEXT = colors.HexColor("#0d47a1") # blue
C_TABLE_HEAD = colors.HexColor("#3949ab") # indigo
C_TABLE_ALT = colors.HexColor("#f3f4ff") # very light blue
C_WHITE = colors.white
C_BLACK = colors.HexColor("#212121")
C_GOLD = colors.HexColor("#ff8f00")
C_SPEC_BG = colors.HexColor("#fce4ec") # pink tint for special organisms
C_SPEC_TEXT = colors.HexColor("#880e4f")
# ── Page setup ───────────────────────────────────────────────────────────────
OUTPUT = "/tmp/workspace/biochem-flowchart/Biochemical_Tests_NEET_PG.pdf"
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
rightMargin=15*mm, leftMargin=15*mm,
topMargin=15*mm, bottomMargin=15*mm,
title="High-Yield Biochemical Tests – NEET PG Quick Revision",
author="Orris Medical"
)
W = A4[0] - 30*mm # usable width
# ── Styles ───────────────────────────────────────────────────────────────────
base = getSampleStyleSheet()
def style(name, parent="Normal", **kw):
s = ParagraphStyle(name, parent=base[parent])
for k, v in kw.items():
setattr(s, k, v)
return s
sTitle = style("sTitle", "Title",
fontSize=20, textColor=C_HEADER,
alignment=TA_CENTER, spaceAfter=4,
fontName="Helvetica-Bold")
sSubtitle = style("sSubtitle",
fontSize=11, textColor=colors.HexColor("#5c6bc0"),
alignment=TA_CENTER, spaceAfter=2,
fontName="Helvetica")
sSectionHead = style("sSectionHead",
fontSize=13, textColor=C_WHITE,
alignment=TA_LEFT, leading=18,
fontName="Helvetica-Bold",
leftIndent=6, spaceAfter=0, spaceBefore=0)
sCell = style("sCell", fontSize=9, leading=13,
fontName="Helvetica", textColor=C_BLACK)
sCellBold = style("sCellBold", fontSize=9, leading=13,
fontName="Helvetica-Bold", textColor=C_BLACK)
sCellPos = style("sCellPos", fontSize=9, leading=13,
fontName="Helvetica-Bold", textColor=C_POS_TEXT)
sCellNeg = style("sCellNeg", fontSize=9, leading=13,
fontName="Helvetica-Bold", textColor=C_NEG_TEXT)
sTip = style("sTip", fontSize=9, leading=13,
fontName="Helvetica-Oblique", textColor=colors.HexColor("#e65100"),
leftIndent=8)
sMnem = style("sMnem", fontSize=9, leading=13,
fontName="Helvetica-Bold", textColor=C_MNEM_TEXT,
leftIndent=8)
sBody = style("sBody", fontSize=9, leading=13,
fontName="Helvetica", textColor=C_BLACK)
sFooter = style("sFooter", fontSize=7.5, textColor=colors.grey,
alignment=TA_CENTER, fontName="Helvetica-Oblique")
# ── Helper: section header ───────────────────────────────────────────────────
def section_header(num, title, color=C_HEADER):
data = [[Paragraph(f"<b>{num}. {title}</b>", sSectionHead)]]
t = Table(data, colWidths=[W])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), color),
("ROWPADDING", (0,0), (-1,-1), 5),
("TOPPADDING", (0,0), (-1,-1), 6),
("BOTTOMPADDING", (0,0), (-1,-1), 6),
("ROUNDEDCORNERS", [4,4,4,4]),
]))
return t
def tip_box(text, bg=C_ACCENT_BG, text_color="#e65100"):
tip_style = style(f"tip_{hash(text)}", fontSize=9, leading=13,
fontName="Helvetica-Oblique",
textColor=colors.HexColor(text_color))
data = [[Paragraph(f"★ {text}", tip_style)]]
t = Table(data, colWidths=[W])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), colors.HexColor(bg) if isinstance(bg, str) else bg),
("ROWPADDING", (0,0), (-1,-1), 4),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("BOX", (0,0), (-1,-1), 0.5, colors.HexColor("#f57f17")),
("ROUNDEDCORNERS", [3,3,3,3]),
]))
return t
def mnem_box(text):
data = [[Paragraph(f"🔑 {text}", sMnem)]]
t = Table(data, colWidths=[W])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), C_MNEM_BG),
("ROWPADDING", (0,0), (-1,-1), 4),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("BOX", (0,0), (-1,-1), 0.5, colors.HexColor("#1565c0")),
]))
return t
def main_table(headers, rows, col_widths, pos_col=None, neg_col=None):
"""Build a styled data table."""
# header row
header_row = [Paragraph(f"<b>{h}</b>",
style(f"th_{i}", fontSize=9, fontName="Helvetica-Bold",
textColor=C_WHITE, alignment=TA_CENTER))
for i, h in enumerate(headers)]
data = [header_row]
for row in rows:
formatted = []
for ci, cell in enumerate(row):
if ci == 0:
formatted.append(Paragraph(str(cell), sCellBold))
elif pos_col and ci == pos_col:
formatted.append(Paragraph(str(cell), sCellPos))
elif neg_col and ci == neg_col:
formatted.append(Paragraph(str(cell), sCellNeg))
else:
formatted.append(Paragraph(str(cell), sCell))
data.append(formatted)
t = Table(data, colWidths=col_widths, repeatRows=1)
style_cmds = [
("BACKGROUND", (0,0), (-1,0), C_TABLE_HEAD),
("TEXTCOLOR", (0,0), (-1,0), C_WHITE),
("ALIGN", (0,0), (-1,-1), "LEFT"),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("ROWPADDING", (0,0), (-1,-1), 4),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("GRID", (0,0), (-1,-1), 0.4, colors.HexColor("#c5cae9")),
("ROWBACKGROUNDS", (0,1), (-1,-1), [C_WHITE, C_TABLE_ALT]),
]
t.setStyle(TableStyle(style_cmds))
return t
# ═══════════════════════════════════════════════════════════════════════════
# BUILD CONTENT
# ═══════════════════════════════════════════════════════════════════════════
story = []
sp = lambda n=4: Spacer(1, n)
# ── Cover banner ─────────────────────────────────────────────────────────────
banner_data = [[
Paragraph("HIGH-YIELD BIOCHEMICAL TESTS", sTitle),
Paragraph("NEET PG | Quick Revision Flowchart | Microbiology", sSubtitle),
]]
banner = Table([[Paragraph("HIGH-YIELD BIOCHEMICAL TESTS", sTitle)],
[Paragraph("NEET PG · Quick Revision Flowchart · Microbiology", sSubtitle)]],
colWidths=[W])
banner.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), C_HEADER_BG),
("ROWPADDING", (0,0), (-1,-1), 6),
("BOX", (0,0), (-1,-1), 1.5, C_BOX_BORDER),
("TOPPADDING", (0,0), (0,0), 10),
("BOTTOMPADDING", (0,-1), (-1,-1), 10),
]))
story.append(banner)
story.append(sp(8))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 1 – CATALASE
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header(1, "CATALASE TEST (H₂O₂ → H₂O + O₂) | Bubbles = Positive"))
story.append(sp(3))
story.append(main_table(
["Result", "Organisms", "Key Notes"],
[
["POSITIVE ✓",
"Staphylococcus spp., Bacillus, Listeria, Nocardia,\nAspergillus, most Enterobacteriaceae",
"Gram-positive cocci: separates Staph from Strep"],
["NEGATIVE ✗",
"All Streptococci, Enterococcus,\nClostridium, Bacteroides",
"Gram-positive cocci: all Strep are catalase –"],
],
col_widths=[W*0.18, W*0.48, W*0.34],
pos_col=0
))
story.append(sp(3))
story.append(tip_box("NEET CLASSIC: Catalase POSITIVE = Staph | Catalase NEGATIVE = Strep (most tested distinction!)"))
story.append(sp(6))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 2 – COAGULASE
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header(2, "COAGULASE TEST (Fibrinogen → Fibrin) | Done after Catalase +ve confirmed"))
story.append(sp(3))
story.append(main_table(
["Result", "Organism", "Clinical Importance"],
[
["POSITIVE ✓", "Staphylococcus aureus", "Major pathogen – skin, SSTI, endocarditis, osteomyelitis, TSS"],
["NEGATIVE ✗", "S. epidermidis, S. saprophyticus (CoNS)", "S. saprophyticus = UTI in young women"],
["SPECIAL ⚠", "S. lugdunensis", "Slide coagulase + but tube coagulase –; mimics S. aureus clinically!"],
],
col_widths=[W*0.18, W*0.32, W*0.50],
pos_col=0
))
story.append(sp(3))
story.append(tip_box("S. lugdunensis: slide coagulase POSITIVE, tube coagulase NEGATIVE — favourite MCQ trap!",
bg=C_SPEC_BG, text_color=C_SPEC_TEXT.hexval().lstrip('#')))
story.append(sp(6))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 3 – OXIDASE
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header(3, "OXIDASE TEST (Cytochrome C oxidase) | Positive = Purple/Blue color", color=colors.HexColor("#1a237e")))
story.append(sp(3))
story.append(main_table(
["Result", "Organisms", "Trick"],
[
["POSITIVE ✓",
"Pseudomonas, Neisseria, Vibrio, Campylobacter,\nHelicobacter, Moraxella, Aeromonas, Brucella, Pasteurella",
"None of these are Enterobacteriaceae"],
["NEGATIVE ✗",
"ALL Enterobacteriaceae: E. coli, Klebsiella,\nSalmonella, Shigella, Proteus, Serratia, Enterobacter",
"Oxidase –ve = Enterobacteriaceae (key rule!)"],
],
col_widths=[W*0.18, W*0.52, W*0.30],
pos_col=0
))
story.append(sp(3))
story.append(mnem_box("Mnemonic: 'Pee Never Very Carefully Haemorrhaging' → Pseudomonas, Neisseria, Vibrio, Campylobacter, Helicobacter/Haemophilus"))
story.append(sp(6))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 4 – UREASE
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header(4, "UREASE TEST (Urea → NH₃ + CO₂) | Positive = Pink/Red (alkaline pH)", color=colors.HexColor("#4a148c")))
story.append(sp(3))
story.append(main_table(
["Speed", "Organisms", "Clinical Relevance"],
[
["Rapid+ (< 4 h)",
"Proteus spp., H. pylori, Ureaplasma urealyticum,\nCryptococcus neoformans",
"H. pylori → CLO test, Urea Breath Test (UBT);\nProteus → struvite kidney stones"],
["Slow/Weak+",
"Klebsiella, Pseudomonas, Brucella,\nYersinia enterocolitica, Helicobacter",
"Klebsiella – weakly positive"],
["NEGATIVE ✗",
"E. coli, Salmonella, Shigella, Streptococci",
"Helps distinguish from Proteus/Klebsiella"],
],
col_widths=[W*0.22, W*0.42, W*0.36],
))
story.append(sp(3))
story.append(mnem_box("Mnemonic: 'Proteus Helps Kill Certain Bacteria Yearly' → Proteus, H. pylori, Klebsiella, Cryptococcus, Brucella, Yersinia"))
story.append(sp(3))
story.append(tip_box("CLO test (campylobacter-like organism test) = rapid urease test on gastric biopsy → yellow→pink/magenta = H. pylori +ve"))
story.append(sp(6))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 5 – INDOLE
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header(5, "INDOLE TEST (Tryptophan → Indole) | Red color with Kovacs reagent = Positive", color=colors.HexColor("#01579b")))
story.append(sp(3))
story.append(main_table(
["Result", "Organisms", "Notes"],
[
["POSITIVE ✓",
"E. coli, Proteus vulgaris, Vibrio cholerae, Klebsiella oxytoca",
"E. coli – used for quick ID in urine culture"],
["NEGATIVE ✗",
"Proteus mirabilis, K. pneumoniae, Salmonella, most Shigella",
"P. mirabilis vs P. vulgaris – classic MCQ pair"],
],
col_widths=[W*0.18, W*0.47, W*0.35],
pos_col=0
))
story.append(sp(3))
story.append(tip_box("P. vulgaris = Indole POSITIVE | P. mirabilis = Indole NEGATIVE (high-yield pair!)"))
story.append(sp(6))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 6 – IMViC TABLE
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header(6, "IMViC TESTS (Indole / Methyl Red / Voges-Proskauer / Citrate)", color=colors.HexColor("#1b5e20")))
story.append(sp(3))
imvic_data = [
[Paragraph("<b>Organism</b>", sCellBold),
Paragraph("<b>Indole (I)</b>", sCellBold),
Paragraph("<b>Methyl Red (M)</b>", sCellBold),
Paragraph("<b>Voges-Proskauer (V)</b>", sCellBold),
Paragraph("<b>Citrate (C)</b>", sCellBold)],
[Paragraph("E. coli", sCell),
Paragraph("+", sCellPos), Paragraph("+", sCellPos),
Paragraph("−", sCellNeg), Paragraph("−", sCellNeg)],
[Paragraph("Klebsiella / Enterobacter", sCell),
Paragraph("−", sCellNeg), Paragraph("−", sCellNeg),
Paragraph("+", sCellPos), Paragraph("+", sCellPos)],
[Paragraph("Salmonella", sCell),
Paragraph("−", sCellNeg), Paragraph("+", sCellPos),
Paragraph("−", sCellNeg), Paragraph("+ (not Typhi)", sCell)],
[Paragraph("Shigella", sCell),
Paragraph("−", sCellNeg), Paragraph("+", sCellPos),
Paragraph("−", sCellNeg), Paragraph("−", sCellNeg)],
[Paragraph("Proteus mirabilis", sCell),
Paragraph("−", sCellNeg), Paragraph("+", sCellPos),
Paragraph("−", sCellNeg), Paragraph("+", sCellPos)],
[Paragraph("Proteus vulgaris", sCell),
Paragraph("+", sCellPos), Paragraph("+", sCellPos),
Paragraph("−", sCellNeg), Paragraph("−", sCellNeg)],
]
imvic_t = Table(imvic_data, colWidths=[W*0.32, W*0.17, W*0.17, W*0.17, W*0.17])
imvic_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), C_TABLE_HEAD),
("TEXTCOLOR", (0,0), (-1,0), C_WHITE),
("ALIGN", (1,0), (-1,-1), "CENTER"),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("ROWPADDING", (0,0), (-1,-1), 4),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("GRID", (0,0), (-1,-1), 0.4, colors.HexColor("#c5cae9")),
("ROWBACKGROUNDS", (0,1), (-1,-1), [C_WHITE, C_TABLE_ALT]),
]))
story.append(imvic_t)
story.append(sp(3))
story.append(tip_box("E. coli = + + − − | Klebsiella/Enterobacter = − − + + (mirror image!)"))
story.append(sp(6))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 7 – CITRATE
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header(7, "CITRATE UTILIZATION (Simmons) | Blue = Positive (citrate = sole carbon source)", color=colors.HexColor("#006064")))
story.append(sp(3))
story.append(main_table(
["Result", "Organisms"],
[
["POSITIVE ✓", "Klebsiella, Enterobacter, Citrobacter, Serratia, Proteus mirabilis, Salmonella (except Typhi)"],
["NEGATIVE ✗", "E. coli, Salmonella Typhi, Shigella, Proteus vulgaris"],
],
col_widths=[W*0.18, W*0.82],
pos_col=0
))
story.append(sp(6))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 8 – H2S
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header(8, "H₂S PRODUCTION | Black precipitate on TSI / SIM agar = Positive", color=colors.HexColor("#37474f")))
story.append(sp(3))
story.append(main_table(
["Result", "Organisms", "Note"],
[
["POSITIVE ✓", "Salmonella, Proteus, Citrobacter, Francisella (weak)", "Key in TSI agar identification"],
["NEGATIVE ✗", "Shigella, E. coli, Klebsiella", "Shigella vs Salmonella: Salmonella H₂S+, Shigella H₂S−"],
],
col_widths=[W*0.18, W*0.45, W*0.37],
pos_col=0
))
story.append(sp(6))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 9 – GRAM-POSITIVE SPECIAL TESTS
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header(9, "GRAM-POSITIVE IDENTIFICATION TESTS", color=colors.HexColor("#880e4f")))
story.append(sp(3))
gp_data = [
[Paragraph("<b>Test</b>", sCellBold),
Paragraph("<b>Sensitive / Positive</b>", sCellBold),
Paragraph("<b>Resistant / Negative</b>", sCellBold),
Paragraph("<b>Clinical Use</b>", sCellBold)],
[Paragraph("Optochin", sCell),
Paragraph("S. pneumoniae ✓", sCellPos),
Paragraph("Viridans Streptococci ✗", sCellNeg),
Paragraph("Distinguishes S. pneumoniae from Viridans strep", sCell)],
[Paragraph("Bile Solubility", sCell),
Paragraph("S. pneumoniae ✓", sCellPos),
Paragraph("Viridans Streptococci ✗", sCellNeg),
Paragraph("Confirmatory for S. pneumoniae", sCell)],
[Paragraph("Bacitracin", sCell),
Paragraph("Group A Strep (S. pyogenes) ✓", sCellPos),
Paragraph("Group B, C, G Strep ✗", sCellNeg),
Paragraph("ID of S. pyogenes (Group A)", sCell)],
[Paragraph("PYR Test", sCell),
Paragraph("S. pyogenes (Grp A), Enterococcus ✓", sCellPos),
Paragraph("Group B, C, G Strep ✗", sCellNeg),
Paragraph("More specific than Bacitracin", sCell)],
[Paragraph("CAMP Test", sCell),
Paragraph("S. agalactiae (Grp B), Listeria ✓", sCellPos),
Paragraph("Other Strep ✗", sCellNeg),
Paragraph("Listeria: arrowhead pointing TOWARDS streak", sCell)],
[Paragraph("Novobiocin", sCell),
Paragraph("S. epidermidis (Sensitive) ✓", sCellPos),
Paragraph("S. saprophyticus (Resistant) ✗", sCellNeg),
Paragraph("Young female UTI → S. saprophyticus (resistant)", sCell)],
[Paragraph("6.5% NaCl growth", sCell),
Paragraph("Enterococcus ✓", sCellPos),
Paragraph("Streptococcus ✗", sCellNeg),
Paragraph("Distinguishes Enterococcus from Strep", sCell)],
]
gp_t = Table(gp_data, colWidths=[W*0.18, W*0.26, W*0.25, W*0.31])
gp_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), C_TABLE_HEAD),
("TEXTCOLOR", (0,0), (-1,0), C_WHITE),
("ALIGN", (0,0), (-1,-1), "LEFT"),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("ROWPADDING", (0,0), (-1,-1), 4),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("GRID", (0,0), (-1,-1), 0.4, colors.HexColor("#f8bbd0")),
("ROWBACKGROUNDS", (0,1), (-1,-1), [C_WHITE, C_SPEC_BG]),
]))
story.append(gp_t)
story.append(sp(3))
story.append(tip_box("S. saprophyticus = Novobiocin RESISTANT — young female with uncomplicated UTI (classic NEET scenario!)"))
story.append(sp(6))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 10 – TSI AGAR PATTERNS
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header(10, "TRIPLE SUGAR IRON (TSI) AGAR PATTERNS", color=colors.HexColor("#1a237e")))
story.append(sp(3))
tsi_data = [
[Paragraph("<b>Organism</b>", sCellBold),
Paragraph("<b>Slant</b>", sCellBold),
Paragraph("<b>Butt</b>", sCellBold),
Paragraph("<b>Gas</b>", sCellBold),
Paragraph("<b>H₂S</b>", sCellBold)],
[Paragraph("E. coli", sCell),
Paragraph("A (yellow)", sCellPos),
Paragraph("A (yellow)", sCellPos),
Paragraph("+", sCellPos),
Paragraph("−", sCellNeg)],
[Paragraph("Salmonella", sCell),
Paragraph("K (red/alk)", sCellNeg),
Paragraph("A (yellow)", sCellPos),
Paragraph("+", sCellPos),
Paragraph("+", sCellPos)],
[Paragraph("Shigella", sCell),
Paragraph("K (red/alk)", sCellNeg),
Paragraph("A (yellow)", sCellPos),
Paragraph("−", sCellNeg),
Paragraph("−", sCellNeg)],
[Paragraph("Klebsiella", sCell),
Paragraph("A (yellow)", sCellPos),
Paragraph("A (yellow)", sCellPos),
Paragraph("++", sCellPos),
Paragraph("−", sCellNeg)],
[Paragraph("Pseudomonas", sCell),
Paragraph("K (alk)", sCellNeg),
Paragraph("K (alk)", sCellNeg),
Paragraph("−", sCellNeg),
Paragraph("−", sCellNeg)],
[Paragraph("Proteus", sCell),
Paragraph("K (red/alk)", sCellNeg),
Paragraph("A (yellow)", sCellPos),
Paragraph("+", sCellPos),
Paragraph("+", sCellPos)],
]
tsi_t = Table(tsi_data, colWidths=[W*0.30, W*0.20, W*0.20, W*0.15, W*0.15])
tsi_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), C_TABLE_HEAD),
("TEXTCOLOR", (0,0), (-1,0), C_WHITE),
("ALIGN", (1,0), (-1,-1), "CENTER"),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("ROWPADDING", (0,0), (-1,-1), 4),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("GRID", (0,0), (-1,-1), 0.4, colors.HexColor("#c5cae9")),
("ROWBACKGROUNDS", (0,1), (-1,-1), [C_WHITE, C_TABLE_ALT]),
]))
story.append(tsi_t)
story.append(sp(3))
story.append(tip_box("A = Acid (yellow) | K = Alkaline (red/pink) | Salmonella & Proteus: K/A + H₂S (classic!)"))
story.append(sp(6))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 11 – QUICK SUMMARY FLOWCHART (Text-based)
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header(11, "MASTER IDENTIFICATION FLOWCHART (Text)", color=colors.HexColor("#212121")))
story.append(sp(3))
flowchart_steps = [
("GRAM STAIN", "First step always", "#1a237e", C_WHITE),
("GRAM +ve COCCI", "→ CATALASE TEST", "#4caf50", C_WHITE),
("Catalase POSITIVE", "→ Staphylococcus → COAGULASE TEST:\n Coagulase + = S. aureus\n Coagulase − = CoNS (S. epidermidis, etc.)\n Novobiocin test for CoNS: Resistant = S. saprophyticus", "#388e3c", C_WHITE),
("Catalase NEGATIVE", "→ Streptococcus / Enterococcus → HEMOLYSIS + OPTOCHIN/BACITRACIN:\n β-hemolytic + Bacitracin S = S. pyogenes (Grp A)\n β-hemolytic + CAMP+ = S. agalactiae (Grp B)\n α-hemolytic + Optochin S = S. pneumoniae\n α-hemolytic + Optochin R = Viridans Strep\n 6.5% NaCl growth = Enterococcus", "#c62828", C_WHITE),
("GRAM −ve RODS", "→ OXIDASE TEST", "#1565c0", C_WHITE),
("Oxidase NEGATIVE", "→ Enterobacteriaceae → IMViC + TSI + Urease + H₂S\n E. coli (I+ M+ V− C−, Urease−)\n Klebsiella (I− M− V+ C+, Urease+)\n Salmonella (I− M+ V− C+, H₂S+)\n Shigella (I− M+ V− C−, H₂S−)\n Proteus mirabilis (I− Urease++, H₂S+, Swarming)\n Proteus vulgaris (I+ Urease+)", "#0d47a1", C_WHITE),
("Oxidase POSITIVE", "→ Non-Enterobacteriaceae GNR:\n Pseudomonas (non-fermenter, green pigment, grape odor)\n Vibrio (curved rod, NaCl requirement)\n Campylobacter (microaerophilic, comma-shaped)\n Neisseria (diplococcus, not rod)", "#6a1b9a", C_WHITE),
]
for step, detail, bg_hex, txt_color in flowchart_steps:
step_style = style(f"fs_{step[:5]}", fontSize=10, fontName="Helvetica-Bold",
textColor=txt_color if txt_color == C_WHITE else C_BLACK)
detail_style = style(f"fd_{step[:5]}", fontSize=8.5, fontName="Helvetica",
textColor=colors.HexColor("#37474f"), leading=13)
row_data = [[Paragraph(f"<b>{step}</b>", step_style),
Paragraph(detail, detail_style)]]
t = Table(row_data, colWidths=[W*0.26, W*0.74])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (0,-1), colors.HexColor(bg_hex)),
("BACKGROUND", (1,0), (1,-1), colors.HexColor("#f5f5f5")),
("VALIGN", (0,0), (-1,-1), "TOP"),
("ROWPADDING", (0,0), (-1,-1), 5),
("TOPPADDING", (0,0), (-1,-1), 6),
("BOTTOMPADDING", (0,0), (-1,-1), 6),
("BOX", (0,0), (-1,-1), 0.5, colors.HexColor("#bdbdbd")),
("GRID", (0,0), (-1,-1), 0.3, colors.HexColor("#e0e0e0")),
]))
story.append(t)
story.append(sp(2))
story.append(sp(6))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 12 – MNEMONICS BOX
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header(12, "HIGH-YIELD MNEMONICS AT A GLANCE", color=colors.HexColor("#e65100")))
story.append(sp(3))
mnem_rows = [
("Urease+ organisms",
"Proteus Helps Kill Certain Bacteria Yearly\n→ Proteus, H. pylori, Klebsiella, Cryptococcus, Brucella, Yersinia"),
("Oxidase+ gram-negatives",
"Pee Never Very Carefully Haemorrhaging\n→ Pseudomonas, Neisseria, Vibrio, Campylobacter, Helicobacter"),
("Catalase negative gram+",
"All Streptococci + Enterococcus = Catalase NEGATIVE"),
("Coagulase+",
"Only S. aureus (among clinically relevant Staphylococci)"),
("S. saprophyticus",
"Young female + uncomplicated UTI + Novobiocin RESISTANT = S. saprophyticus"),
("Optochin rule",
"S. pneumoniae = Optochin SENSITIVE = 'PnOSitive for Optochin'"),
("Indole pair",
"P. vulgaris = Indole + vs P. mirabilis = Indole −"),
("IMViC mirror",
"E. coli = + + − − vs Klebsiella = − − + + (complete mirror)"),
]
m_data = [[Paragraph("<b>Topic</b>", sCellBold), Paragraph("<b>Mnemonic / Rule</b>", sCellBold)]]
for topic, mnem in mnem_rows:
m_data.append([Paragraph(topic, sCellBold), Paragraph(mnem, sCell)])
m_t = Table(m_data, colWidths=[W*0.28, W*0.72])
m_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), colors.HexColor("#bf360c")),
("TEXTCOLOR", (0,0), (-1,0), C_WHITE),
("ALIGN", (0,0), (-1,-1), "LEFT"),
("VALIGN", (0,0), (-1,-1), "TOP"),
("ROWPADDING", (0,0), (-1,-1), 4),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("GRID", (0,0), (-1,-1), 0.4, colors.HexColor("#ffccbc")),
("ROWBACKGROUNDS", (0,1), (-1,-1), [C_WHITE, colors.HexColor("#fff3e0")]),
]))
story.append(m_t)
story.append(sp(8))
# ════════════════════════════════════════════════════════════════════════════
# FOOTER
# ════════════════════════════════════════════════════════════════════════════
story.append(HRFlowable(width=W, thickness=0.5, color=colors.HexColor("#9fa8da")))
story.append(sp(3))
story.append(Paragraph(
"High-Yield Biochemical Tests – NEET PG Quick Revision | Sources: Jawetz Medical Microbiology 28E, Sherris & Ryan 8E, Quick Compendium of Clinical Pathology 5E",
sFooter
))
# ════════════════════════════════════════════════════════════════════════════
# BUILD PDF
# ════════════════════════════════════════════════════════════════════════════
doc.build(story)
print(f"PDF created: {OUTPUT}")