A lightweight course portal for ~30 students with Google Sheets as the backend.
- FastAPI application scaffold
- Configuration via environment variables
- Docker containerization
- Development environment with hot reload
- SQLite for tokens and rate limiting
- Health check endpoint (
/health) - Google Sheets integration (with caching)
- Data models (Student, Quiz, QuizSubmission)
- Magic link authentication
- Student account claiming
- Onboarding flow
- Quiz system with auto-grading
- Admin analytics dashboard (per-question quiz performance)
- Admin grading page (spreadsheet view of all student scores)
- CSV export of grades
- CI/CD with GitHub Actions
- Production deployment to DigitalOcean
- Interactive Tools Feature
- Tools landing page (
/tools) - Individual tool pages (
/tools/{tool_id}) - Command builder UI (beginner flags)
- Scenario-based exercises with collapsible hints
- Embedded mini-quizzes with instant feedback
- Output simulator (sample command outputs)
- Progress tracking (localStorage)
- Tools landing page (
| Component | Technology |
|---|---|
| Backend | FastAPI (Python 3.12) |
| Database | Google Sheets + SQLite |
| Templates | Jinja2 |
| Auth | Magic links (JWT sessions) |
| Container | Docker |
| Hosting | DigitalOcean Droplet |
classapp/
├── app/
│ ├── main.py # FastAPI entry point
│ ├── config.py # Settings (pydantic-settings)
│ ├── routers/ # Route handlers
│ ├── services/ # Business logic
│ ├── models/ # Data models
│ ├── db/ # Database (SQLite)
│ └── templates/ # Jinja2 templates
├── content/
│ ├── quizzes/ # Quiz markdown files
│ └── tools/ # Interactive tool reference pages
├── tests/ # Pytest tests
├── scripts/ # Utility scripts
├── nginx/ # Nginx config
├── Dockerfile
├── docker-compose.yml # Production
└── docker-compose.dev.yml # Development
- Python 3.12+
- Docker (optional, recommended)
-
Clone the repository
git clone <repo-url> cd classapp
-
Create environment file
cp .env.example .env # Edit .env with your values -
Option A: Run with Docker (recommended)
docker compose -f docker-compose.dev.yml up --build
-
Option B: Run locally
python -m venv venv source venv/bin/activate # or `venv\Scripts\activate` on Windows pip install -r requirements-dev.txt uvicorn app.main:app --reload
-
Access the app
http://localhost:8000
| Variable | Required | Default | Description |
|---|---|---|---|
SECRET_KEY |
Yes | - | JWT signing key (32+ chars) |
BASE_URL |
Yes | - | Public URL of the app |
GOOGLE_SHEETS_ID |
Yes | - | Google Spreadsheet ID |
GOOGLE_SERVICE_ACCOUNT_PATH |
Yes | - | Path to service account JSON |
SMTP_HOST |
Yes | - | SMTP server hostname |
SMTP_PORT |
No | 587 |
SMTP server port |
SMTP_USER |
Yes | - | SMTP username |
SMTP_PASS |
Yes | - | SMTP password |
ENV |
No | development |
Environment (development/production) |
LOG_LEVEL |
No | INFO |
Log level |
SQLITE_PATH |
No | data/app.db |
SQLite database path |
pytestpytest --cov=app --cov-report=htmlpytest tests/test_root.py -vdocker compose -f docker-compose.dev.yml run --rm app pytestThe docker-compose.yml is configured for production:
# On the server
docker compose pull
docker compose up -d-
Build the image
docker build -t classapp . -
Run the container
docker run -d \ --name classapp \ -p 127.0.0.1:8000:8000 \ --env-file .env \ -v /var/lib/classapp:/var/lib/classapp \ classapp
GitHub Actions workflow will:
- Run tests on pull requests
- Build and push Docker image to GHCR on merge to
main - SSH to droplet and deploy
| Method | Path | Description |
|---|---|---|
| GET | / |
Root endpoint (returns app info) |
| GET | /health |
Health check (SQLite + Sheets status) |
| Method | Path | Description |
|---|---|---|
| POST | /auth/request-link |
Request magic link |
| GET | /auth/verify |
Verify magic link token |
| POST | /auth/logout |
Logout |
| GET | /claim |
Account claim form |
| POST | /claim |
Submit claim |
| GET | /onboarding |
Onboarding form |
| POST | /onboarding |
Submit onboarding |
| GET | /home |
Dashboard |
| GET | /quizzes |
Quiz list |
| GET | /quiz/{id} |
Quiz form |
| POST | /quiz/{id} |
Submit quiz |
| GET | /me |
Profile |
| GET | /tools |
Tools landing page |
| GET | /tools/{id} |
Individual tool page |
| Method | Path | Description |
|---|---|---|
| GET | /admin/analytics |
Quiz analytics overview |
| GET | /admin/quiz/{id} |
Per-question analytics for a quiz |
| GET | /admin/grading |
Grading table (all students x all quizzes) |
| GET | /admin/grading/csv |
Download grades as CSV |
The admin dashboard requires setting admin_email in the Config sheet of your Google Spreadsheet:
| key | value |
|---|---|
| admin_email | admin@example.com |
Only the user with this email can access admin pages:
- Analytics (
/admin/analytics) - Quiz completion rates and average scores - Quiz Details (
/admin/quiz/{id}) - Per-question analytics with answer distribution - Grading (
/admin/grading) - Spreadsheet view of all students' best scores per quiz - CSV Export (
/admin/grading/csv) - Download grades as CSV file
Admin users see an "Admin" link in the navigation bar on all pages.
Private - All rights reserved