Skip to content

feat: async combined student-report PDFs per school#82

Open
pritamps wants to merge 2 commits into
mainfrom
feat/reports-ui
Open

feat: async combined student-report PDFs per school#82
pritamps wants to merge 2 commits into
mainfrom
feat/reports-ui

Conversation

@pritamps

Copy link
Copy Markdown
Contributor

What

Adds an async API + SQS worker to generate a combined student-report PDF for one school on one test — the self-serve replacement for the manual etl-data-flow/flows/reports laptop run, scoped to physical-centre size (~50–60 students). Driven from the af_lms school page (companion PR: avantifellows/af_lms).

How it works

af_lms ──POST /reports/combined (X-Api-Key)──▶ submit: write report_jobs (queued) + enqueue SQS
                                                worker Lambda: read student_quiz_reports_v2 by session
                                                → 3-way id match to the school roster
                                                → render each via student_quiz_report_v2_print.html
                                                → html_to_pdf → pypdf merge → S3
af_lms ──GET /reports/combined/{id} (poll)──▶  status + presigned download_url when done
  • No BigQuery in the worker — the v2 docs already hold the AIET report content; we reuse the same template the single-report endpoint renders v2 with, so each page is identical to the per-student PDF the site already serves.
  • Auth: X-Api-Key service key (this PR also fills the previously-unvalidated APIKeyHeader placeholder). The end user is authorized by af_lms.
  • Status lifecycle: queued → started → processing → done | errored; errored jobs are retryable.

Infra (SAM, prod + staging)

  • New SQS-triggered worker Lambda + queue + DLQ; reserved concurrency throttles the shared Chromium.
  • S3 bucket avantifellows-combined-reports + report_jobs DynamoDB table are managed outside the stack (already created); the stack only references them.
  • Deploy workflows pass ReportingServiceApiKey (GH env secret, set) + ReportsBucketName.

Verification

Validated end-to-end against live prod data (no deploy): real session (AIET-01-G12-PCM, 40 students, JNV Palghar) → job done, 3-id match 40/40, merged 120-page PDF, presigned download. Also exercised locally through the af_lms school-page UI.

See docs/combined-reports-contract.md for the wire contract.

🤖 Generated with Claude Code

Generate one merged PDF of every student's report for a school on a given
test, async via an SQS worker, sourced from the existing
student_quiz_reports_v2 DynamoDB docs (no BigQuery).

- combined_reports router: submit / status / list / retry, gated by X-Api-Key
- ReportJobsDB: report_jobs table + status lifecycle (queued->...->done|errored)
- worker (SQS-triggered Lambda): read v2 -> 3-way id roster filter -> render via
  the existing student_quiz_report_v2_print.html -> html_to_pdf -> pypdf merge
  -> S3; presigned download regenerated on each poll
- service-key auth (fills the previously-unvalidated APIKeyHeader placeholder)
- SAM (prod+staging): worker function + SQS queue + DLQ, env/IAM; S3 bucket
  referenced (managed outside the stack); deploy workflows pass the new params
- docs/combined-reports-contract.md: the af_lms <-> reporting wire contract

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…limit)

Staging deploy failed: the combined-reports env additions pushed the API
function's environment over Lambda's 4KB cap. FIRESTORE_CREDENTIALS — a ~3KB
base64 service-account JSON — was 81% of the env (3136B of 3874B). Move it to
Secrets Manager, mirroring the existing BqCredentialsSecretName pattern:

- sessions_db: load the encoded key from FIRESTORE_CREDENTIALS_SECRET_NAME via
  Secrets Manager; keep the raw FIRESTORE_CREDENTIALS env path for local dev
- SAM (prod+staging): FirestoreCredentialsSecretName param + env;
  secretsmanager:GetSecretValue on the Firestore secret (was env var)
- workflows: a step syncs the FIRESTORE_CREDENTIALS GH secret into a per-env
  Secrets Manager secret (reporting-firestore-creds-{staging,prod}) before
  deploy; SAM passes only the name

Drops the API function env from ~4.1KB to ~1KB.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant