File-backed Flask web app for course homework PDF collection (no SQL database). This public repository ships the runnable app and tests; longer design/plan documents may live outside this tree (e.g. internal agent workspace). PLAN-B-002 scope: teacher web editor for course.yaml + Git publication.
- Public GitHub: https://github.com/thiswind/homework-collector
- Default branch:
master. Never commit.env,data/, or Fly/teacher secrets — useflyctl secrets setand.gitignore.
- Runtime
course.yamldefaults to$DATA_DIR/course.yaml(same persistent directory asroster.csvandstorage/on Fly). Override with envCOURSE_CONFIG. - On first boot, if that file is missing, the app copies the bundled template from
config/course.yamlin the image. - After teacher login → 课程与作业设置 → edit
course_id, course title, and each assignment (id+ display title). Changing an assignment id does not delete oldstorage/<old_id>/folders automatically.
- Python 3.12+
- Dependencies:
requirements.txt
conda activate base
pip install -r requirements-dev.txt
export SECRET_KEY="dev-secret"
export TEACHER_PASSWORD="your-teacher-password"
flask --app app:create_app run --debugOpen http://127.0.0.1:5000 — health check: http://127.0.0.1:5000/health
pytestdocker build -t homework-collector .
mkdir -p data
docker run --rm -d --name hc-local -p 8080:8080 \
-v "$(pwd)/data:/data" \
-e SECRET_KEY=replace-me \
-e TEACHER_PASSWORD=replace-teacher \
homework-collector
curl -sfS http://127.0.0.1:8080/health
docker rm -f hc-localPersistent roster and uploads live under DATA_DIR (default /data in the image). Mount a host directory to /data for local smoke tests so roster.csv survives container restarts.
flyctl auth loginthenflyctl auth whoami- Edit
fly.toml— set uniqueappname; adjustprimary_regionif needed - Create an app:
flyctl apps create <name> - Create a volume for persistent roster/storage:
flyctl volumes create homework_data --region sin --size 1 -a <app> - Volume mount is already declared in
fly.toml([[mounts]]→/data). If you previously createdhomework_datain another region (e.g.nrt), create a new volume insinfor this app; Fly volumes are region-bound. - Set secrets:
flyctl secrets set SECRET_KEY=... TEACHER_PASSWORD=... flyctl deploy— if the remote builder stalls or errors (e.g. depot handshake / deadline exceeded), retry withflyctl deploy --local-only(requires local Docker such as OrbStack).- Single Machine in
sin: runflyctl machines list; if more than one Machine is running, runflyctl scale count 1 --region sin --yes(or remove extra machines per Fly docs). - Verify:
curl -sfS https://<app>.fly.dev/health
curl -sfS https://<app>.fly.dev/healthreturns JSON with"status":"ok".- Open
https://<app>.fly.dev/in a browser; no TLS warnings. - Teacher login with secrets you set via
flyctl secrets set. - Optional: student enroll → upload PDF → teacher ZIP download (see manual E2E below).
Automated gates (pytest, docker build / local curl) are run on the implementation machine or CI; final sign-off is this HTTPS checklist, not localhost.
- Open
https://<app>.fly.dev/ - Teacher login → download roster template → optional import
- Student: enroll with valid 学号+姓名 from roster → save one-time password
- Student: login → upload PDF for hw01
- Teacher: download ZIP for hw01 → contains
pdfs/PDFs +ledger.csv
See .env.example. Critical: SECRET_KEY, TEACHER_USERNAME, TEACHER_PASSWORD, COURSE_CONFIG (optional; defaults to DATA_DIR/course.yaml), paths under DATA_DIR on persistent disk for Fly.
- Python 3.12
- Flask 3.x, Flask-WTF 1.3.x
- Docker base
python:3.12-slim-bookworm