Repository navigation
feat: import partner-lab reports (report-reader) - #23
Conversation
importExternalReport / externalReport(s) / matchExternalReport / applyExternalReport / rejectExternalReport / externalSignatories / nameExternalSignature, and Report.provenance. - report-reader extracts in the request up to IMPORT_INLINE_MAX_BYTES, else in the worker (imports queue; the payload is the stored file's id and the actor). IMPORT_MAX_BYTES caps the PDF (15 MiB); only an importExternalReport-only operation may exceed BODY_LIMIT_BYTES. - Booking match: name without titles, sex, age +-1; differences need confirmPatientMismatch. - Apply: results as printed into our reports (status entered) with report_provenance; staff verify. Edits after import are audited; catalog defaults are not filled in. - Printing: per-test provenance line; NABL only on tests the partner marked accredited; partner signature only from the same PDF (orgId + sourceSha256 + sha), instead of ours on single-source pages. - Errors: UNSUPPORTED_REPORT, FILE_TOO_LARGE, BOOKING_PATIENT_MISMATCH, REPORT_ALREADY_APPLIED. - Tests on fictional PDFs drawn at test time; docs/18-imports.md; schema, reference, llms regenerated. - Kits: auth-kit 1.4.0, db-kit 2.1.0, report-kit 2.1.0, report-reader 0.1.0, client 1.4.0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018jBohmYiwjQLWmN2duiZcA
|
CI Generated by Claude Code |
…1.0, report-reader 0.1.0, client 1.4.0, pdf-lib Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018jBohmYiwjQLWmN2duiZcA
|
The install now passes. The The published db-kit 2.1.0 still has a foreign key from The fix is in mgpl-diagnostics/db-kit#12 (2.1.1), which adds a contract check that would have caught this. Once v2.1.1 is published, I'll move this PR to Generated by Claude Code |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018jBohmYiwjQLWmN2duiZcA
Conflicts in kits.json, package files, errors.ts and llms-full.txt: kept both sides (report-reader and db-kit 2.1.1 from main; auth-kit 1.5.0, client 1.5.0 and ORG_UNAVAILABLE from this branch). Generated files regenerated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018jBohk4otGVRjt3Y1cF1N2
…dates) (#26) <!-- claude-projects-attribution: {"github_login":"BackStacked"} --> _Requested by **Himanshu** · [project thread](https://claude.ai/code/project/chan_016JXeBZ2UxSi4N5VU2C26fQ?thread=cmsg_016JXeBZ2UxSi4N5VU2C26fQU57JpX29saJ5azzjf6zEWH)_ ## What **Images per release.** `.github/workflows/release.yml`: a `v*` tag (admin-only) or a manual run on `main` checks the tag equals `package.json`, builds `ghcr.io/mgpl-diagnostics/api` for linux/amd64 and arm64 (tags `1.2.3`, `1.2`, `1` from 1.0, `latest`; kits read with the workflow token as a build secret) and creates the GitHub release with generated notes. **Easy hosting.** `docker-compose.yml` now pulls the released api and dashboard images (`MGPL_API_VERSION` / `MGPL_DASHBOARD_VERSION`, default `latest`) and adds the dashboard. nginx serves both on one origin: `/graphql` and `/health` → api replicas, the rest → dashboard, so `TRUST_PROXY=1` stays right and there is no CORS. Building from a checkout moved to `docker-compose.build.yml`. **Update notices.** New query `systemUpdates(dashboardVersion, refresh)` (permission `settings:manage`: owner and admin) compares this api's version and the dashboard's with the newest published GitHub releases of both repos, and returns the changelog in between plus the update steps. One GET per repo per `UPDATE_CHECK_INTERVAL_MS` (6 h) per process, "refresh" at most once a minute, last good answer kept on failure; `UPDATE_CHECK=false` makes no outbound call. Feed data is typed and capped; non-https links dropped; pre-releases and drafts ignored. New module `system` (schema, controller, service), no change to existing modules. **Fix.** `health.version` read `../../package.json` relative to the bundled chunk, so production images answered `0.0.0`; `src/version.ts` now reads the right file in both `tsx` and `dist`. Docs: new `docs/18-updates.md` (images, hosting, updating, releasing, the check), config table, `.env.example`, 12/17 updated. `schema.graphql`, API reference and `llms-full.txt` regenerated. ## Merge order `@mgpl-diagnostics/client` 1.6.0 (mgpl-diagnostics/client PR) has the `systemUpdates` operation, which the "every root field has a client operation" check needs, so this PR bumps the client to `^1.6.0`. Client main already includes the operations of #23 and #24, so: merge **#24 and #23**, then **client 1.6.0** (tag it), then this. `kits.json`/`package.json`/lock will conflict with #24 on the client line; take `1.6.0`, then `npm install` to refresh the lock and `npm run schema && npm run docs:reference && npm run llms`. ## Tests `TEST_BACKENDS=pg npm test` with kits built from local checkouts: 16 new `[updates]` checks pass (versions, changelog window, cache, refresh limit, unknown dashboard version, UNAUTHENTICATED, FORBIDDEN for front desk, 403 feed, `UPDATE_CHECK=false`, repo validation). The one failure is the client-contract check against today's main, which lacks #23/#24's fields (goes away in the merge order above). `docker compose config` passes for both compose files; `nginx -t` passes; nginx routing checked with stub containers. The image build itself runs for the first time in CI on the first tag. ## After merging - First release only: GitHub → org → Packages → `api` → change visibility to Public (or `docker login ghcr.io` on servers). - One-click updates are not included (the api never gets the Docker socket); asked in the project thread. --- _Generated by [Claude Code](https://claude.ai/code/session_018jBohmSXdQwAG8EmEBKYkQ)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
…d from the image (#27) <!-- claude-projects-attribution: {"github_login":"BackStacked"} --> _Requested by **Himanshu** · [project thread](https://claude.ai/code/project/chan_016JXeBZ2UxSi4N5VU2C26fQ?thread=cmsg_016JXeBZ2UxSi4N5VU2C26fQU57JpX29saJ5azzjf6zEWH)_ - **docs/20-vps.md**: one Ubuntu VPS with the released images: server hardening (ufw, SSH keys, Docker), files, the production `.env`, start, first lab, HTTPS with Caddy, daily `pg_dump` with off-server copies and a restore, updates, everyday commands, checklist. - **deploy/production.env.example**: only what production needs, `CHANGE_ME` where a value is required. `HTTP_PORT=127.0.0.1:8080` (Docker's ports bypass ufw) and `TRUST_PROXY=2` (Caddy + nginx). - **dist/seed.js** is now built (tsup entry), so the image can create the first lab: `docker compose run --rm db-sync node dist/seed.js` (direct to PostgreSQL; password taken from the shell's environment, never on the command line). Checked: `node dist/sync.js` then `node dist/seed.js` against PostgreSQL 17 created the org, branch, owner and 25 ledgers. - Links fixed: the update guide is `docs/19-updates.md` since #23 took 18 (`.env.example`, compose header, release notes text). `docker compose --env-file deploy/production.env.example config` binds nginx to 127.0.0.1:8080 and passes `TRUST_PROXY=2`. --- _Generated by [Claude Code](https://claude.ai/code/session_018jBohmSXdQwAG8EmEBKYkQ)_ Co-authored-by: Claude <noreply@anthropic.com>
What
Partner-lab PDFs (Hy Patho Lab, Clinova Pathlabs) are read by
@mgpl-diagnostics/report-reader. The import is matched to one of our bookings and applied to our reports as entered results with provenance. Staff verify and release as usual. Full write-up: docs/18-imports.md.importExternalReport(pdfBase64, fileName, bookingId, branchId)report:import(branch)externalReport(id),externalReports(branchId, bookingId, status, first, after)report:importmatchExternalReport(id, bookingId)report:importapplyExternalReport(id, mappings, confirmPatientMismatch)report:import+report:enterrejectExternalReport(id, reason)report:importexternalSignatories(lab)/nameExternalSignature(input)signatory:read/signatory:manageReport.provenancereport:readNew error codes:
UNSUPPORTED_REPORT,FILE_TOO_LARGE,BOOKING_PATIENT_MISMATCH,REPORT_ALREADY_APPLIED.How the rules are kept
test.accredited === true. No logo is ever drawn. Hy Patho reports carry no marks, so they never get the line.(orgId, that PDF's sha256, image sha). On pages from a single source, the partner signature prints instead of ours (report-kit's default, the owner's choice). It is never used as our own signatory, and our own reports never print a partner signature (a test checks this).entered, and printing still needsverifyReport. Results are copied as printed, with no catalog defaults and no recomputed ranges. Later edits are audited asreport.edit_imported(parameter names only).test/partner-pdfs.ts).Changes from the brief (choices for review)
resource:actionform:report:import,signatory:read/signatory:manage(auth-kit 1.4.0).importExternalReporttakes an optionalbranchId, which defaults to the booking's branch.applyExternalReporttakesconfirmPatientMismatch, so staff confirm a mismatch explicitly.SignatureImageonExternalSignatoryOnReport.image, not as aDocument.IMPORT_MAX_BYTES, but only for an operation whose single root field isimportExternalReport. Every other operation keepsBODY_LIMIT_BYTES(413,FILE_TOO_LARGE).Dependencies
auth-kit 1.4.0, db-kit 2.1.1 (
external_report_fileswithout a foreign key), report-kit 2.1.0, report-reader 0.1.0, client 1.4.0 (dev), and pdf-lib (dev, for the test PDFs). All are published, andpackage-lock.jsonandkits.jsonare updated. The client stays on 1.4.0 so that the lab-switching PR can follow this one.Tests
CI: 438 passed, 0 failed. It runs on PostgreSQL, through PgBouncer, with replicas, and on MongoDB, with BullMQ on a real Redis. Typecheck, build, schema, API reference and llms-full freshness all pass.
The import checks cover:
entereduntil verify);nameExternalSignaturere-extraction;NOT_FOUNDfor another organisation;Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_018jBohmYiwjQLWmN2duiZcA