An AI-powered chapter-wise test platform for Indian school students (Grade 6–10). Students practise by answering questions verbally — the app listens, evaluates their answer against key concepts, and gives instant encouraging feedback.
- Student login — Students log in with a name and 4-digit PIN assigned by their teacher (no self-registration)
- Session management — 30-minute inactivity timeout with automatic session expiry; active sessions resume on re-login
- Cascading selection — Board (CBSE / ICSE) → Grade → Subject → Chapter, all populated from uploaded PDFs
- Predefined subject list — Subjects are selected from a fixed dropdown (Maths, Physics, Chemistry, Biology, History, Civics, Geography, Hindi, English) ensuring consistent naming
- Hindi language support — When a student takes a Hindi subject test, all AI-generated content (questions, key points, topic tags, summaries, feedback, and hints) is produced in Hindi (Devanagari script); voice input and text-to-speech automatically switch to
hi-IN - CBSE-pattern question paper — Three sections per chapter, scaled to chapter size:
- Section A — 10–15 one-mark questions (definitions, single facts, one-liners)
- Section B — 5–10 three-mark questions (brief explanations, 3 key points)
- Section C — 5–10 five-mark questions (detailed answers, 5 key points)
- Custom Test Builder — Build a personalised test spanning multiple chapters across subjects in a 3-step wizard:
- Step 1 — Select Board & Grade
- Step 2 — Browse subjects, click chapters to add them to a basket (shown as removable chips); freely switch subjects to pick across different subjects
- Step 3 — Review an AI-generated summary for each selected chapter, then start the test; questions for all chapters are pre-generated in the background so the test starts instantly
- Questions from all selected chapters are merged by mark-band (Section A → B → C) and shuffled within each section
- Question cap — custom tests are capped at 75 questions; if selections exceed this, questions are sampled proportionally per chapter while guaranteeing at least one question per unique (chapter, topic) pair
- Chapter summaries — AI-generated 3–5 sentence summaries for each chapter, displayed in the Custom Test Builder review step and cached after first generation
- Marks-aware hints — Each question shows its mark value and a plain-English guide on how much to write
- Full-topic coverage — Questions are scaled to chapter size (simple / medium / large) and distributed across every section of the chapter
- Voice answers — Speak your answer; the browser transcribes it in real time using the Web Speech API
- Context-aware hints — Tap "Get Hint" on any question for a gentle nudge; hints are personalised using the student's previous answers on the same topic and never reveal key points directly (max 30 hints per session, enforced and persisted in the database)
- Text-to-Speech — Each question is read aloud automatically; a replay button is always available
- Shuffled questions — Question order is randomised on every new test attempt so no two attempts are identical
- AI evaluation — Claude checks whether the student covered the key points (lenient on grammar, sequence, and phrasing)
- Encouraging feedback — Points covered shown in green; missed points shown in amber with a gentle reminder
- Test summary — Total score, percentage, section-wise breakdown (Section A / B / C), missed topics, and a per-question breakdown at the end
- Tabbed dashboard — Four tabs for clean navigation: Upload, Content, Students, Progress
- Student management — Create student accounts with name + 4-digit PIN, reset PINs, and delete students from the dashboard
- Single PDF upload — Attach a board, grade, subject (dropdown), and chapter name to any PDF
- Bulk PDF upload — Upload multiple PDFs at once; files are processed sequentially with live per-file progress; chapter names are auto-extracted from the first page of each PDF
- Duplicate chapter handling — If a chapter name already exists within the same board/grade/subject, a numeric suffix is appended automatically (e.g.
Chapter 1 (2)) - Chapter rename — Rename any chapter inline from the Uploaded Content table; duplicate names within the same board/grade/subject are rejected
- PDF viewer — Click any chapter name to open the original PDF in a full-screen modal viewer
- Content management — View, organise, and delete uploaded chapters
- Student progress — View every student's test attempts, scores, time taken, and per-question breakdown in a searchable, paginated table
- Question cache — Questions are generated once, validated by an LLM-as-judge, and cached; a "Refresh Questions" button forces full regeneration and re-validation
- AI model selection — Switch between Claude Haiku (fast & economical) and Claude Sonnet (more capable) from the Settings page — no server restart needed
- API key management — Enter the Anthropic API key directly in the UI (no server restarts needed)
- Password change — Update the admin password from the settings page
- Secure by default — bcrypt-hashed passwords, session-based authentication
- ChapterWise logo — Displayed in the header of every page; clicking it returns to the home page
- Brand colour scheme — Blue-to-green gradient matching the ChapterWise logo throughout the portal
- Mobile-friendly — Fully responsive layout for phones and tablets
| Layer | Technology |
|---|---|
| Backend | Python 3.9+, Flask 3.0, SQLAlchemy, SQLite |
| AI | Anthropic Claude API (Haiku & Sonnet; selectable per deployment) |
| PDF Extraction | pdftotext (poppler) → pypdf → pdfplumber (3-strategy cascade) |
| Frontend | React 19, Vite, React Router v7 |
| Voice Input | Browser Web Speech API (en-IN) |
| Text-to-Speech | Browser speechSynthesis API |
| Auth | bcrypt, Flask sessions, PIN-based student login |
| Web Server | nginx (production) with TLS 1.2/1.3, rate limiting, HSTS |
- Python 3.9+
- Node.js 20+ and npm 10+
- poppler-utils (for
pdftotext) —sudo apt install poppler-utilson Ubuntu /brew install poppleron macOS - An Anthropic API key — get one at console.anthropic.com
git clone https://github.com/ankit2101/chapterwise.git
cd chapterwisepip install -r requirements.txtcd frontend
npm install
cd ..Create a .env file in the project root:
# Required — change this to a long random string before deploying
SECRET_KEY=chapterwise-secret-change-in-prod-2024
# Optional — fallback Anthropic API key.
# You can also set this through Admin Panel → Settings after startup.
# ANTHROPIC_API_KEY=sk-ant-...The
SECRET_KEYsecures Flask session cookies. Any long random string works for local use. For production, generate a strong one withpython3 -c "import secrets; print(secrets.token_hex(48))".
The
ANTHROPIC_API_KEYset through the Admin Panel takes precedence over the.envvalue.
Open two terminals:
Terminal 1 — Flask API server:
python app.pyTerminal 2 — Vite dev server (hot reload):
cd frontend
npm run devOpen http://localhost:5173 in your browser.
Voice input works best in Google Chrome. Other browsers may not support the Web Speech API.
Build the React app once, then serve everything from Flask:
cd frontend && npm run build && cd ..
python app.pyOpen http://localhost:5000.
A convenience script starts the server and opens the browser automatically:
./start.sh- Open
http://localhost:5000/admin/login - Log in with the default credentials:
- Username:
admin - Password:
admin123
- Username:
- Go to Settings → paste your Anthropic API key → click Save API Key
- (Optional) In Settings → AI Model, choose between Claude Haiku (faster, cheaper) and Claude Sonnet (richer questions, deeper feedback) → click Save Model
- Go to Dashboard → Students tab → create student accounts:
- Enter a student name and a 4-digit PIN
- Share the name and PIN with the student (students cannot self-register)
- Upload your first PDF(s) from the Upload tab:
- Single upload — Select Board, Grade, Subject (dropdown), enter a Chapter Name, and upload one PDF
- Bulk upload — Select Board, Grade, Subject, then drag-and-drop multiple PDFs; chapter names are extracted automatically from each PDF's first page. After upload, use the ✏ button to rename any chapter directly in the results table.
- The app extracts text automatically. Chapters with very little text (scanned/image PDFs) will show a warning.
- Click any chapter name in the Content tab to preview the original PDF in a modal viewer, or click ✏ to rename it.
Change the default password immediately after first login via Settings → Change Password.
Admin creates student accounts (name + 4-digit PIN)
Admin uploads PDF
↓
Text extracted: pdftotext → pypdf → pdfplumber (cascade)
↓
Student logs in with name + PIN
↓
Option A — Single Chapter Test:
Student selects Board → Grade → Subject → Chapter → Start Test
Option B — Custom Multi-Chapter Test:
Step 1: Select Board & Grade
Step 2: Add chapters from one or more subjects to basket
Step 3: Review AI-generated chapter summaries → Start Custom Test
↓
Claude counts chapter subtopics and generates:
• 10–15 one-mark questions (Section A)
• 5–10 three-mark questions (Section B)
• 5–10 five-mark questions (Section C)
↓
LLM-as-judge evaluates questions (up to 3 iterations):
• Structural integrity — key_points count matches marks
• Factual accuracy — key points are correct and relevant
• Topic coverage — all chapter subtopics represented
• Topic tag accuracy — topic_tag matches question content
• Age appropriateness — language suits the grade level
Fixer corrects flagged questions; result cached once validated
↓
Question displayed with mark value + answer-length hint
Question read aloud via Text-to-Speech
↓
Student speaks answer → Web Speech API → transcript appears in text area
↓
Student submits → Claude evaluates key-point coverage
↓
Feedback shown: covered points ✓ | missed points → (with gentle guidance)
(Student may tap "Get Hint" at any time for a topic-aware nudge)
↓
Next question … until all questions answered
↓
Summary: total score, section-wise breakdown, missed topics, per-question detail
Questions are generated by Claude and scaled to chapter complexity:
| Chapter size | Section A (1 mark) | Section B (3 marks) | Section C (5 marks) | Total |
|---|---|---|---|---|
| Simple (1–4 subtopics) | 10 | 5 | 5 | 20 |
| Medium (5–7 subtopics) | 12 | 7 | 7 | 26 |
| Large (8+ subtopics) | 15 | 10 | 10 | 35 |
Every subtopic of the chapter appears in at least one question across all three sections. The number of key points per question exactly matches its mark value (1 key point for 1-mark, 3 for 3-mark, 5 for 5-mark), so scoring is transparent and proportional.
After generation, a second Claude call acts as a quality judge before the questions are cached. The judge evaluates each question against five criteria and returns a structured list of issues:
| Criterion | What is checked |
|---|---|
| Structural integrity | len(key_points) == marks for every question |
| Factual accuracy | All key points are correct and relevant to the question |
| Topic coverage | Every important chapter subtopic has at least one question |
| Topic tag accuracy | topic_tag accurately reflects what the question tests |
| Age appropriateness | Language complexity is suitable for the grade level |
If issues are found, a fixer agent corrects only the flagged questions (and generates new questions for any missing topics), then the judge runs again. This feedback loop repeats for up to 3 iterations, after which the best-available questions are cached regardless. The loop is capped to control cost — evaluation and fixing are charged only once per chapter since the final result is cached.
If the judge or fixer fails at any point, the system falls back gracefully to the questions as generated, so students are never blocked.
The project includes a comprehensive automated test suite covering API, integration, and end-to-end scenarios.
# Install test dependencies
pip install -r tests/requirements-test.txt
# Run all tests with coverage
pytest
# Run specific test files
pytest tests/test_student_api.py # 50+ student endpoint tests
pytest tests/test_admin_api.py # 60+ admin endpoint tests
pytest tests/test_integration.py # 9 end-to-end backend flow testsTests use an in-memory SQLite database with seeded test data — no real database or API key required.
cd e2e
npm install
npx playwright install chromium
# Run all UI tests
npm test
# Run specific suites
npx playwright test tests/student.spec.js # Student UI flows
npx playwright test tests/admin.spec.js # Admin UI flowschapterwise/
├── app.py # Flask app factory, SPA catch-all route
├── config.py # Configuration (model, DB path, upload folder)
├── models.py # SQLAlchemy models: Student, Admin, Chapter, TestSession, AppSettings
├── requirements.txt
│
├── routes/
│ ├── student.py # /api/student/login, /api/grades, /api/subjects, /api/chapters,
│ │ # /api/start-test, /api/start-custom-test, /api/chapter-summary/<id>,
│ │ # /api/submit-answer, /api/student/hint, /api/session/<key>
│ └── admin.py # /api/admin/* (login, upload, bulk-upload, chapter/pdf, chapter/rename,
│ # delete, password, API key, model config, students, student-progress)
│
├── services/
│ ├── pdf_service.py # 3-strategy PDF extraction: pdftotext → pypdf → pdfplumber
│ └── claude_service.py # Question generation, LLM-as-judge validation + fixer loop,
│ # answer evaluation, and hint generation prompts & functions
│
├── frontend/
│ ├── vite.config.js # Dev proxy (/api → :5000), build output → ../static/
│ └── src/
│ ├── App.jsx # React Router route definitions
│ ├── constants/
│ │ └── subjects.js # Shared predefined subject list
│ ├── context/ # AdminAuthContext, StudentAuthContext (login state)
│ ├── api/ # studentApi.js, adminApi.js (fetch wrappers with error handling)
│ ├── hooks/ # useSpeechRecognition.js, useTextToSpeech.js
│ ├── components/
│ │ ├── student/ # StudentLogin, SelectionPage, CustomTestBuilder (3-step wizard),
│ │ │ # TestPage, QuestionCard, VoiceInput, FeedbackCard, SummaryPage
│ │ ├── admin/ # AdminLogin, AdminDashboard (tabbed), UploadForm, BulkUploadForm,
│ │ │ # ChapterTable (with PDF viewer + rename), AdminSettings,
│ │ │ # StudentManagement, StudentProgress
│ │ └── shared/ # LoadingOverlay, Toast, ProtectedRoute,
│ │ # StudentProtectedRoute, Logo
│ └── styles/ # student.css, admin.css
│
├── tests/
│ ├── conftest.py # pytest fixtures with in-memory DB and seeded test data
│ ├── test_student_api.py # 50+ unit tests for all student API endpoints
│ ├── test_admin_api.py # 60+ unit tests for all admin API endpoints
│ ├── test_integration.py # 9 end-to-end backend flow tests
│ └── requirements-test.txt # Test dependencies (pytest, pytest-cov, pytest-flask)
│
├── e2e/
│ ├── playwright.config.js # Playwright configuration
│ ├── package.json
│ └── tests/
│ ├── student.spec.js # Student UI E2E tests
│ └── admin.spec.js # Admin UI E2E tests
│
├── docs/screenshots/ # App screenshots (used in README)
│
├── static/ # Built React output (gitignored; regenerated by npm run build)
├── uploads/pdfs/ # Uploaded chapter PDFs (gitignored)
└── chapterwise.db # SQLite database (gitignored, auto-created on first run)
| Variable | Required | Default | Description |
|---|---|---|---|
SECRET_KEY |
Required | (none — app will not start without it) | Flask session secret. Generate with python3 -c "import secrets; print(secrets.token_hex(32))". |
ANTHROPIC_API_KEY |
Optional | (none) | Fallback Anthropic API key. Can be set via Admin Panel → Settings instead. |
Generate a secure secret key:
python3 -c "import secrets; print(secrets.token_hex(48))"| Setting | Value | Description |
|---|---|---|
CLAUDE_MODEL |
claude-haiku-4-5-20251001 |
Default Claude model (fallback if not set via Admin Panel) |
AVAILABLE_MODELS |
Haiku, Sonnet | Models selectable from Admin Panel → Settings → AI Model |
MAX_CONTENT_LENGTH |
32 MB |
Maximum PDF upload size |
SQLALCHEMY_DATABASE_URI |
sqlite:///chapterwise.db |
SQLite database in the project root |
UPLOAD_FOLDER |
uploads/pdfs/ |
Directory where uploaded PDFs are stored |
DEFAULT_ADMIN_USERNAME |
admin |
Initial admin username (change via Admin Panel) |
DEFAULT_ADMIN_PASSWORD |
admin123 |
Initial admin password (change immediately after first login) |
| Method | URL | Description |
|---|---|---|
POST |
/api/student/login |
Authenticate student with name + PIN |
POST |
/api/student/session-ping |
Keep session alive (resets 30-min timer) |
GET |
/api/grades?board=CBSE |
Available grades for a board |
GET |
/api/subjects?board=CBSE&grade=8 |
Subjects for board + grade |
GET |
/api/chapters?board=CBSE&grade=8&subject=Science |
Chapters for board + grade + subject |
POST |
/api/start-test |
Create session, generate & validate questions, return first question |
POST |
/api/prefetch-questions |
Pre-generate and cache questions for a list of chapter IDs (called from Custom Test Builder Step 3; idempotent — skips chapters already cached) |
POST |
/api/start-custom-test |
Create a multi-chapter test session; merges questions by mark-band across all selected chapters (capped at 75 with topic-coverage sampling) |
GET |
/api/chapter-summary/<id> |
Get (and generate + cache if missing) an AI summary for a chapter |
POST |
/api/submit-answer |
Evaluate answer, return feedback + next question or summary |
POST |
/api/student/hint |
Generate a context-aware hint for the current question (uses student's prior answers on same topic; max 30 hints per session, persisted in DB) |
GET |
/api/session/<key> |
Restore session state (used on page refresh) |
| Method | URL | Description |
|---|---|---|
POST |
/api/admin/login |
Authenticate admin |
POST |
/api/admin/logout |
Clear session |
GET |
/api/admin/content |
All chapters grouped by board/grade/subject |
POST |
/api/admin/upload |
Upload a single PDF + metadata |
POST |
/api/admin/bulk-upload |
Upload multiple PDFs; chapter names auto-extracted from first page |
GET |
/api/admin/chapter/<id>/pdf |
Serve chapter PDF inline (used by the PDF modal viewer) |
PATCH |
/api/admin/chapter/<id>/rename |
Rename a chapter (duplicate-safe within board/grade/subject) |
DELETE |
/api/admin/chapter/<id> |
Delete chapter and PDF file |
POST |
/api/admin/change-password |
Update admin password |
POST |
/api/admin/save-api-key |
Store Anthropic API key |
GET |
/api/admin/api-key-status |
Check if API key is configured |
GET |
/api/admin/model-config |
Get available models and currently active model |
POST |
/api/admin/save-model |
Switch the active Claude model |
GET |
/api/admin/students |
List all students with session counts |
POST |
/api/admin/students |
Create a new student (name + PIN) |
DELETE |
/api/admin/students/<id> |
Delete a student account |
POST |
/api/admin/students/<id>/reset-pin |
Reset a student's PIN |
POST |
/api/admin/regenerate-questions/<id> |
Regenerate, validate (LLM-as-judge loop), and cache questions for a chapter; returns question_count |
GET |
/api/admin/student-progress |
All test sessions with scores, time, and per-question breakdown |
The production deployment is hardened with the following controls:
| Control | Detail |
|---|---|
| TLS | TLS 1.2 and 1.3 only; TLS 1.0/1.1 disabled |
| HSTS | max-age=31536000; includeSubDomains |
| Security headers | X-Frame-Options: SAMEORIGIN, X-Content-Type-Options: nosniff, Referrer-Policy, Permissions-Policy, Content-Security-Policy |
| Session cookies | Secure, HttpOnly, SameSite=Lax flags set in production |
| Session lifetime | Admin sessions expire after 8 hours of inactivity |
| Login rate limiting | 5 requests/minute (burst 2) on both /api/admin/login and /api/student/login |
| API rate limiting | 30 requests/minute on all other /api/ endpoints |
| Rate limit response | 429 with Retry-After: 60 header and JSON body (not nginx HTML) |
| Hint rate limiting | Max 30 hints per test session; count persisted in database so the limit survives page refreshes |
| Username enumeration | Student login returns identical error for wrong name and wrong PIN |
| bcrypt | All passwords and PINs stored as bcrypt hashes |
| Session auth | All admin endpoints require a valid session cookie |
| File permissions | .env → 0600; chapterwise.db → 0640; uploads/ → 0750 |
| HTTP redirect | Bare IP HTTP access redirects to HTTPS hostname |
| nginx version | Hidden (server_tokens off) |
- Voice input requires Google Chrome (or another Chromium-based browser). Firefox and Safari do not support the Web Speech API. Students on unsupported browsers can type their answers instead.
- Image-based PDFs (scanned documents) will extract very little text. The admin dashboard shows a warning for these files, and tests cannot be started until sufficient text is available.
- PDF extraction uses a 3-strategy cascade:
pdftotext(poppler C binary, fastest) →pypdf→pdfplumber. This ensures reliable extraction even for complex or large PDFs that cause Python-based parsers to hang. Thepdftotextbinary path is resolved automatically viashutil.which()so it works on both macOS (Homebrew) and Linux without hardcoding. - Question caching — questions for a chapter are generated, validated by the LLM-as-judge loop, and then stored in the database. Use the Refresh Q button in the admin dashboard to fully regenerate and re-validate them (e.g., after re-uploading a better PDF). The validation cost (up to 3 judge/fixer API calls) is incurred only once per chapter since the result is immediately cached.
- Custom tests — the Custom Test Builder generates questions for each selected chapter independently (using the same cache as single-chapter tests), then merges them into one combined paper ordered Section A → B → C. Questions within each section are shuffled. The existing
TestPagehandles both single-chapter and custom tests without modification. To prevent very large tests, the total is capped at 75 questions; when the cap applies, questions are sampled proportionally per chapter while guaranteeing one question per unique (chapter, topic_tag) pair. Questions are pre-generated in the background when the student reaches Step 3, so clicking "Start Custom Test" is instant after prefetch completes; the Start Test button is disabled until prefetch finishes. - Hints — each hint call is a lightweight Claude request (max 200 tokens). Hints are context-aware: Claude receives the student's previous answers on the same
topic_tag(up to 3) so it can connect the nudge to knowledge the student has already demonstrated. Hints never reveal key points word-for-word. The hint count is persisted in the database (test_sessions.hints_used) so the 30-hint cap survives page refreshes and session recovery. - Model selection — switching the Claude model (Haiku ↔ Sonnet) takes effect immediately for all new question generation, validation, answer evaluation, and hint generation; no server restart is required. The active model is cached in memory for 60 seconds to avoid a database round-trip on every Claude call; the cache is invalidated instantly when you save a new model via the Admin Panel.
- Bulk upload — files are uploaded one at a time sequentially to avoid timeouts on large PDFs. A live progress indicator shows which file is being processed.
- New: Hindi language support — questions, feedback, hints, and summaries are generated in Hindi (Devanagari script) for Hindi subject tests; voice input and TTS switch to
hi-INautomatically - New: Comprehensive automation test suite — 110+ backend unit tests (pytest), 9 integration flow tests, and Playwright E2E tests for both student and admin UIs
- New:
POST /api/prefetch-questions— pre-generates and caches questions for all selected chapters in the background when the student reaches Step 3 of the Custom Test Builder, so tests start instantly - Fix: Custom tests capped at 75 questions with proportional-per-chapter sampling that guarantees at least one question per unique (chapter, topic) pair
- Fix: Start Test button disabled while question prefetch is in progress to prevent premature submission
- Fix: nginx timeout increased and gunicorn upgraded to handle long-running question generation for large custom tests without a gateway timeout
- Fix:
studentApi.jsnow catches non-JSON server responses and surfaces a human-readable error instead of an unhandled parse failure
- New: Custom Test Builder — 3-step wizard for multi-chapter, cross-subject tests
- New: AI-generated chapter summaries with database caching (
summary_cache) - New:
POST /api/start-custom-testandGET /api/chapter-summary/<id>endpoints - Fix: Hints rate limit now properly persisted in
test_sessions.hints_usedcolumn (was resetting to 0 on every request) - Fix: Admin Students tab 500 error when a student record had a NULL
created_at - Security: Session cookies hardened (
Secure,HttpOnly,SameSite=Lax); session lifetime capped at 8 hours - Security: File permissions tightened —
.env→0600,chapterwise.db→0640,uploads/→0750 - Security: TLS 1.0 and 1.1 disabled on production server
See GitHub Releases for earlier release notes.
MIT


