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

Asking for Preferences
Reading File
Finding Sources
Finding Sources
Finding Sources
Finding Sources
Finding Sources
Finding Sources
Finding Sources
Finding Sources
Finding Sources
That's your complete quick-revision guide. Here's a summary of what's covered:
What's included:
  • Topic 1 - All physical + chemical urine tests with reagents, positive results, and clinical meaning
  • Topic 2 - All 7 carbohydrate tests in a table + osazone crystal shapes (a classic viva trap)
  • Topic 3 - All 7 protein tests with the specific amino acid/group each detects
  • Topic 4 - Beer-Lambert law (A = εcl), all parts of a colorimeter, sources of error
  • Topic 5 - Colorimeter vs spectrophotometer comparison, centrifuge, micropipette, water bath
  • Topic 6 - GOD-POD two-step reaction written out, normal glucose values, clinical states
  • Topic 7 - All LFT parameters with normal values + jaundice differentiator table
  • Topic 8 - Urea, creatinine (Jaffe's), uric acid with normal values + BUN:creatinine ratio
  • Topic 9 - Lipid profile values + Friedewald formula (LDL = TC - HDL - TG/5)
  • Viva tips - 10 most commonly asked one-liner questions with answers
A few things to memorize before the exam: the Friedewald formula, Beer-Lambert's law, the GOD-POD wavelength (505 nm), and why proline gives yellow with ninhydrin - examiners love those details.

Generate a PDF quick-reference card for exam day

Reading File
Running Command

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')"

Running Command

mkdir -p /home/daytona/workspace/biochem-refcard

Writing File

~/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()
Your credits are exhausted. You can purchase a top-up or subscribe to a plan to continue using Orris. If you're already subscribed, your daily credits will reset tomorrow.
This is a shared conversation. Sign in to Orris to start your own chat.