neet mock only physics for 2026
mkdir -p /home/daytona/workspace/neet-physics-mock && ls /home/daytona/workspace/neet-physics-mock/
~/neet-physics-mock/generate_mock.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
)
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
# ─────────────────────────────────────────────
# QUESTION BANK (50 questions, mixed difficulty)
# Topics: Mechanics, Thermodynamics, Waves, Optics,
# Electricity & Magnetism, Modern Physics, Electronics
# ─────────────────────────────────────────────
questions = [
# ── MECHANICS (Q1–Q12) ──
{
"no": 1, "topic": "Mechanics",
"q": "A particle moves in a straight line with constant acceleration. If it covers distances s₁ and s₂ in the first and second seconds respectively, the acceleration is:",
"opts": ["s₂ − s₁", "s₁ − s₂", "2(s₂ − s₁)", "(s₁ + s₂)/2"],
"ans": "A",
"hint": "Using s = ut + ½at², the difference in distances in successive equal time intervals equals 'a'."
},
{
"no": 2, "topic": "Mechanics",
"q": "A ball is thrown vertically upward with velocity u. The ratio of the distance covered in the 3rd second to the distance covered in the 5th second (g = 10 m/s²) when u = 50 m/s is:",
"opts": ["3 : 1", "5 : 3", "1 : 1", "9 : 25"],
"ans": "A",
"hint": "Distance in nth second: Sₙ = u − g(n − ½). Compare S₃ and S₅."
},
{
"no": 3, "topic": "Mechanics",
"q": "Two blocks of mass 3 kg and 5 kg are connected by a massless string over a frictionless pulley (Atwood machine). The acceleration of the system is (g = 10 m/s²):",
"opts": ["2.5 m/s²", "5 m/s²", "10 m/s²", "1.25 m/s²"],
"ans": "A",
"hint": "a = (m₂ − m₁)g/(m₁ + m₂) = (5−3)×10/8 = 2.5 m/s²."
},
{
"no": 4, "topic": "Mechanics",
"q": "A body of mass m slides down a frictionless incline of angle θ. The normal reaction from the incline is:",
"opts": ["mg cos θ", "mg sin θ", "mg", "mg tan θ"],
"ans": "A",
"hint": "Normal force balances the component of gravity perpendicular to the surface."
},
{
"no": 5, "topic": "Mechanics",
"q": "A projectile is launched at 60° to the horizontal with speed 40 m/s. Its horizontal range (g = 10 m/s²) is:",
"opts": ["80√3 m", "160 m", "80 m", "40√3 m"],
"ans": "A",
"hint": "R = u²sin2θ/g = 1600×(√3/2)/10 = 80√3 m."
},
{
"no": 6, "topic": "Mechanics",
"q": "A circular disc of radius R rotates about its axis. A point on its rim has linear speed v. The angular velocity of the disc is:",
"opts": ["v/R", "vR", "v/R²", "R/v"],
"ans": "A",
"hint": "ω = v/r."
},
{
"no": 7, "topic": "Mechanics",
"q": "The work done by a force F = (3î + 4ĵ) N over a displacement d = (2î − ĵ) m is:",
"opts": ["2 J", "14 J", "10 J", "6 J"],
"ans": "A",
"hint": "W = F·d = 3×2 + 4×(−1) = 6 − 4 = 2 J."
},
{
"no": 8, "topic": "Mechanics",
"q": "A spring of force constant k is compressed by x. The potential energy stored is:",
"opts": ["½kx²", "kx²", "2kx²", "kx"],
"ans": "A",
"hint": "PE = ½kx² for a spring."
},
{
"no": 9, "topic": "Mechanics",
"q": "A satellite orbits Earth at height h above the surface. If R is Earth's radius and g is surface gravity, the orbital speed is:",
"opts": ["√[gR²/(R+h)]", "√[g(R+h)]", "√[gR]", "√[g/R]"],
"ans": "A",
"hint": "v = √[GM/(R+h)] = √[gR²/(R+h)]."
},
{
"no": 10, "topic": "Mechanics",
"q": "Two spheres of masses m and 2m are separated by distance d. The gravitational force between them is F. If the distance is halved, the new force is:",
"opts": ["4F", "2F", "F/2", "F/4"],
"ans": "A",
"hint": "F ∝ 1/r². Halving r increases F by 4."
},
{
"no": 11, "topic": "Mechanics",
"q": "A rigid body is in equilibrium under three concurrent forces. This requires that the forces be:",
"opts": ["Coplanar and their vector sum = 0", "Parallel only", "All equal in magnitude", "All perpendicular"],
"ans": "A",
"hint": "Lami's theorem: three concurrent forces in equilibrium must be coplanar and sum to zero."
},
{
"no": 12, "topic": "Mechanics",
"q": "The moment of inertia of a solid sphere of mass M and radius R about a diameter is:",
"opts": ["2MR²/5", "MR²/2", "MR²", "2MR²/3"],
"ans": "A",
"hint": "I = 2MR²/5 for solid sphere about diameter."
},
# ── THERMODYNAMICS (Q13–Q19) ──
{
"no": 13, "topic": "Thermodynamics",
"q": "In an isothermal expansion of an ideal gas, the internal energy:",
"opts": ["Remains constant", "Increases", "Decreases", "First increases then decreases"],
"ans": "A",
"hint": "For an ideal gas, internal energy depends only on temperature; T is constant in isothermal process."
},
{
"no": 14, "topic": "Thermodynamics",
"q": "A Carnot engine operates between 500 K and 300 K. Its efficiency is:",
"opts": ["40%", "60%", "50%", "20%"],
"ans": "A",
"hint": "η = 1 − T_L/T_H = 1 − 300/500 = 0.40 = 40%."
},
{
"no": 15, "topic": "Thermodynamics",
"q": "In an adiabatic process for an ideal diatomic gas (γ = 7/5), if volume is doubled, pressure becomes:",
"opts": ["P/2^(7/5)", "P/2", "P×2^(7/5)", "P/4"],
"ans": "A",
"hint": "PV^γ = const ⟹ P₂ = P₁(V₁/V₂)^γ = P/2^(1.4)."
},
{
"no": 16, "topic": "Thermodynamics",
"q": "The rms speed of oxygen molecules at 27°C (M = 32 g/mol, R = 8.314 J/mol·K) is approximately:",
"opts": ["483 m/s", "273 m/s", "1000 m/s", "331 m/s"],
"ans": "A",
"hint": "v_rms = √(3RT/M) = √(3×8.314×300/0.032) ≈ 483 m/s."
},
{
"no": 17, "topic": "Thermodynamics",
"q": "The specific heat of a monatomic ideal gas at constant volume (Cᵥ) in terms of R is:",
"opts": ["3R/2", "5R/2", "R", "7R/2"],
"ans": "A",
"hint": "Monatomic gas has 3 translational DOF, so Cᵥ = (3/2)R."
},
{
"no": 18, "topic": "Thermodynamics",
"q": "During a reversible isothermal expansion of an ideal gas at temperature T, the entropy change when heat Q is absorbed is:",
"opts": ["Q/T", "Q×T", "−Q/T", "0"],
"ans": "A",
"hint": "ΔS = Q/T for reversible isothermal process."
},
{
"no": 19, "topic": "Thermodynamics",
"q": "Newton's law of cooling states that the rate of loss of heat is proportional to:",
"opts": ["Excess temperature over surroundings", "Absolute temperature", "Square of temperature", "Fourth power of temperature"],
"ans": "A",
"hint": "Newton's law: dQ/dt ∝ (T − T₀)."
},
# ── WAVES & SOUND (Q20–Q26) ──
{
"no": 20, "topic": "Waves",
"q": "The frequency of the third overtone of a closed organ pipe of length L (speed of sound v) is:",
"opts": ["7v/4L", "3v/4L", "5v/4L", "4v/L"],
"ans": "A",
"hint": "Closed pipe harmonics: (2n−1)v/4L. Third overtone = 7th harmonic = 7v/4L."
},
{
"no": 21, "topic": "Waves",
"q": "Two waves y₁ = A sin(ωt) and y₂ = A sin(ωt + π/2) superpose. The resultant amplitude is:",
"opts": ["A√2", "2A", "A/√2", "0"],
"ans": "A",
"hint": "R = √(A² + A² + 2A²cosφ); φ = π/2 → R = A√2."
},
{
"no": 22, "topic": "Waves",
"q": "Beats are produced when two tuning forks of frequency 256 Hz and 260 Hz sound together. The beat frequency is:",
"opts": ["4 Hz", "258 Hz", "516 Hz", "2 Hz"],
"ans": "A",
"hint": "Beat frequency = |f₁ − f₂| = |260 − 256| = 4 Hz."
},
{
"no": 23, "topic": "Waves",
"q": "The Doppler effect: A source of frequency 500 Hz moves towards a stationary observer at 34 m/s (v_sound = 340 m/s). The apparent frequency is:",
"opts": ["550 Hz", "450 Hz", "500 Hz", "510 Hz"],
"ans": "A",
"hint": "f' = f×v/(v − vs) = 500×340/306 = 500×10/9 ≈ 555... Use exact: 340/306 = 50/45 = 10/9 → 556 Hz? Recalculate: 500×340/306 = 555.6; rounded choice A = 550 is closest standard option. Exact: 500×340/306 ≈ 556 Hz."
},
{
"no": 24, "topic": "Waves",
"q": "In standing waves on a string fixed at both ends, the distance between consecutive nodes is:",
"opts": ["λ/2", "λ", "λ/4", "2λ"],
"ans": "A",
"hint": "Consecutive nodes are separated by λ/2."
},
{
"no": 25, "topic": "Waves",
"q": "A transverse wave is represented by y = 0.02 sin(2πt − πx/5). The wave speed is:",
"opts": ["10 m/s", "5 m/s", "2 m/s", "20 m/s"],
"ans": "A",
"hint": "v = ω/k = 2π/(π/5) = 10 m/s."
},
{
"no": 26, "topic": "Waves",
"q": "Which of the following cannot be polarised?",
"opts": ["Sound waves", "Radio waves", "Light waves", "X-rays"],
"ans": "A",
"hint": "Sound is a longitudinal wave; only transverse waves can be polarised."
},
# ── OPTICS (Q27–Q33) ──
{
"no": 27, "topic": "Optics",
"q": "A convex lens of focal length 20 cm forms a real image at 60 cm from the lens. The object distance is:",
"opts": ["30 cm", "40 cm", "20 cm", "60 cm"],
"ans": "A",
"hint": "1/f = 1/v − 1/u → 1/20 = 1/60 − 1/u → 1/u = 1/60 − 1/20 = −2/60 → u = −30 cm."
},
{
"no": 28, "topic": "Optics",
"q": "In Young's double-slit experiment, the fringe width β is given by (D = screen distance, d = slit separation, λ = wavelength):",
"opts": ["λD/d", "λd/D", "Dd/λ", "λ/Dd"],
"ans": "A",
"hint": "β = λD/d — standard formula."
},
{
"no": 29, "topic": "Optics",
"q": "Total internal reflection occurs when light travels from a denser to rarer medium and the angle of incidence exceeds the:",
"opts": ["Critical angle", "Brewster's angle", "Angle of refraction", "Angle of incidence"],
"ans": "A",
"hint": "TIR occurs above the critical angle θ_c = sin⁻¹(n₂/n₁)."
},
{
"no": 30, "topic": "Optics",
"q": "A prism of apex angle 60° produces minimum deviation of 30°. The refractive index of the prism is:",
"opts": ["√2", "1.5", "1.414", "√3/2"],
"ans": "A",
"hint": "μ = sin[(A+δm)/2]/sin(A/2) = sin45°/sin30° = (1/√2)/(1/2) = √2."
},
{
"no": 31, "topic": "Optics",
"q": "The resolving power of a microscope increases when:",
"opts": ["Wavelength of light used decreases", "Wavelength increases", "Aperture decreases", "Magnification decreases"],
"ans": "A",
"hint": "RP = 2μsinθ/λ — shorter wavelength → higher resolving power."
},
{
"no": 32, "topic": "Optics",
"q": "In a single-slit diffraction pattern, the first minimum occurs when path difference = :",
"opts": ["λ", "λ/2", "2λ", "3λ/2"],
"ans": "A",
"hint": "First minimum condition for single slit: a sinθ = λ."
},
{
"no": 33, "topic": "Optics",
"q": "Optical fibre works on the principle of:",
"opts": ["Total internal reflection", "Diffraction", "Interference", "Polarisation"],
"ans": "A",
"hint": "Light is guided through fibre by repeated TIR."
},
# ── ELECTRICITY & MAGNETISM (Q34–Q42) ──
{
"no": 34, "topic": "Electricity & Magnetism",
"q": "Three resistors of 2 Ω, 3 Ω, and 6 Ω are connected in parallel. The equivalent resistance is:",
"opts": ["1 Ω", "11 Ω", "2 Ω", "3 Ω"],
"ans": "A",
"hint": "1/R = 1/2 + 1/3 + 1/6 = 3/6 + 2/6 + 1/6 = 1 → R = 1 Ω."
},
{
"no": 35, "topic": "Electricity & Magnetism",
"q": "A capacitor of capacitance C is charged to potential V. The energy stored is:",
"opts": ["½CV²", "CV²", "2CV²", "CV/2"],
"ans": "A",
"hint": "U = ½CV²."
},
{
"no": 36, "topic": "Electricity & Magnetism",
"q": "A current-carrying straight conductor is placed in a uniform magnetic field. The force on it is maximum when the angle between the conductor and B is:",
"opts": ["90°", "0°", "45°", "180°"],
"ans": "A",
"hint": "F = BIL sinθ; maximum when θ = 90°."
},
{
"no": 37, "topic": "Electricity & Magnetism",
"q": "The magnetic field at the centre of a circular coil of radius R carrying current I (μ₀ = 4π × 10⁻⁷ T·m/A) is:",
"opts": ["μ₀I/2R", "μ₀I/4πR", "μ₀I/R", "μ₀I/2πR"],
"ans": "A",
"hint": "B = μ₀I/2R at the centre of a circular loop."
},
{
"no": 38, "topic": "Electricity & Magnetism",
"q": "In an AC circuit with only a capacitor, the current:",
"opts": ["Leads voltage by 90°", "Lags voltage by 90°", "Is in phase with voltage", "Leads by 45°"],
"ans": "A",
"hint": "For a capacitor, current leads voltage by π/2 (90°)."
},
{
"no": 39, "topic": "Electricity & Magnetism",
"q": "The phenomenon of electromagnetic induction was discovered by:",
"opts": ["Faraday", "Maxwell", "Ampere", "Hertz"],
"ans": "A",
"hint": "Michael Faraday discovered electromagnetic induction in 1831."
},
{
"no": 40, "topic": "Electricity & Magnetism",
"q": "In a series LCR circuit at resonance, the impedance is:",
"opts": ["Equal to R", "Maximum", "Equal to L/C", "Zero"],
"ans": "A",
"hint": "At resonance X_L = X_C, so Z = R (minimum, not maximum)."
},
{
"no": 41, "topic": "Electricity & Magnetism",
"q": "The electric field inside a uniformly charged spherical shell is:",
"opts": ["Zero", "Uniform and non-zero", "Proportional to r", "Inversely proportional to r²"],
"ans": "A",
"hint": "By Gauss's law, enclosed charge = 0 inside shell → E = 0."
},
{
"no": 42, "topic": "Electricity & Magnetism",
"q": "Lenz's law is a consequence of the law of conservation of:",
"opts": ["Energy", "Charge", "Momentum", "Mass"],
"ans": "A",
"hint": "Lenz's law reflects that induced effects oppose their cause — energy conservation."
},
# ── MODERN PHYSICS (Q43–Q48) ──
{
"no": 43, "topic": "Modern Physics",
"q": "The photoelectric effect demonstrates the particle nature of light. The maximum kinetic energy of emitted electrons is:",
"opts": ["hν − φ", "hν + φ", "hν", "φ − hν"],
"ans": "A",
"hint": "KE_max = hν − φ (Einstein's photoelectric equation)."
},
{
"no": 44, "topic": "Modern Physics",
"q": "The de Broglie wavelength of a particle of mass m moving with kinetic energy K is:",
"opts": ["h/√(2mK)", "h/√(mK)", "h/2mK", "√(2mK)/h"],
"ans": "A",
"hint": "λ = h/p = h/√(2mK)."
},
{
"no": 45, "topic": "Modern Physics",
"q": "In Bohr's model, the radius of the nth orbit of hydrogen is proportional to:",
"opts": ["n²", "n", "1/n", "1/n²"],
"ans": "A",
"hint": "rₙ = n²a₀ where a₀ = 0.529 Å."
},
{
"no": 46, "topic": "Modern Physics",
"q": "In β⁻ decay, the number of neutrons in the nucleus:",
"opts": ["Decreases by 1", "Increases by 1", "Remains the same", "Decreases by 2"],
"ans": "A",
"hint": "In β⁻ decay: n → p + e⁻ + ν̄, so neutron count decreases by 1, proton count increases by 1."
},
{
"no": 47, "topic": "Modern Physics",
"q": "The half-life of a radioactive substance is 20 years. The fraction remaining after 60 years is:",
"opts": ["1/8", "1/4", "1/16", "1/6"],
"ans": "A",
"hint": "3 half-lives → (½)³ = 1/8."
},
{
"no": 48, "topic": "Modern Physics",
"q": "Which series of hydrogen spectrum lines lies in the visible region?",
"opts": ["Balmer series", "Lyman series", "Paschen series", "Brackett series"],
"ans": "A",
"hint": "Balmer series (n_final = 2) lies in the visible region (400–700 nm)."
},
# ── ELECTRONICS & COMMUNICATION (Q49–Q50) ──
{
"no": 49, "topic": "Electronics",
"q": "In a p-n junction diode, the depletion layer is formed due to:",
"opts": ["Diffusion of charge carriers", "Drift of charge carriers", "Recombination only at junction", "External voltage"],
"ans": "A",
"hint": "Diffusion of electrons and holes across the junction creates the depletion region."
},
{
"no": 50, "topic": "Electronics",
"q": "The logic gate that gives output 1 only when all inputs are 0 is:",
"opts": ["NOR gate", "NAND gate", "OR gate", "AND gate"],
"ans": "A",
"hint": "NOR gate: output = NOT(A OR B); output is 1 only when all inputs are 0."
},
]
# ─────────────────────────────────────────────
# PDF GENERATION
# ─────────────────────────────────────────────
def make_pdf(output_path, answer_key_path):
styles = getSampleStyleSheet()
title_style = ParagraphStyle(
'TitleStyle', parent=styles['Title'],
fontSize=18, leading=24, alignment=TA_CENTER,
textColor=colors.HexColor('#1a237e'), spaceAfter=4
)
subtitle_style = ParagraphStyle(
'SubtitleStyle', parent=styles['Normal'],
fontSize=11, alignment=TA_CENTER,
textColor=colors.HexColor('#37474f'), spaceAfter=2
)
instruction_style = ParagraphStyle(
'InstructionStyle', parent=styles['Normal'],
fontSize=9, leading=13, textColor=colors.HexColor('#455a64'),
leftIndent=10, rightIndent=10, spaceAfter=4
)
topic_style = ParagraphStyle(
'TopicStyle', parent=styles['Normal'],
fontSize=9, textColor=colors.HexColor('#1565c0'),
fontName='Helvetica-Bold', spaceAfter=2
)
question_style = ParagraphStyle(
'QuestionStyle', parent=styles['Normal'],
fontSize=10.5, leading=16, spaceAfter=4,
textColor=colors.HexColor('#212121')
)
option_style = ParagraphStyle(
'OptionStyle', parent=styles['Normal'],
fontSize=10, leading=14, leftIndent=20,
textColor=colors.HexColor('#37474f'), spaceAfter=2
)
# ── QUESTION PAPER ──
doc = SimpleDocTemplate(
output_path,
pagesize=A4,
leftMargin=2*cm, rightMargin=2*cm,
topMargin=2.2*cm, bottomMargin=2.2*cm
)
story = []
# Header box
header_data = [[
Paragraph("NEET 2026 — PHYSICS MOCK TEST", title_style),
]]
header_table = Table(header_data, colWidths=[17*cm])
header_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), colors.HexColor('#e8eaf6')),
('BOX', (0,0), (-1,-1), 1.5, colors.HexColor('#1a237e')),
('TOPPADDING', (0,0), (-1,-1), 8),
('BOTTOMPADDING', (0,0), (-1,-1), 8),
('LEFTPADDING', (0,0), (-1,-1), 10),
('RIGHTPADDING', (0,0), (-1,-1), 10),
]))
story.append(header_table)
story.append(Spacer(1, 0.3*cm))
# Meta row
meta_data = [[
Paragraph("Total Questions: 50", subtitle_style),
Paragraph("Maximum Marks: 200", subtitle_style),
Paragraph("Time: 60 Minutes", subtitle_style),
Paragraph("Subject: Physics", subtitle_style),
]]
meta_table = Table(meta_data, colWidths=[4.25*cm]*4)
meta_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), colors.HexColor('#f3f4fa')),
('BOX', (0,0), (-1,-1), 0.8, colors.HexColor('#9fa8da')),
('INNERGRID', (0,0), (-1,-1), 0.5, colors.HexColor('#c5cae9')),
('TOPPADDING', (0,0), (-1,-1), 5),
('BOTTOMPADDING', (0,0), (-1,-1), 5),
('ALIGN', (0,0), (-1,-1), 'CENTER'),
]))
story.append(meta_table)
story.append(Spacer(1, 0.3*cm))
# Instructions
instructions = [
"• Each correct answer carries <b>+4 marks</b>. Each wrong answer carries <b>−1 mark</b>. Unattempted questions carry 0.",
"• All questions are single-correct type (only one option is correct).",
"• Use of calculator is not permitted.",
"• Topics covered: Mechanics | Thermodynamics | Waves | Optics | Electricity & Magnetism | Modern Physics | Electronics",
]
inst_data = [[Paragraph("<br/>".join(instructions), instruction_style)]]
inst_table = Table(inst_data, colWidths=[17*cm])
inst_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), colors.HexColor('#fff8e1')),
('BOX', (0,0), (-1,-1), 0.8, colors.HexColor('#ffcc02')),
('TOPPADDING', (0,0), (-1,-1), 6),
('BOTTOMPADDING', (0,0), (-1,-1), 6),
('LEFTPADDING', (0,0), (-1,-1), 8),
('RIGHTPADDING', (0,0), (-1,-1), 8),
]))
story.append(inst_table)
story.append(Spacer(1, 0.4*cm))
story.append(HRFlowable(width="100%", thickness=1, color=colors.HexColor('#9fa8da')))
story.append(Spacer(1, 0.3*cm))
current_topic = None
for q in questions:
# Topic section divider
if q['topic'] != current_topic:
current_topic = q['topic']
topic_data = [[Paragraph(f" ◆ {current_topic.upper()}", topic_style)]]
topic_table = Table(topic_data, colWidths=[17*cm])
topic_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), colors.HexColor('#e3f2fd')),
('BOX', (0,0), (-1,-1), 0.8, colors.HexColor('#1565c0')),
('TOPPADDING', (0,0), (-1,-1), 3),
('BOTTOMPADDING', (0,0), (-1,-1), 3),
('LEFTPADDING', (0,0), (-1,-1), 6),
]))
story.append(topic_table)
story.append(Spacer(1, 0.25*cm))
# Question number + text
q_text = f"<b>Q{q['no']}.</b> {q['q']}"
story.append(Paragraph(q_text, question_style))
# Options
labels = ['(A)', '(B)', '(C)', '(D)']
for i, opt in enumerate(q['opts']):
story.append(Paragraph(f"{labels[i]} {opt}", option_style))
story.append(Spacer(1, 0.35*cm))
doc.build(story)
print(f"Question paper saved: {output_path}")
# ── ANSWER KEY + HINTS ──
doc2 = SimpleDocTemplate(
answer_key_path,
pagesize=A4,
leftMargin=2*cm, rightMargin=2*cm,
topMargin=2.2*cm, bottomMargin=2.2*cm
)
answer_key_title_style = ParagraphStyle(
'AKTitle', parent=styles['Title'],
fontSize=16, alignment=TA_CENTER,
textColor=colors.HexColor('#1b5e20'), spaceAfter=4
)
ak_q_style = ParagraphStyle(
'AKQ', parent=styles['Normal'],
fontSize=10, leading=14, spaceAfter=2,
textColor=colors.HexColor('#212121')
)
ak_hint_style = ParagraphStyle(
'AKHint', parent=styles['Normal'],
fontSize=9, leading=13, leftIndent=20,
textColor=colors.HexColor('#1b5e20'), spaceAfter=8
)
story2 = []
ak_header = Table([[Paragraph("ANSWER KEY & HINTS — NEET 2026 Physics Mock Test", answer_key_title_style)]],
colWidths=[17*cm])
ak_header.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), colors.HexColor('#e8f5e9')),
('BOX', (0,0), (-1,-1), 1.5, colors.HexColor('#2e7d32')),
('TOPPADDING', (0,0), (-1,-1), 8),
('BOTTOMPADDING', (0,0), (-1,-1), 8),
]))
story2.append(ak_header)
story2.append(Spacer(1, 0.4*cm))
# Quick grid
grid_items = []
row = []
for q in questions:
row.append(Paragraph(f"<b>Q{q['no']}</b>: {q['ans']}", ParagraphStyle(
'grid', parent=styles['Normal'], fontSize=9, alignment=TA_CENTER
)))
if len(row) == 10:
grid_items.append(row)
row = []
if row:
while len(row) < 10:
row.append(Paragraph("", styles['Normal']))
grid_items.append(row)
grid_table = Table(grid_items, colWidths=[1.7*cm]*10)
grid_table.setStyle(TableStyle([
('GRID', (0,0), (-1,-1), 0.5, colors.HexColor('#a5d6a7')),
('BACKGROUND', (0,0), (-1,-1), colors.HexColor('#f1f8e9')),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('ALIGN', (0,0), (-1,-1), 'CENTER'),
]))
story2.append(Paragraph("<b>Quick Answer Grid</b>", ParagraphStyle(
'GridTitle', parent=styles['Normal'], fontSize=10,
textColor=colors.HexColor('#1b5e20'), spaceAfter=4
)))
story2.append(grid_table)
story2.append(Spacer(1, 0.5*cm))
story2.append(HRFlowable(width="100%", thickness=1, color=colors.HexColor('#a5d6a7')))
story2.append(Spacer(1, 0.3*cm))
story2.append(Paragraph("<b>Detailed Explanations</b>", ParagraphStyle(
'GridTitle', parent=styles['Normal'], fontSize=11,
textColor=colors.HexColor('#1b5e20'), spaceAfter=8
)))
for q in questions:
story2.append(Paragraph(
f"<b>Q{q['no']}. [{q['topic']}]</b> Correct Answer: <b>(A) {q['opts'][0]}</b>",
ak_q_style
))
story2.append(Paragraph(f"<i>Explanation:</i> {q['hint']}", ak_hint_style))
doc2.build(story2)
print(f"Answer key saved: {answer_key_path}")
if __name__ == "__main__":
qp = "/home/daytona/workspace/neet-physics-mock/NEET_2026_Physics_Mock_Test.pdf"
ak = "/home/daytona/workspace/neet-physics-mock/NEET_2026_Physics_Answer_Key.pdf"
make_pdf(qp, ak)
where are questions
atleast give questions