Commit graph

7 commits

Author SHA1 Message Date
dev
5c4b8ab7f2 Rename zenith-pyq-clone to whaleprep
Package, binary, tracing filter, footer copy, run_server.sh and README
updated. Footer no longer attributes zenithprepare.com.
2026-10-12 08:17:40 +05:30
dev
3df1d62002 Add expand_seed and document data sourcing
scripts/expand_seed.py generates synthetic questions across the full JEE Main
syllabus taxonomy (15 chapters, 45 subtopics, correct MCQ/NUM split). Stems
are deliberately sparse so no-one mistakes them for NTA content.

DATA.md documents why there is no real scraper: NTA PDFs render questions as
images; pdftotext extracts only IDs. Every aggregator with structured data
built it through manual entry pipelines. The document lists realistic paths
to a real bank and the SQL schema for loading one.
2026-10-12 05:19:03 +05:30
dev
1d8ee3b5ff Subtopic picker, JEE mode, per-subject timers
The builder previously exposed only chapters. Now each chapter expands to show
its subtopics as individual checkboxes: selecting a chapter checks all of its
subtopics, selecting the subject-level box checks everything, and selecting
individual subtopics sets the chapter to indeterminate. The exam query filters
by subtopic_id when any are given; the SQL path deduplicates so a question
matching two subtopics only appears once.

JEE mode sets duration to 60 × unique-subject-count (180 min for a full paper).
On the exam screen this renders per-subject countdown strips: Physics 60:00,
Maths 60:00, Chemistry 60:00. Each strip counts down independently; when one
hits zero the screen auto-jumps to the first unanswered question of the next
subject. The last strip exhausting time submits the form.

Single-subject papers keep the flat timer and show no strips.

Also: all audit targets now genuinely 44px (the .opt labels were 42px).
2026-10-12 05:13:09 +05:30
dev
14542f169a Make the exam screen work on a phone
The screen had no mobile state at all: the clock scrolled away mid-paper, the
only navigation was save-and-next so a candidate could not go back, the palette
sat below the question where it pushed submit off-screen, and the nav row lost
three of its four links under 700px.

Sticky header carries the clock and a progress bar; previous/next are real
buttons; the palette is a drawer below 1024px opened by a floating button that
sits above the tab bar. Primary destinations move to a bottom tab bar on
phones, data tables scroll inside their own card, and every control meets the
44px touch minimum.

Verified with a headless browser across five viewports rather than by reading
the stylesheet: scripts/audit_mobile.py fails on horizontal overflow, tap
targets under 44px, and content hidden behind a fixed bar; scripts/shots.py
captures each page at both widths. Both caught bugs I would otherwise have
shipped, including a palette toggle that lived inside the drawer it opened and
so could never be pressed on a phone.

fmt_dur drops a leading zero hour, so a 24 minute paper reads 24m 00s.
2026-10-11 21:54:17 +05:30
dev
c6269ad3dd Honour RUST_LOG, add load and scale benchmarks
The subscriber was pinned to DEBUG, so RUST_LOG=warn was ignored and sqlx
logged every statement to stderr in production. Now an EnvFilter defaults to
info with sqlx at warn; RUST_LOG=debug restores the query log.

Three benchmarks, so the hosting numbers come from measurement rather than
guesswork: scripts/bench.py for latency and throughput, bench_write.py for the
autosave and submit path under concurrent students, and bench_scale.py which
builds a database at the real bank's size (10,905 questions, 2,000 students,
12,000 papers, 900k answers) to report disk cost and query timings.
2026-10-11 21:08:35 +05:30
dev
3f88f6c388 Google sign-in with server-side token verification
Two paths, one session. The GSI popup posts an ID token to /student/google;
where a popup is refused a PKCE authorization-code flow redeems server side at
/student/google/callback, keeping the client secret out of the browser.

ID tokens are verified here rather than trusted from the browser: RS256
signature against Google's published JWKS (cached an hour), audience pinned to
this client, issuer restricted to Google, expiry enforced, email must be
verified. State is single use and expires in ten minutes. Without
GOOGLE_CLIENT_ID the button is hidden and the email form is offered instead.

13 tests cover the verification rules using locally generated RSA keys,
including rejection of a foreign signature, a wrong audience, a wrong issuer,
an expired token and a rewritten payload. An example signs a token from an
untrusted key so the rejection path can be checked without a Google account.
2026-10-11 20:37:56 +05:30
dev
d3001612b2 PYQ clone: builder, CBT exam screen, grading, analytics dashboard
Axum + SQLite full-stack copy of the ZenithPrep PYQ practice app.
Paper builder with unseen-question exclusion, timed CBT screen with
palette and autosave, +4/-1 grading with numerical tolerance, per-question
analysis, history, dashboard with weak-subtopic tracking and coach notes.
2026-10-11 20:06:26 +05:30