For 1st Year MBBS Biochemistry Practical, these are the most important practical topics that are commonly asked in university exams and viva. 1. Urine Analysis (Very Important ⭐⭐⭐⭐⭐) Physical examination of urine Colour Appearance Odour Specific gravity pH Chemical tests Protein (Heat & Acetic acid test) Glucose (Benedict's test) Ketone bodies (Rothera's test) Bile salts (Hay's sulphur test) Bile pigments (Fouchet's/Gmelin's test) Blood (Benzidine/Orthotolidine test) 2. Qualitative Analysis of Carbohydrates ⭐⭐⭐⭐⭐ Know the principle, reagent, procedure, observation and inference of: Molisch test Benedict test Barfoed test Seliwanoff test Bial's test Iodine test Osazone test 3. Qualitative Analysis of Proteins ⭐⭐⭐⭐⭐ Biuret test Ninhydrin test Xanthoproteic test Millon's test Hopkins-Cole test Sulphur test Sakaguchi test 4. Colorimetry ⭐⭐⭐⭐ Principle of colorimetry Beer-Lambert's law Parts of colorimeter Uses Sources of error 5. Instruments ⭐⭐⭐⭐ Colorimeter Centrifuge Micropipette Spectrophotometer (basic) Water bath 6. Blood Glucose Estimation ⭐⭐⭐⭐ GOD-POD method Principle Procedure Normal values Clinical significance 7. Liver Function Tests (LFT) ⭐⭐⭐⭐ Parameters Normal values Clinical significance 8. Kidney Function Tests (KFT) ⭐⭐⭐⭐ Serum urea Serum creatinine Uric acid Clinical importance 9. Lipid Profile ⭐⭐⭐ Total cholesterol Triglycerides HDL VLDL
Generate a PDF quick-reference card for exam day
ls /home/daytona/workspace/ 2>/dev/null && echo "---" && python3 -c "import reportlab; print('reportlab ok')" && python3 -c "from reportlab.platypus import SimpleDocTemplate; print('platypus ok')"
mkdir -p /home/daytona/workspace/biochem-refcard
~/biochem-refcard/build_refcard.py
"""
1st Year MBBS Biochemistry Practical - Quick Reference Card (PDF)
A4 landscape, 2 columns, dense but readable layout
"""
from reportlab.lib.pagesizes import A4, landscape
from reportlab.lib.units import mm, cm
from reportlab.lib import colors
from reportlab.lib.styles import ParagraphStyle, getSampleStyleSheet
from reportlab.platypus import (
BaseDocTemplate, PageTemplate, Frame, Paragraph, Table, TableStyle,
Spacer, KeepTogether, HRFlowable
)
from reportlab.lib.enums import TA_CENTER, TA_LEFT
# ── Colour palette ──────────────────────────────────────────────────────────
DARK_BLUE = colors.HexColor("#1a3a5c")
MED_BLUE = colors.HexColor("#2563ab")
LIGHT_BLUE = colors.HexColor("#dbeafe")
ACCENT_GREEN= colors.HexColor("#166534")
LIGHT_GREEN = colors.HexColor("#dcfce7")
ACCENT_RED = colors.HexColor("#991b1b")
LIGHT_RED = colors.HexColor("#fee2e2")
ACCENT_PURPLE=colors.HexColor("#5b21b6")
LIGHT_PURPLE= colors.HexColor("#ede9fe")
ORANGE = colors.HexColor("#92400e")
LIGHT_ORANGE= colors.HexColor("#fef3c7")
GRAY_LIGHT = colors.HexColor("#f1f5f9")
GRAY_MID = colors.HexColor("#cbd5e1")
GRAY_DARK = colors.HexColor("#334155")
WHITE = colors.white
BLACK = colors.black
# ── Page setup ───────────────────────────────────────────────────────────────
PAGE_W, PAGE_H = landscape(A4)
MARGIN = 8*mm
COL_GAP = 5*mm
COL_W = (PAGE_W - 2*MARGIN - COL_GAP) / 2
HEADER_H = 14*mm
# ── Styles ───────────────────────────────────────────────────────────────────
def make_styles():
s = {}
base = dict(fontName="Helvetica", leading=9)
s["section_title"] = ParagraphStyle(
"section_title", fontSize=7, fontName="Helvetica-Bold",
textColor=WHITE, leading=9, spaceAfter=0, spaceBefore=0
)
s["col_header"] = ParagraphStyle(
"col_header", fontSize=5.5, fontName="Helvetica-Bold",
textColor=DARK_BLUE, leading=7
)
s["cell"] = ParagraphStyle(
"cell", fontSize=5.5, fontName="Helvetica",
textColor=GRAY_DARK, leading=7
)
s["cell_bold"] = ParagraphStyle(
"cell_bold", fontSize=5.5, fontName="Helvetica-Bold",
textColor=DARK_BLUE, leading=7
)
s["cell_red"] = ParagraphStyle(
"cell_red", fontSize=5.5, fontName="Helvetica-Bold",
textColor=ACCENT_RED, leading=7
)
s["cell_green"] = ParagraphStyle(
"cell_green", fontSize=5.5, fontName="Helvetica-Bold",
textColor=ACCENT_GREEN, leading=7
)
s["viva_q"] = ParagraphStyle(
"viva_q", fontSize=5.5, fontName="Helvetica-Bold",
textColor=DARK_BLUE, leading=7.5
)
s["viva_a"] = ParagraphStyle(
"viva_a", fontSize=5.5, fontName="Helvetica",
textColor=GRAY_DARK, leading=7.5
)
s["formula"] = ParagraphStyle(
"formula", fontSize=6.5, fontName="Helvetica-Bold",
textColor=ACCENT_PURPLE, leading=9, alignment=TA_CENTER
)
s["tiny"] = ParagraphStyle(
"tiny", fontSize=4.8, fontName="Helvetica",
textColor=GRAY_DARK, leading=6.5
)
return s
ST = make_styles()
# ── Helper builders ───────────────────────────────────────────────────────────
def section_header(title, bg_color=DARK_BLUE, star=""):
label = f"{title} {star}" if star else title
tbl = Table([[Paragraph(label, ST["section_title"])]],
colWidths=[COL_W], rowHeights=[9*mm])
tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), bg_color),
("ROUNDEDCORNERS", [3]),
("LEFTPADDING", (0,0), (-1,-1), 4),
("TOPPADDING", (0,0), (-1,-1), 1),
("BOTTOMPADDING", (0,0), (-1,-1), 1),
]))
return tbl
def data_table(headers, rows, col_widths, accent_col=MED_BLUE, row_colors=None):
"""Build a compact styled table."""
p = lambda txt, style=ST["cell"]: Paragraph(str(txt), style)
pb = lambda txt: Paragraph(str(txt), ST["cell_bold"])
header_row = [pb(h) for h in headers]
data_rows = [[p(c) for c in row] for row in rows]
all_rows = [header_row] + data_rows
style_cmds = [
("BACKGROUND", (0,0), (-1,0), accent_col),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 5.5),
("LEADING", (0,0), (-1,-1), 7),
("LEFTPADDING",(0,0), (-1,-1), 3),
("RIGHTPADDING",(0,0),(-1,-1), 2),
("TOPPADDING", (0,0), (-1,-1), 1.5),
("BOTTOMPADDING",(0,0),(-1,-1), 1.5),
("GRID", (0,0), (-1,-1), 0.3, GRAY_MID),
("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, GRAY_LIGHT]),
]
if row_colors:
for row_idx, bg in row_colors:
style_cmds.append(("BACKGROUND", (0,row_idx), (-1,row_idx), bg))
tbl = Table(all_rows, colWidths=col_widths)
tbl.setStyle(TableStyle(style_cmds))
return tbl
def sp(h=2): return Spacer(1, h*mm)
# ── Content builders (return list of flowables) ────────────────────────────
def build_urine_analysis():
items = []
items.append(section_header("1. URINE ANALYSIS", DARK_BLUE, "★★★★★"))
items.append(sp(1))
# Physical
phys_hdr = Paragraph("PHYSICAL EXAMINATION", ST["col_header"])
phys_tbl = data_table(
["Parameter", "Normal", "Abnormal / Significance"],
[
["Colour", "Pale–amber (urochrome)", "Dark=dehydration; Red=hematuria; Green=bile pigments"],
["Appearance", "Clear", "Turbid=pus/bacteria/phosphates"],
["Odour", "Faint aromatic", "Fruity=ketones(DM); Ammoniacal=UTI decomposition"],
["Sp. Gravity", "1.003–1.030", "Low=diabetes insipidus; High=DM, dehydration"],
["pH", "4.5–8.0 (avg 6.0)", "Acidic=DM/fever; Alkaline=UTI/vegetarians"],
],
[22*mm, 30*mm, COL_W-52*mm-4],
accent_col=MED_BLUE
)
items += [phys_hdr, sp(0.5), phys_tbl, sp(1.5)]
# Chemical
chem_hdr = Paragraph("CHEMICAL TESTS", ST["col_header"])
chem_tbl = data_table(
["Test", "Reagent", "Positive", "Significance"],
[
["Protein\n(Heat+AcOH)", "Heat + 1% acetic acid", "White ppt persists after acid", "Proteinuria – nephrotic sy., GN"],
["Glucose\n(Benedict's)", "Benedict's + heat", "Green→Yellow→Orange→Red ppt", "Glycosuria – DM, renal glycosuria"],
["Ketones\n(Rothera's)", "(NH₄)₂SO₄ + Na-nitroprusside + NH₃", "Purple/violet ring", "Ketonuria – DM, starvation"],
["Bile Salts\n(Hay's)", "Sprinkle sulphur powder", "Sulphur sinks", "Obstructive jaundice (↓surface tension)"],
["Bile Pigments\n(Fouchet's)", "BaCl₂ + Fouchet's reagent", "Green colour", "Jaundice – bilirubin in urine"],
["Blood\n(Orthotolidine)", "H₂O₂ + orthotolidine", "Blue/green colour", "Hematuria, hemoglobinuria"],
],
[20*mm, 36*mm, 22*mm, COL_W-78*mm-4],
accent_col=ACCENT_GREEN
)
items += [chem_hdr, sp(0.5), chem_tbl]
return items
def build_carbohydrates():
items = []
items.append(section_header("2. QUALITATIVE CARBOHYDRATE TESTS", MED_BLUE, "★★★★★"))
items.append(sp(1))
tbl = data_table(
["Test", "Reagent", "Principle", "+ve Result", "Detects"],
[
["Molisch", "α-naphthol + conc H₂SO₄", "Dehydration→furfural+α-naphthol", "Purple ring at interface", "ALL carbohydrates"],
["Benedict's", "CuSO₄+Na-citrate+Na₂CO₃", "Reducing sugars→Cu²⁺→Cu⁺(Cu₂O)", "Green/Yellow/Orange/Red ppt", "All reducing sugars"],
["Barfoed's", "Cu-acetate in acetic acid (acidic)", "Monosaccharides reduce Cu²⁺ faster", "Red ppt within 5 min", "Monosaccharides only"],
["Seliwanoff's", "Resorcinol + conc HCl", "Ketoses dehydrate faster than aldoses", "Cherry red <1 min", "Ketoses (fructose)"],
["Bial's", "Orcinol+FeCl₃+conc HCl", "Pentose→furfural+orcinol", "Blue-green colour", "Pentoses (ribose, arabinose)"],
["Iodine", "I₂/KI solution", "Iodine enters starch helical coils", "Blue-black", "Starch; Glycogen=reddish-brown"],
["Osazone", "Phenylhydrazine+NaOAc+AcOH", "Reducing sugar→osazone crystals", "Yellow crystals", "Glucose/Fructose/Lactose/Maltose"],
],
[18*mm, 33*mm, 35*mm, 24*mm, COL_W-110*mm-4],
accent_col=MED_BLUE
)
items.append(tbl)
items.append(sp(1))
# Crystal shapes box
crystal_hdr = Paragraph("OSAZONE CRYSTAL SHAPES (★ Viva favourite)", ST["col_header"])
crystal_tbl = data_table(
["Sugar", "Crystal Shape", "Notes"],
[
["Glucose / Fructose / Mannose", "Needle / sunflower / broomstick", "All three give SAME shape"],
["Lactose", "Mushroom / powder-puff", "Disc-shaped clumps"],
["Maltose", "Star / sea-urchin", "Spiky radiating needles"],
["Sucrose", "NO osazone", "Non-reducing – no free aldehyde/ketone"],
],
[42*mm, 42*mm, COL_W-84*mm-4],
accent_col=ORANGE,
row_colors=[(4, LIGHT_RED)]
)
items += [crystal_hdr, sp(0.5), crystal_tbl]
return items
def build_proteins():
items = []
items.append(section_header("3. QUALITATIVE PROTEIN TESTS", ACCENT_GREEN, "★★★★★"))
items.append(sp(1))
tbl = data_table(
["Test", "Reagent", "Principle (group detected)", "+ve Result"],
[
["Biuret", "NaOH + dilute CuSO₄", "Cu²⁺ complexes with peptide bonds (≥2 bonds)", "Violet/purple"],
["Ninhydrin", "Triketohydrindene hydrate", "Oxidative deamination of α-amino group", "Purple (Ruhemann's); Proline=YELLOW"],
["Xanthoproteic", "Conc HNO₃ then NH₄OH", "Nitration of aromatic rings (Phe,Tyr,Trp)", "Yellow→Orange on alkalinisation"],
["Millon's", "Hg-sulphate+HNO₃+NaNO₂", "Hg reacts with hydroxyphenyl group (Tyr)", "Brick red ppt"],
["Hopkins-Cole", "Glyoxylic acid+conc H₂SO₄", "Indole ring of Trp condenses with glyoxylate", "Violet ring at interface"],
["Sulphur test", "NaOH + lead acetate", "H₂S released from Cys/Cystine+Pb-acetate", "Black ppt (lead sulphide)"],
["Sakaguchi", "α-naphthol+NaOH+NaOBr", "Guanidinium group of Arginine", "Red/orange colour"],
],
[22*mm, 36*mm, 44*mm, COL_W-102*mm-4],
accent_col=ACCENT_GREEN
)
items.append(tbl)
return items
def build_colorimetry():
items = []
items.append(section_header("4. COLORIMETRY & BEER-LAMBERT'S LAW", ACCENT_PURPLE, "★★★★"))
items.append(sp(1))
# Formula box
formula_tbl = Table(
[[Paragraph("A = ε × c × l", ST["formula"]),
Paragraph("A = Absorbance | ε = Molar extinction coeff. | c = Concentration | l = Path length (1 cm cuvette)", ST["tiny"])]],
colWidths=[38*mm, COL_W-38*mm-4]
)
formula_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), LIGHT_PURPLE),
("LEFTPADDING",(0,0),(-1,-1), 4),
("TOPPADDING",(0,0),(-1,-1), 3),
("BOTTOMPADDING",(0,0),(-1,-1), 3),
("VALIGN",(0,0),(-1,-1),"MIDDLE"),
("BOX",(0,0),(-1,-1),0.5,ACCENT_PURPLE),
]))
items += [formula_tbl, sp(1.5)]
parts_hdr = Paragraph("PARTS OF COLORIMETER", ST["col_header"])
parts_tbl = data_table(
["Part", "Function"],
[
["Light source", "Tungsten bulb (visible light)"],
["Filter", "Selects wavelength complementary to solution colour"],
["Cuvette", "Holds sample (glass/plastic, 1 cm path)"],
["Photodetector", "Converts transmitted light → electrical signal"],
["Galvanometer/Display", "Reads absorbance or % transmittance"],
],
[35*mm, COL_W-35*mm-4],
accent_col=ACCENT_PURPLE
)
items += [parts_hdr, sp(0.5), parts_tbl, sp(1.5)]
err_hdr = Paragraph("SOURCES OF ERROR", ST["col_header"])
errors = ["Stray light | Dirty/scratched cuvette | Wrong wavelength | Solution too concentrated (non-linear) | Air bubbles | Temperature variation | Not zeroed with blank"]
err_box = Table([[Paragraph(errors[0], ST["tiny"])]],
colWidths=[COL_W])
err_box.setStyle(TableStyle([
("BACKGROUND",(0,0),(-1,-1), LIGHT_RED),
("LEFTPADDING",(0,0),(-1,-1),4),("TOPPADDING",(0,0),(-1,-1),2),
("BOTTOMPADDING",(0,0),(-1,-1),2),
("BOX",(0,0),(-1,-1),0.4,ACCENT_RED),
]))
items += [err_hdr, sp(0.5), err_box]
return items
def build_instruments():
items = []
items.append(section_header("5. LABORATORY INSTRUMENTS", colors.HexColor("#0f766e"), "★★★★"))
items.append(sp(1))
tbl = data_table(
["Instrument", "Principle", "Key Points"],
[
["Colorimeter", "Absorbance of complementary wavelength", "Filters; Visible light; Routine assays"],
["Spectrophotometer", "Prism/diffraction grating (continuous)", "UV+Vis+IR; More precise; Research use"],
["Centrifuge", "Centrifugal force separates by density", "Serum separation, urine sediment, cell fractionation"],
["Micropipette", "Air displacement (piston-driven)", "P20/P200/P1000; Use correct tips; Keep vertical when aspirating"],
["Water Bath", "Maintains constant temperature", "37°C = body temp; 56°C = complement inactivation"],
],
[28*mm, 42*mm, COL_W-70*mm-4],
accent_col=colors.HexColor("#0f766e")
)
items.append(tbl)
return items
def build_blood_glucose():
items = []
items.append(section_header("6. BLOOD GLUCOSE – GOD-POD METHOD", colors.HexColor("#b45309"), "★★★★"))
items.append(sp(1))
rxn_hdr = Paragraph("REACTION STEPS", ST["col_header"])
rxn_box = Table([
[Paragraph("Step 1 (GOD): Glucose + O₂ + H₂O → Gluconic acid + H₂O₂", ST["tiny"])],
[Paragraph("Step 2 (POD): H₂O₂ + 4-Aminoantipyrine + Phenol → Quinoneimine dye (PINK) + H₂O", ST["tiny"])],
[Paragraph("Read at 505 nm. Colour intensity ∝ glucose concentration.", ST["tiny"])],
], colWidths=[COL_W])
rxn_box.setStyle(TableStyle([
("BACKGROUND",(0,0),(-1,-1), LIGHT_ORANGE),
("LEFTPADDING",(0,0),(-1,-1),4),("TOPPADDING",(0,0),(-1,-1),1.5),
("BOTTOMPADDING",(0,0),(-1,-1),1.5),
("BOX",(0,0),(-1,-1),0.4, ORANGE),
]))
items += [rxn_hdr, sp(0.5), rxn_box, sp(1.5)]
val_hdr = Paragraph("NORMAL VALUES", ST["col_header"])
val_tbl = data_table(
["State", "Value"],
[
["Fasting blood glucose", "70–100 mg/dL"],
["Post-prandial (2 hr)", "< 140 mg/dL"],
["Random blood glucose", "< 200 mg/dL"],
["Impaired fasting", "100–125 mg/dL"],
["DM diagnosis (fasting)", "≥ 126 mg/dL"],
],
[50*mm, COL_W-50*mm-4],
accent_col=colors.HexColor("#b45309"),
row_colors=[(5, LIGHT_RED)]
)
items += [val_hdr, sp(0.5), val_tbl]
return items
def build_lft():
items = []
items.append(section_header("7. LIVER FUNCTION TESTS (LFT)", colors.HexColor("#7c3aed"), "★★★★"))
items.append(sp(1))
lft_tbl = data_table(
["Parameter", "Normal", "Elevated In"],
[
["Total Bilirubin", "0.2–1.0 mg/dL", "All types of jaundice"],
["Direct (conjugated)", "0–0.3 mg/dL", "Obstructive / hepatic jaundice"],
["Indirect (unconjugated)", "0.1–0.8 mg/dL", "Haemolytic jaundice"],
["SGOT / AST", "10–40 U/L", "Hepatocellular damage, MI"],
["SGPT / ALT", "7–40 U/L", "Viral hepatitis (liver-specific)"],
["Alkaline Phosphatase", "40–125 U/L", "Obstructive jaundice, bone disease"],
["GGT", "10–66 U/L", "Alcoholic liver disease"],
["Total Protein", "6.0–8.0 g/dL", "↓ in liver failure, malnutrition"],
["Albumin", "3.5–5.0 g/dL", "↓ in cirrhosis, nephrotic syndrome"],
["Prothrombin Time", "11–13 sec", "Prolonged in liver disease"],
],
[35*mm, 28*mm, COL_W-63*mm-4],
accent_col=colors.HexColor("#7c3aed")
)
items += [lft_tbl, sp(1.5)]
jaund_hdr = Paragraph("JAUNDICE DIFFERENTIATOR", ST["col_header"])
jaund_tbl = data_table(
["Feature", "Pre-hepatic", "Hepatic", "Post-hepatic"],
[
["Urine bilirubin", "Absent", "Present", "Present"],
["Urine urobilinogen", "Increased", "Variable", "Absent"],
["Stool colour", "Normal/dark", "Pale", "Clay/pale"],
["ALP", "Normal", "Slight ↑", "Markedly ↑"],
],
[28*mm, 24*mm, 22*mm, COL_W-74*mm-4],
accent_col=colors.HexColor("#7c3aed")
)
items += [jaund_hdr, sp(0.5), jaund_tbl]
return items
def build_kft():
items = []
items.append(section_header("8. KIDNEY FUNCTION TESTS (KFT)", colors.HexColor("#0369a1"), "★★★★"))
items.append(sp(1))
kft_tbl = data_table(
["Parameter", "Normal", "Method", "Elevated In"],
[
["Serum Urea", "15–40 mg/dL", "Diacetyl monoxime (pink colour)", "Renal failure, high protein diet, dehydration"],
["Serum Creatinine", "M: 0.7–1.2\nF: 0.5–1.0 mg/dL", "Jaffe's reaction (picric acid, alkaline → orange-red)", "Renal failure (more specific than urea)"],
["Uric Acid", "M: 3.5–7.0\nF: 2.5–6.0 mg/dL", "Uricase / phosphotungstic acid", "Gout, renal failure, leukaemia, diuretics"],
["eGFR", "> 90 mL/min/1.73m²", "CKD-EPI formula (from creatinine)", "Decreased in CKD; <15 = ESRD"],
],
[25*mm, 25*mm, 38*mm, COL_W-88*mm-4],
accent_col=colors.HexColor("#0369a1")
)
items += [kft_tbl, sp(1)]
note_box = Table([[Paragraph(
"BUN:Creatinine ratio Normal = 10:1–20:1 | >20 = Pre-renal (dehydration/GI bleed) | <10 = Liver disease | "
"GFR <60 for >3 months = CKD | GFR <15 = ESRD (dialysis)",
ST["tiny"])]],
colWidths=[COL_W])
note_box.setStyle(TableStyle([
("BACKGROUND",(0,0),(-1,-1), LIGHT_BLUE),
("LEFTPADDING",(0,0),(-1,-1),4),("TOPPADDING",(0,0),(-1,-1),2),
("BOTTOMPADDING",(0,0),(-1,-1),2),
("BOX",(0,0),(-1,-1),0.4, MED_BLUE),
]))
items.append(note_box)
return items
def build_lipid():
items = []
items.append(section_header("9. LIPID PROFILE", colors.HexColor("#be185d"), "★★★"))
items.append(sp(1))
formula_box = Table(
[[Paragraph("Friedewald Formula: LDL = Total Cholesterol − HDL − (Triglycerides ÷ 5) [Valid when TG < 400 mg/dL]",
ST["formula"])]],
colWidths=[COL_W]
)
formula_box.setStyle(TableStyle([
("BACKGROUND",(0,0),(-1,-1), LIGHT_PURPLE),
("LEFTPADDING",(0,0),(-1,-1),4),("TOPPADDING",(0,0),(-1,-1),3),
("BOTTOMPADDING",(0,0),(-1,-1),3),
("BOX",(0,0),(-1,-1),0.5, ACCENT_PURPLE),
]))
items += [formula_box, sp(1)]
lip_tbl = data_table(
["Parameter", "Desirable", "Borderline", "High Risk / Abnormal"],
[
["Total Cholesterol", "< 200 mg/dL", "200–239 mg/dL", "≥ 240 mg/dL"],
["LDL ('bad')", "< 100 mg/dL (optimal)", "130–159 mg/dL", "≥ 160 mg/dL"],
["HDL ('good')", "> 60 mg/dL (protective)", "40–59 mg/dL", "< 40 mg/dL (RISK FACTOR)"],
["Triglycerides", "< 150 mg/dL", "150–199 mg/dL", "≥ 200 mg/dL"],
["VLDL", "2–30 mg/dL", "—", "> 30 mg/dL"],
],
[25*mm, 30*mm, 26*mm, COL_W-81*mm-4],
accent_col=colors.HexColor("#be185d")
)
items.append(lip_tbl)
return items
def build_viva():
items = []
items.append(section_header("VIVA HOT QUESTIONS", ACCENT_RED, "★"))
items.append(sp(1))
qa = [
("Why add acetic acid in protein test?",
"Dissolves phosphate precipitate; protein ppt persists after acidification"),
("Biuret +ve for amino acids?",
"NO – needs ≥ 2 peptide bonds (tripeptides and above)"),
("Ninhydrin colour with proline?",
"YELLOW (imino acid, not primary amino)"),
("GOD-POD wavelength?",
"505 nm"),
("Sucrose in osazone test?",
"NEGATIVE – non-reducing sugar (no free aldehyde/ketone)"),
("Beer-Lambert law formula?",
"A = ε × c × l"),
("Jaffe's reaction is used for?",
"Serum creatinine estimation"),
("ALT or AST – more liver specific?",
"ALT (SGPT) is more liver-specific"),
("Why does sulphur sink in Hay's test?",
"Bile salts reduce surface tension of urine"),
("Rothera's test detects?",
"Acetoacetate + acetone (ketone bodies)"),
("Barfoed's – why acidic medium?",
"Monosaccharides react in 5 min; disaccharides >10 min (differentiation)"),
("Seliwanoff's positive in 1 min means?",
"Ketose (fructose); Aldoses give faint pink only after prolonged heating"),
]
rows = []
for q, a in qa:
rows.append([
Paragraph("Q: " + q, ST["viva_q"]),
Paragraph("→ " + a, ST["viva_a"])
])
viva_tbl = Table(rows, colWidths=[55*mm, COL_W-55*mm-4])
viva_tbl.setStyle(TableStyle([
("FONTSIZE",(0,0),(-1,-1), 5.5),
("LEADING",(0,0),(-1,-1), 7.5),
("LEFTPADDING",(0,0),(-1,-1), 3),
("RIGHTPADDING",(0,0),(-1,-1), 3),
("TOPPADDING",(0,0),(-1,-1), 1.5),
("BOTTOMPADDING",(0,0),(-1,-1), 1.5),
("ROWBACKGROUNDS",(0,0),(-1,-1), [WHITE, LIGHT_RED]),
("GRID",(0,0),(-1,-1), 0.3, GRAY_MID),
("LINEAFTER",(0,0),(0,-1), 0.5, ACCENT_RED),
]))
items.append(viva_tbl)
return items
# ── Page template with header/footer ─────────────────────────────────────────
def on_page(canvas, doc):
canvas.saveState()
w, h = landscape(A4)
# Top banner
canvas.setFillColor(DARK_BLUE)
canvas.rect(0, h - HEADER_H, w, HEADER_H, fill=1, stroke=0)
canvas.setFillColor(WHITE)
canvas.setFont("Helvetica-Bold", 9)
canvas.drawCentredString(w/2, h - HEADER_H + 4.5*mm,
"1st YEAR MBBS – BIOCHEMISTRY PRACTICAL QUICK REFERENCE CARD")
canvas.setFont("Helvetica", 6.5)
canvas.setFillColor(colors.HexColor("#93c5fd"))
canvas.drawString(MARGIN, h - HEADER_H + 4.5*mm,
"Urine Analysis • Carbohydrates • Proteins • Colorimetry • Instruments")
canvas.drawRightString(w - MARGIN, h - HEADER_H + 4.5*mm,
"Blood Glucose • LFT • KFT • Lipid Profile")
# Vertical divider
canvas.setStrokeColor(GRAY_MID)
canvas.setLineWidth(0.5)
mid_x = MARGIN + COL_W + COL_GAP/2
canvas.line(mid_x, MARGIN + 4*mm, mid_x, h - HEADER_H - 2*mm)
# Footer
canvas.setFillColor(GRAY_DARK)
canvas.setFont("Helvetica", 5)
footer = "Sources: Lippincott's Biochemistry 8e | Harper's Illustrated Biochemistry 32e | Basic Medical Biochemistry 6e"
canvas.drawCentredString(w/2, 3*mm, footer)
canvas.restoreState()
# ── Build document ────────────────────────────────────────────────────────────
def build():
out = "/home/daytona/workspace/biochem-refcard/Biochem_Practical_QuickRef.pdf"
doc = BaseDocTemplate(
out,
pagesize=landscape(A4),
leftMargin=MARGIN, rightMargin=MARGIN,
topMargin=HEADER_H + 2*mm, bottomMargin=6*mm,
title="MBBS Biochemistry Practical Quick Reference",
author="Orris"
)
left_frame = Frame(
MARGIN, 6*mm,
COL_W, PAGE_H - HEADER_H - 8*mm,
leftPadding=0, rightPadding=0, topPadding=0, bottomPadding=0
)
right_frame = Frame(
MARGIN + COL_W + COL_GAP, 6*mm,
COL_W, PAGE_H - HEADER_H - 8*mm,
leftPadding=0, rightPadding=0, topPadding=0, bottomPadding=0
)
doc.addPageTemplates([
PageTemplate(id="TwoCol", frames=[left_frame, right_frame], onPage=on_page)
])
# ── Page 1: Left col (topics 1–3), Right col (topics 4–6 + viva start) ──
# ── Page 2: Left col (7–8), Right col (9 + viva) ──
story = []
# --- LEFT COLUMN PAGE 1 ---
story += build_urine_analysis()
story.append(sp(3))
story += build_carbohydrates()
story.append(sp(3))
story += build_proteins()
# --- RIGHT COLUMN PAGE 1 ---
from reportlab.platypus import FrameBreak
story.append(FrameBreak())
story += build_colorimetry()
story.append(sp(3))
story += build_instruments()
story.append(sp(3))
story += build_blood_glucose()
# --- PAGE 2 LEFT ---
from reportlab.platypus import PageBreak
story.append(PageBreak())
story += build_lft()
story.append(sp(3))
story += build_kft()
# --- PAGE 2 RIGHT ---
story.append(FrameBreak())
story += build_lipid()
story.append(sp(3))
story += build_viva()
doc.build(story)
print(f"PDF written to: {out}")
return out
if __name__ == "__main__":
build()