ALS — An offline-first intelligent learning platform that personalises lesson sequences and quiz difficulty based on individual student performance — no internet connection required at runtime.
📖 Documentation · 🚀 Quick Start · 📡 API Reference · 🤝 Contributing
- About the Project
- Key Features
- System Architecture
- Tech Stack
- Project Structure
- Quick Start
- Environment Variables
- API Reference
- Adaptive Engine
- Role System
- Database Schema
- Testing
- Roadmap
- Contributing
- License
The Adaptive Learning System (ALS) is a full-stack offline-capable application built for educational environments with unreliable or no internet access. It delivers a personalised curriculum to each student by continuously evaluating quiz performance and automatically adjusting lesson difficulty — no instructor intervention required.
The backend is a versioned REST API built with FastAPI, persisting to a local SQLite database via SQLAlchemy 2.x. The frontend is a React SPA with role-gated views for students and administrators. At the centre of the system is an adaptive engine that scores quizzes, tracks progress, and recommends the next lesson based on configurable performance thresholds.
All architectural decisions — schema design, API contracts, service layer boundaries, middleware stack — are documented as living artefacts in /docs and must remain in sync with the codebase they describe.
- 🔌 Offline-first — SQLite database, zero external service dependencies at runtime
- 🧠 Adaptive engine — promotes, demotes, or holds lesson difficulty based on real student performance
- 🔐 JWT authentication — stateless token-based auth with configurable expiry and refresh
- 🛡️ Role-based access control — student and admin roles enforced at the middleware layer
- 📡 Versioned REST API — all endpoints under
/api/v1/with consistent{data, error, meta}envelopes - 📋 Auto-generated OpenAPI docs — Swagger UI and ReDoc available in development mode
- 🪵 Structured request logging — every request logged with method, path, status, duration, and trace ID
- 🏗️ Clean architecture — strict layer separation: routes → services → repositories → models
- 🧪 Isolated test suite — in-memory SQLite, fresh session per test, 80%+ coverage enforced
┌──────────────────────────────────────────────────────┐
│ React SPA │
│ React Router v6 · Axios │
│ Student Views Admin Dashboard │
└─────────────────────┬────────────────────────────────┘
│ HTTP REST /api/v1/
┌─────────────────────▼────────────────────────────────┐
│ FastAPI Backend │
│ │
│ ┌─────────────────────────────────────────────┐ │
│ │ Middleware Stack │ │
│ │ request_logger → auth.py → permissions.py │ │
│ └──────────────────────┬──────────────────────┘ │
│ │ │
│ ┌──────────────────────▼──────────────────────┐ │
│ │ Routes /api/v1/ │ │
│ │ auth · lessons · quizzes · progress │ │
│ │ adaptive │ │
│ └──────────────────────┬──────────────────────┘ │
│ │ │
│ ┌──────────────────────▼──────────────────────┐ │
│ │ Services │ │
│ │ auth · lesson · quiz · progress │ │
│ │ recommendation · adaptive_engine │ │
│ └──────────────────────┬──────────────────────┘ │
│ │ │
│ ┌──────────────────────▼──────────────────────┐ │
│ │ Repositories │ │
│ │ student · lesson · question · result │ │
│ └──────────────────────┬──────────────────────┘ │
│ │ │
│ ┌──────────────────────▼──────────────────────┐ │
│ │ SQLite learning_system.db │ │
│ │ Student · Lesson · Question · QuizResult │ │
│ │ Progress · Recommendation · Subject │ │
│ └─────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────┘
| Concern | Technology |
|---|---|
| Framework | FastAPI |
| ORM | SQLAlchemy 2.x |
| Migrations | Alembic |
| Validation | Pydantic v2 |
| Authentication | JWT — python-jose |
| Password hashing | passlib[bcrypt] |
| Database | SQLite (dev) · PostgreSQL-ready |
| Logging | structlog |
| Testing | pytest · pytest-asyncio · httpx |
| Config | pydantic-settings BaseSettings |
| Concern | Technology |
|---|---|
| Framework | React 18 |
| Routing | React Router v6 |
| HTTP client | Axios |
| State management | Custom hooks |
adaptive-learning-system/
│
├── backend/
│ ├── api/
│ │ ├── routes/ # Thin endpoint handlers — no business logic
│ │ │ ├── auth_routes.py
│ │ │ ├── lesson_routes.py
│ │ │ ├── quiz_routes.py
│ │ │ ├── progress_routes.py
│ │ │ └── adaptive_routes.py
│ │ └── dependencies/ # FastAPI Depends providers
│ │
│ ├── core/ # Cross-cutting foundation
│ │ ├── config.py # Pydantic BaseSettings — single env var source
│ │ ├── security.py # JWT encode/decode, password hashing
│ │ ├── middleware.py # Middleware registration and ordering
│ │ ├── logging.py # Structured JSON logger
│ │ └── constants.py # Typed literals — roles, limits, thresholds
│ │
│ ├── database/
│ │ ├── connection.py # Engine, sessionmaker, get_db()
│ │ ├── migrations/ # Alembic revision scripts
│ │ └── seeders/ # Development and test seed data
│ │
│ ├── middleware/ # HTTP-layer gates
│ │ ├── auth.py # JWT verification → request.state principal
│ │ ├── permissions.py # Role enforcement (student vs admin)
│ │ └── request_logger.py # Request/response logging with trace ID
│ │
│ ├── models/ # SQLAlchemy declarative models (schema source of truth)
│ │ ├── student.py
│ │ ├── subject.py
│ │ ├── lesson.py
│ │ ├── question.py
│ │ ├── quiz_result.py
│ │ ├── progress.py
│ │ └── recommendation.py
│ │
│ ├── repositories/ # All database query logic
│ │ ├── student_repository.py
│ │ ├── lesson_repository.py
│ │ ├── question_repository.py
│ │ └── result_repository.py
│ │
│ ├── schemas/ # Pydantic request/response contracts
│ │ ├── student_schema.py
│ │ ├── auth_schema.py
│ │ ├── lesson_schema.py
│ │ ├── question_schema.py
│ │ ├── quiz_schema.py
│ │ └── progress_schema.py
│ │
│ ├── services/ # Business logic and orchestration
│ │ ├── auth_service.py
│ │ ├── lesson_service.py
│ │ ├── quiz_service.py
│ │ ├── progress_service.py
│ │ ├── recommendation_service.py
│ │ └── adaptive_engine.py # Core adaptive algorithm
│ │
│ ├── storage/ # Physical asset directories
│ │ ├── images/
│ │ ├── documents/
│ │ │ ├── lessons/
│ │ │ ├── notes/
│ │ │ └── pdfs/
│ │ └── videos/
│ │
│ ├── tests/ # Isolated pytest test suite
│ │ ├── conftest.py # Fixtures: DB session, test client, factories
│ │ ├── test_auth.py
│ │ ├── test_quiz.py
│ │ ├── test_progress.py
│ │ └── test_adaptive_engine.py
│ │
│ ├── utils/ # Pure stateless utility functions
│ │ ├── helpers.py
│ │ ├── validators.py
│ │ ├── score_calculator.py
│ │ └── level_generator.py
│ │
│ ├── main.py # FastAPI app entry point
│ ├── requirements.txt
│ └── .env.example
│
├── frontend/
│ ├── src/
│ │ ├── components/
│ │ ├── pages/
│ │ ├── hooks/
│ │ ├── services/
│ │ └── routes/
│ └── public/
│
├── docs/ # Living project documentation
│ ├── ProjectProposal.docx
│ ├── SRS.docx
│ ├── DatabaseDesign.docx
│ ├── API_Documentation.docx
│ ├── UserManual.docx
│ └── FinalReport.docx
│
└── README.md
- Python 3.11+
- Node.js 18+
- pip
- Git
git clone https://github.com/your-username/adaptive-learning-system.git
cd adaptive-learning-systemcd backend
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txtcd ../frontend
npm installcp backend/.env.example backend/.env
# Open .env and fill in all required values — see Environment Variables belowcd backend
alembic upgrade head
⚠️ Never useBase.metadata.create_all()in production. Alembic is the only sanctioned schema management path.
python -m database.seeders.seed_dev# Backend — from /backend
uvicorn main:app --reload --port 8000
# Frontend — from /frontend
npm run dev| Service | URL |
|---|---|
| Frontend | http://localhost:5173 |
| API | http://localhost:8000/api/v1/ |
| Swagger UI | http://localhost:8000/docs |
| ReDoc | http://localhost:8000/redoc |
All project documentation lives in /docs as .docx files and must remain in sync with the codebase:
| Document | Purpose |
|---|---|
ProjectProposal.docx |
System rationale, goals, and stakeholders |
SRS.docx |
Functional and non-functional requirements with acceptance criteria |
DatabaseDesign.docx |
ERD, table definitions, and constraint rationale |
API_Documentation.docx |
Human-readable rendering of the OpenAPI schema |
UserManual.docx |
Student and admin usage guide with screenshots |
FinalReport.docx |
Architecture decisions, retrospective, and deployment summary |
Stale documentation is a defect. Any schema, API contract, or architecture change requires a corresponding doc update in the same PR.
| Variable | Description | Required |
|---|---|---|
DATABASE_URL |
SQLite path or PostgreSQL DSN | ✅ |
SECRET_KEY |
JWT signing secret (min 32 chars) | ✅ |
ALGORITHM |
JWT algorithm — HS256 |
✅ |
ACCESS_TOKEN_EXPIRE_MINUTES |
Token TTL in minutes | ✅ |
STUDENT_SCORE_THRESHOLD_LOW |
Score below which difficulty decreases | ✅ |
STUDENT_SCORE_THRESHOLD_HIGH |
Score above which difficulty increases | ✅ |
MAX_UPLOAD_SIZE_MB |
Maximum allowed file upload size | ✅ |
SCORE_VERSION |
Active scoring formula version | ✅ |
ENVIRONMENT |
development or production |
✅ |
There are no silent defaults for secrets. The app fails fast with a clear error if any required variable is missing at startup.
All endpoints are versioned under /api/v1/. Every response follows a consistent envelope:
{
"data": {},
"error": null,
"meta": {
"trace_id": "550e8400-e29b-41d4-a716",
"timestamp": "2026-06-27T10:00:00Z"
}
}| Method | Endpoint | Auth | Description |
|---|---|---|---|
POST |
/api/v1/auth/register |
❌ | Register a new student account |
POST |
/api/v1/auth/login |
❌ | Obtain JWT access token |
POST |
/api/v1/auth/refresh |
✅ | Refresh an access token |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/v1/lessons |
✅ | List all available lessons |
GET |
/api/v1/lessons/{id} |
✅ | Get a single lesson by ID |
POST |
/api/v1/lessons |
✅ Admin | Create a new lesson |
PATCH |
/api/v1/lessons/{id} |
✅ Admin | Update an existing lesson |
DELETE |
/api/v1/lessons/{id} |
✅ Admin | Remove a lesson |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/v1/quizzes/{lesson_id} |
✅ | Get quiz questions for a lesson |
POST |
/api/v1/quizzes/{lesson_id}/submit |
✅ | Submit answers and receive scored result |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/v1/progress |
✅ | Get authenticated student's progress |
GET |
/api/v1/progress/report |
✅ Admin | Get all students' aggregated progress |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/v1/adaptive/next-lesson |
✅ | Get the recommended next lesson for the current student |
HTTP 401 — missing or expired JWT · HTTP 403 — insufficient role · HTTP 405 — unsupported method · HTTP 422 — validation failure with field-level detail
The adaptive engine lives in services/adaptive_engine.py and exposes a single callable interface:
recommend_next_lesson(student_id: int, recent_results: list[QuizResult]) -> Lessonavg(recent scores) < STUDENT_SCORE_THRESHOLD_LOW → difficulty - 1 (easier lesson)
avg(recent scores) > STUDENT_SCORE_THRESHOLD_HIGH → difficulty + 1 (harder lesson)
otherwise → difficulty ± 0 (same level)
- All thresholds are sourced from
core/constants.py— never hardcoded in the engine - The engine is stateless and HTTP-context-free — fully unit-testable without mocking FastAPI
- Formula version tracked via
SCORE_VERSIONconstant to support re-scoring historical results
| Role | Capabilities |
|---|---|
student |
Register · Login · View lessons · Submit quizzes · View own progress · Get adaptive recommendations |
admin |
All student capabilities + Create/edit/delete lessons · View all students' progress reports |
Roles are enforced exclusively at middleware/permissions.py. Role checks are never duplicated inside individual endpoint handlers.
Core tables and their relationships:
Student ──────────┬──── QuizResult ────── Question ──── Lesson ──── Subject
│ │ │
└──── Progress └──── Recommendation ──────────────────►┘
| Table | Primary Key | Key Columns |
|---|---|---|
students |
id |
email, password_hash, role, created_at |
subjects |
id |
name, description |
lessons |
id |
subject_id, title, difficulty, content_path |
questions |
id |
lesson_id, body, correct_answer, weight |
quiz_results |
id |
student_id, lesson_id, score, submitted_at |
progress |
id |
student_id, lesson_id, status, updated_at |
recommendations |
id |
student_id, lesson_id, reason, created_at |
The
DatabaseDesign.docxin/docscontains the full ERD and constraint rationale. It must match/modelswith zero discrepancies at all times.
cd backend
# Run full test suite
pytest
# Run with coverage report
pytest --cov=. --cov-report=term-missing
# Run a specific test module
pytest tests/test_adaptive_engine.py -v
# Run async tests only
pytest -m asyncio -v| Layer | Target |
|---|---|
services/ |
90% |
repositories/ |
85% |
utils/ |
100% |
| Overall minimum | 80% |
- All tests use an in-memory SQLite database — no dependency on the production
.dbfile - Each test function receives a fresh session via pytest fixture — no shared state between tests
- External dependencies are mocked with
unittest.mock.patch conftest.pyprovides: DB session fixture · test FastAPI client fixture · Student, Lesson, and Question factory helperstest_adaptive_engine.pymust cover three branches: below threshold, at threshold, above threshold
- Core authentication — register, login, JWT refresh
- Lesson CRUD with admin role gate
- Quiz submission and scoring
- Adaptive engine v1 — threshold-based difficulty adjustment
- Progress tracking and aggregation
- Structured request logging with trace IDs
- Adaptive engine v2 — weighted rolling average over last N results
- Offline PWA shell for the frontend
- Admin analytics dashboard with progress heatmaps
- Export progress reports to PDF
- PostgreSQL migration guide for production deployment
- Browse the open GitHub Issues — every backend folder has a dedicated issue with objectives and acceptance criteria
- Assign yourself to the issue before starting work to avoid duplication
- Create a branch following the convention below
- Implement the changes and write tests — PRs without tests for new logic will not be merged
- Open a PR that references the issue (
Closes #N) - Any schema, API contract, or architecture change requires a corresponding
/docsupdate in the same PR
{scope}-{short-description}
Examples:
models-orm-schema-definitions
services-adaptive-engine
api-routes-http-surface
middleware-jwt-auth
database-connection-and-migrations
frontend-progress-dashboard
tests-backend-quality-gate
This project is licensed under the MIT License — see the LICENSE file for details.
Built with precision · Documented with intent · Tested before merged