~/biochem-pdf/make_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, HRFlowable, Table, TableStyle
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
OUTPUT = "/tmp/workspace/biochem-pdf/Glucose_Metabolism_QA.pdf"
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
rightMargin=2*cm,
leftMargin=2*cm,
topMargin=2*cm,
bottomMargin=2*cm,
)
styles = getSampleStyleSheet()
# Custom styles
title_style = ParagraphStyle(
'CustomTitle',
parent=styles['Title'],
fontSize=20,
textColor=colors.HexColor('#1a3c6e'),
spaceAfter=6,
spaceBefore=0,
alignment=TA_CENTER,
)
subtitle_style = ParagraphStyle(
'Subtitle',
parent=styles['Normal'],
fontSize=11,
textColor=colors.HexColor('#555555'),
spaceAfter=16,
alignment=TA_CENTER,
)
q_style = ParagraphStyle(
'Question',
parent=styles['Normal'],
fontSize=11,
leading=15,
textColor=colors.HexColor('#1a3c6e'),
fontName='Helvetica-Bold',
spaceBefore=14,
spaceAfter=6,
)
options_style = ParagraphStyle(
'Options',
parent=styles['Normal'],
fontSize=10,
leading=14,
textColor=colors.HexColor('#333333'),
leftIndent=12,
spaceAfter=4,
)
answer_label_style = ParagraphStyle(
'AnswerLabel',
parent=styles['Normal'],
fontSize=10.5,
fontName='Helvetica-Bold',
textColor=colors.HexColor('#ffffff'),
spaceBefore=6,
spaceAfter=3,
)
answer_text_style = ParagraphStyle(
'AnswerText',
parent=styles['Normal'],
fontSize=10,
leading=14,
textColor=colors.HexColor('#1a1a1a'),
alignment=TA_JUSTIFY,
spaceAfter=4,
)
source_style = ParagraphStyle(
'Source',
parent=styles['Normal'],
fontSize=8.5,
textColor=colors.HexColor('#777777'),
fontName='Helvetica-Oblique',
spaceAfter=4,
)
# ── Data ──────────────────────────────────────────────────────────────────────
qa_data = [
{
"num": "1.1",
"question": (
"A 4-month-old infant presents with severe fasting hypoglycemia, "
"hepatomegaly, and lactic acidosis. Laboratory evaluation shows elevated "
"serum uric acid and triglycerides. This patient most likely has a deficiency "
"in which of the following enzymes?"
),
"options": [
("A", "Hexokinase"),
("B", "Phosphofructokinase"),
("C", "Pyruvate carboxylase"),
("D", "Glucose-6-phosphatase"),
],
"answer": "D",
"correct": "Glucose-6-phosphatase",
"explanation": (
"This is a classic presentation of Von Gierke Disease (GSD Type I) — deficiency of "
"glucose-6-phosphatase. Without this enzyme, the liver cannot release free glucose "
"from glucose-6-phosphate, leading to: hepatomegaly (glycogen/fat accumulation), "
"fasting hypoglycemia (no glucose export), lactic acidosis (pyruvate/lactate build-up), "
"hyperuricemia, and hypertriglyceridemia."
),
"source": "Lippincott Biochemistry 8e — Table 11.1; Yamada's Gastroenterology 7e",
},
{
"num": "1.2",
"question": "Gluconeogenesis is decreased by:",
"options": [
("A", "Glucagon"),
("B", "Epinephrine"),
("C", "Glucocorticoids"),
("D", "Insulin"),
],
"answer": "D",
"correct": "Insulin",
"explanation": (
"Glucagon, Epinephrine, and Glucocorticoids all stimulate gluconeogenesis — "
"they are counter-regulatory hormones that raise blood glucose. "
"Insulin is the only hormone here that inhibits gluconeogenesis by suppressing "
"key gluconeogenic enzymes (PEPCK, fructose-1,6-bisphosphatase, glucose-6-phosphatase)."
),
"source": "Basic Medical Biochemistry 6e — Regulation of Fuel Metabolism",
},
{
"num": "1.3",
"question": "Lactate formed in muscles can be utilized through:",
"options": [
("A", "Rapoport-Luebeling cycle"),
("B", "Glucose-alanine cycle"),
("C", "Cori's cycle"),
("D", "Citric acid cycle"),
],
"answer": "C",
"correct": "Cori's cycle",
"explanation": (
"In the Cori cycle, lactate produced by anaerobic glycolysis in muscle is transported "
"via the blood to the liver, where it is converted back to glucose "
"(pyruvate → gluconeogenesis), which is then returned to muscle. "
"The Rapoport-Luebeling cycle involves 2,3-BPG in RBCs; the glucose-alanine cycle "
"carries nitrogen, not lactate primarily."
),
"source": "Lippincott Biochemistry 8e — Section B, Lactate (Cori Cycle)",
},
{
"num": "1.4",
"question": "Glucose-6-phosphatase is NOT present in:",
"options": [
("A", "Liver and kidneys"),
("B", "Kidneys and muscles"),
("C", "Kidneys and adipose tissue"),
("D", "Muscles and adipose tissue"),
],
"answer": "D",
"correct": "Muscles and adipose tissue",
"explanation": (
"Glucose-6-phosphatase is present in the liver and renal cortex (kidneys) — "
"organs that export glucose to the blood. It is ABSENT in muscle and adipose tissue. "
"This is why muscle cannot export free glucose derived from glycogen into the bloodstream."
),
"source": "Harper's Illustrated Biochemistry 32e — Glucose-6-Phosphate & Glucose",
},
{
"num": "1.5",
"question": "Pyruvate carboxylase is regulated by:",
"options": [
("A", "Induction"),
("B", "Repression"),
("C", "Allosteric regulation"),
("D", "All of these"),
],
"answer": "D",
"correct": "All of these",
"explanation": (
"Pyruvate carboxylase is regulated by all three mechanisms:\n"
"• Allosteric regulation: Acetyl-CoA is the obligate allosteric activator; "
"the enzyme is virtually inactive without it.\n"
"• Induction: Glucocorticoids and glucagon induce its synthesis during fasting/stress.\n"
"• Repression: Insulin represses gene expression, reducing enzyme levels in the fed state."
),
"source": "Lippincott Biochemistry 8e; Basic Medical Biochemistry 6e — Pyruvate carboxylase regulation",
},
]
# ── Build story ────────────────────────────────────────────────────────────────
story = []
# Title block
story.append(Paragraph("Biochemistry of Glucose Metabolism", title_style))
story.append(Paragraph("MCQ Answer Sheet — Cases 1.1 to 1.5", subtitle_style))
story.append(HRFlowable(width="100%", thickness=2, color=colors.HexColor('#1a3c6e'), spaceAfter=14))
for i, qa in enumerate(qa_data):
# Question number + text
story.append(Paragraph(f"Q{qa['num']}. {qa['question']}", q_style))
# Options
for letter, text in qa["options"]:
marker = "✓ " if letter == qa["answer"] else " "
opt_color = colors.HexColor('#1a7a3e') if letter == qa["answer"] else colors.HexColor('#333333')
opt_style = ParagraphStyle(
f'opt_{letter}',
parent=options_style,
textColor=opt_color,
fontName='Helvetica-Bold' if letter == qa["answer"] else 'Helvetica',
)
story.append(Paragraph(f"({letter}) {text}{' ✓' if letter == qa['answer'] else ''}", opt_style))
# Answer box (table for background colour)
answer_content = [
[Paragraph(f"ANSWER: ({qa['answer']}) {qa['correct']}", ParagraphStyle(
'ans_head', fontName='Helvetica-Bold', fontSize=11,
textColor=colors.white, leading=14,
))],
[Paragraph(qa["explanation"], ParagraphStyle(
'ans_body', fontSize=9.5, leading=14,
textColor=colors.HexColor('#1a1a1a'), alignment=TA_JUSTIFY,
))],
[Paragraph(f"Source: {qa['source']}", ParagraphStyle(
'ans_src', fontSize=8, fontName='Helvetica-Oblique',
textColor=colors.HexColor('#555555'),
))],
]
tbl = Table(answer_content, colWidths=[16.8*cm])
tbl.setStyle(TableStyle([
('BACKGROUND', (0, 0), (0, 0), colors.HexColor('#1a3c6e')),
('BACKGROUND', (0, 1), (0, 1), colors.HexColor('#eaf2fb')),
('BACKGROUND', (0, 2), (0, 2), colors.HexColor('#f5f5f5')),
('BOX', (0, 0), (-1, -1), 1, colors.HexColor('#1a3c6e')),
('INNERGRID', (0, 0), (-1, -1), 0.5, colors.HexColor('#c0c0c0')),
('TOPPADDING', (0, 0), (-1, -1), 6),
('BOTTOMPADDING', (0, 0), (-1, -1), 6),
('LEFTPADDING', (0, 0), (-1, -1), 10),
('RIGHTPADDING', (0, 0), (-1, -1), 10),
('VALIGN', (0, 0), (-1, -1), 'TOP'),
]))
story.append(Spacer(1, 6))
story.append(tbl)
if i < len(qa_data) - 1:
story.append(HRFlowable(width="100%", thickness=0.5, color=colors.HexColor('#cccccc'), spaceAfter=4, spaceBefore=10))
# Footer note
story.append(Spacer(1, 20))
story.append(HRFlowable(width="100%", thickness=1, color=colors.HexColor('#1a3c6e'), spaceBefore=4))
story.append(Paragraph(
"References: Lippincott Illustrated Reviews Biochemistry 8e | Harper's Illustrated Biochemistry 32e | "
"Basic Medical Biochemistry 6e | Yamada's Textbook of Gastroenterology 7e",
ParagraphStyle('footer', fontSize=8, textColor=colors.HexColor('#777777'), fontName='Helvetica-Oblique',
spaceBefore=6, alignment=TA_CENTER)
))
doc.build(story)
print(f"PDF created: {OUTPUT}")