Skip to content

Add FIN (wristband) field — separate from participant ID - #123

Merged
ktshah04 merged 2 commits into
mainfrom
feature/fin-field
Apr 27, 2026
Merged

ktshah04 merged 2 commits into
mainfrom
feature/fin-field

Conversation

@loopback

@loopback loopback commented Mar 25, 2026

Copy link
Copy Markdown
Collaborator

Context

The barcode scanner currently populates the participant ID field, but FIN (Financial Identification Number, from patient wristband) and the research participant ID are different identifiers. Research workflow needs both: a manually-entered participant ID (links to consent tracking, REDCap, audits) and a scanned FIN.

Design Decisions

FIN storage: New ParticipantFIN table in the existing recordings.db (not a separate database). Integer FK to participants.id with a unique constraint (one FIN per participant, upserted on conflict). Proper PHI access control is deferred to the DataJoint export layer (future scope, @ktshah04).

API contract: POST /api/v1/session extended with an optional fin query parameter. FIN is NOT echoed in the response.

Lazy participant creation: set_session() only writes to recordings.db when a FIN is provided — it calls _get_or_create_participant() then stores the FIN. The no-FIN flow remains a no-op against the DB (unchanged behavior).

FIN preserved across patient ID changes (app): If the operator scans a wristband then changes the typed participant ID, the FIN stays — it belongs to the wristband, not the typed ID.

FIN field allows manual entry (app): Editable text field. Barcode scanner auto-fills, manual typing as fallback.

DataJoint: Not implemented (owned by @ktshah04). Commented integration point in push_to_datajoint() references the new get_fin() helper in recording_db.py.

Server Changes (this PR)

Modified: multi_camera/backend/recording_db.py

  • New ParticipantFIN model: id (PK), participant_id (Integer FK → participants.id, unique, not null), fin (String, not null), created_at, updated_at
  • Participant gains a back-reference: fin_record = relationship("ParticipantFIN", uselist=False, back_populates="participant")
  • New _get_or_create_participant(db, participant_name) helper (factored out from existing _get_or_create_session())
  • store_fin(db, participant_name, fin) — upsert (creates participant if needed)
  • get_fin(db, participant_name)Optional[str]
  • push_to_datajoint() TODO updated to reference get_fin() from this same module

Modified: multi_camera/backend/fastapi.py

  • set_session() gains optional fin: Optional[str] = None query parameter
  • Uses the existing db_dependency() (no separate PHI dependency)
  • If fin is non-empty after session creation: calls store_fin(db, participant_name=subject_id, fin=fin.strip())
  • FIN value is not logged. Only logs that a FIN was stored.
  • Response model (Session) unchanged — FIN is not returned

Deleted: multi_camera/backend/phi_db.py

Replaced by the additions to recording_db.py.

App Changes (already on feature/fin-field in capture-app)

State model (patient_info_state.dart)

  • New fin field (String, default '')
  • New bool get hasFin => fin.trim().isNotEmpty
  • FIN is optional — does not gate session creation

Controller (patient_info_controller.dart)

  • updateFin(String value), clearFin()
  • updatePatientId() does NOT clear FIN
  • createSession() passes FIN to repository if non-empty

Repository (prepare_repository.dart)

  • createPreparation() gains optional String? fin parameter
  • Includes 'fin': fin in query parameters when non-null

Session creation screen (patient_info_screen.dart)

  • Patient ID field: barcode scanner icon removed (manual-entry only)
  • New FIN field below Patient ID in the same card; editable, with scanner + clear suffix buttons
  • Card title: "Patient ID" → "Patient Identification"
  • Scanner callback: controller.updatePatientId(value)controller.updateFin(value)

Barcode scanner sheet (barcode_scanner_sheet.dart)

  • Header: "Scan Patient ID Barcode" → "Scan Wristband Barcode"
  • Instruction: "Align Aztec barcode within the frame" → "Align wristband barcode within the frame"

Edge Cases

Scenario Behavior
FIN scanned before patient ID entered FIN populates. User types ID. Both sent on session create.
FIN scanned after session created FIN field disabled (locked with session).
Patient ID changed after FIN scanned FIN preserved. Belongs to wristband, not typed ID.
No FIN provided Session created without FIN. No DB write.
Same participant, different FIN Upsert overwrites previous FIN for that participant.

Verification

  1. Backend: POST /api/v1/session?subject_id=test01&fin=12345 → inspect data/recordings.db and confirm a participant_fin row links to participants.name = test01. Verify data/phi.db is not created. Repeat with same participant + different FIN → verify upsert. Call without fin for a fresh participant → verify no DB write.
  2. App: Patient ID field has no scanner icon. FIN field shows scanner icon, is editable. Scanner populates FIN. Manual typing works. FIN sent in API call on session create. FIN locked after session creation.

New phi_db module stores Financial Identification Numbers (FINs) in a separate SQLite database (data/phi.db) for PHI isolation. The POST /api/v1/session endpoint now accepts an optional `fin` query parameter — when provided, the FIN is upserted into the PHI database linked to the participant name. A TODO comment in push_to_datajoint() marks the integration point for including FIN in DataJoint pushes.
@loopback
loopback force-pushed the feature/fin-field branch from e7b7355 to a22f70d Compare April 8, 2026 21:18
Addresses review feedback on PR #123: drop the separate data/phi.db and store FIN alongside other recording metadata in the existing recordings.db. Proper PHI access control is deferred to the DataJoint export layer (future scope, @ktshah04).

- Delete multi_camera/backend/phi_db.py.
- Add ParticipantFIN model to recording_db.py with an integer FK to participants.id (matches the convention used by Session/Recording/Imported/Photo). Unique on participant_id (one FIN per participant, upserted on conflict).
- Add fin_record back-reference on the Participant model.
- Factor out _get_or_create_participant() from _get_or_create_session() so store_fin() can lazily create the participant row when (and only when) a FIN is provided. The no-FIN session-creation path is unchanged and still does not write to the DB.
- Move store_fin() and get_fin() helpers into recording_db.py.
- fastapi.py: drop phi_db_dependency, switch set_session() to the existing db_dependency, update the docstring, point store_fin import at recording_db. The API contract is unchanged (POST /api/v1/session?subject_id=...&fin=...).
- Update the push_to_datajoint() TODO to reference get_fin() in the same module and note that proper PHI access control belongs in the export layer.
@loopback

loopback commented Apr 8, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed review feedback: dropped the separate data/phi.db and consolidated FIN storage into the existing recordings.db.

  • Deleted multi_camera/backend/phi_db.py
  • Added ParticipantFIN model to recording_db.py with an integer FK to participants.id (matches the convention used by Session/Recording/Imported/Photo)
  • Factored out _get_or_create_participant() so set_session() can lazily create the participant row when (and only when) a FIN is provided — the no-FIN flow still does not write to the DB
  • set_session() now uses the existing db_dependency() instead of a separate PHI dependency
  • Updated the DataJoint TODO in push_to_datajoint() to note that proper PHI access control belongs in the export layer

API contract is unchanged (POST /api/v1/session?subject_id=...&fin=...), so the app-side commits already on feature/fin-field in capture-app need no changes.

Pushed as 81f90f1. PR description updated to reflect the new design.

@loopback
loopback marked this pull request as ready for review April 14, 2026 19:42
@loopback
loopback removed the request for review from peabody124 April 14, 2026 19:42
@ktshah04
ktshah04 merged commit cd5e104 into main Apr 27, 2026
@ktshah04
ktshah04 deleted the feature/fin-field branch April 27, 2026 19:45
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.

2 participants