SeatSeeker is a UC Merced course-availability notifier.
It provides:
- A Flask dashboard for subscription management
- A scheduler that checks CRNs and sends email notifications
- A shared SQLite/Postgres datastore
- Lightweight health and metrics endpoints
- A live queue scene that reflects people waiting for seat alerts
main/app.py: Flask app, API routes, DB initialization, metrics aggregationmain/checker_service.py: Scheduler loop, availability checks, SMTP notificationsmain/ClassChecker.py: UC Merced registration scraper/checkermain/run.py: CLI launcher (dashboard,scheduler,scheduler-once,setup,status)main/templates/index.html: Dashboard UImain/config.env: Environment templatemain/wsgi.py: WSGI entrypoint for GunicornDockerfile: Container build for web service deploymenttests/: Unit testsdocs/: Architecture and operations docs
cd main
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
cp config.env .env
python run.py setupStart web dashboard:
python run.py dashboardStart scheduler in a separate shell:
cd main
source venv/bin/activate
python run.py schedulerDashboard: http://localhost:5000
Edit main/.env (copied from config.env):
DATABASE_PATH: SQLite path (defaultdatabase.db)DATABASE_URL: Postgres connection URL (recommended for production/Supabase)- When set,
DATABASE_URLtakes precedence overDATABASE_PATH
- When set,
EMAIL_SENDER,EMAIL_PASSWORD,SMTP_SERVER,SMTP_PORT: SMTP settingsCHECK_INTERVAL,ERROR_RETRY_INTERVAL: scheduler timingsTERM_CODE: registration term codeSUBJECT_CODES: comma-separated subjects; blank means all subjectsREQUEST_TIMEOUT_SECONDS: HTTP timeout for checker requestsLOG_LEVEL: scheduler log levelNOTIFY_MODE: queue dispatch mode (allorfifo)NOTIFY_BATCH_SIZE: recipients per open CRN whenNOTIFY_MODE=fifoPRIORITY_EMAILS: global priority emails (comma-separated)PRIORITY_EMAILS_BY_CRN: CRN-specific priority map (12345:you@x.com|friend@x.com;23456:vip@x.com)PRIORITY_HOLD_MINUTES: delay before notifying non-priority subscribers after priority recipients are notifiedMAX_REQUEST_BODY_BYTES: max request payload size for API requestsGLOBAL_RATE_LIMIT: app-wide request limitSUBSCRIPTION_POST_RATE: rate limit forPOST /api/subscriptionsSUBSCRIPTION_DELETE_RATE: rate limit forDELETE /api/subscriptionsADMIN_API_KEY: required key for readingGET /api/subscriptions(headerX-SeatSeeker-Admin-Key)EXPOSE_INTERNAL_ERRORS: settrueonly for debugging to expose DB error details in/api/health
Default term note:
- As of April 9, 2026, default
TERM_CODEin project defaults is202630.
GET /: DashboardGET /api/health: Health status (okordegraded)GET /api/metrics: Aggregated metrics (request totals, distinct profiles, status counts)GET /api/subscriptions: Subscriptions grouped by email (requiresX-SeatSeeker-Admin-Key)GET /api/sent-notifications: Recent successful sends (requiresX-SeatSeeker-Admin-Key)POST /api/subscriptions: Add subscriptionsDELETE /api/subscriptions: Remove one subscription
From repository root:
python3 -m pytest -qcd main
pip install -r requirements.txt
python run.py setup
gunicorn -c gunicorn.conf.py wsgi:appRun scheduler as a second process:
python run.py schedulerFor persistent host deployments, prefer systemd units in deploy/systemd/.
Supabase note:
- Set
DATABASE_URLinmain/.envto your Supabase Postgres connection string. - Run
python run.py setuponce to create tables/indexes.
docker build -t seatseeker .
docker run --rm -p 5000:5000 --env-file main/.env -v "$PWD/main:/app/main" seatseekerFor full functionality, run scheduler as a second process/container:
cd main
python run.py scheduler- Health check:
GET /api/health - Metrics:
GET /api/metrics - Scheduler logs: stdout/stderr
- Runtime status:
python run.py status