Add FIN (wristband) field — separate from participant ID - #123
Merged
Merged
Conversation
loopback
force-pushed
the
feature/fin-field
branch
from
March 25, 2026 19:42
1c4719b to
e7b7355
Compare
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
force-pushed
the
feature/fin-field
branch
from
April 8, 2026 21:18
e7b7355 to
a22f70d
Compare
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.
Collaborator
Author
|
Addressed review feedback: dropped the separate
API contract is unchanged ( Pushed as 81f90f1. PR description updated to reflect the new design. |
loopback
marked this pull request as ready for review
April 14, 2026 19:42
ktshah04
approved these changes
Apr 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
ParticipantFINtable in the existingrecordings.db(not a separate database). Integer FK toparticipants.idwith 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/sessionextended with an optionalfinquery parameter. FIN is NOT echoed in the response.Lazy participant creation:
set_session()only writes torecordings.dbwhen 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 newget_fin()helper inrecording_db.py.Server Changes (this PR)
Modified:
multi_camera/backend/recording_db.pyParticipantFINmodel:id(PK),participant_id(Integer FK →participants.id, unique, not null),fin(String, not null),created_at,updated_atParticipantgains a back-reference:fin_record = relationship("ParticipantFIN", uselist=False, back_populates="participant")_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 referenceget_fin()from this same moduleModified:
multi_camera/backend/fastapi.pyset_session()gains optionalfin: Optional[str] = Nonequery parameterdb_dependency()(no separate PHI dependency)finis non-empty after session creation: callsstore_fin(db, participant_name=subject_id, fin=fin.strip())Session) unchanged — FIN is not returnedDeleted:
multi_camera/backend/phi_db.pyReplaced by the additions to
recording_db.py.App Changes (already on
feature/fin-fieldincapture-app)State model (
patient_info_state.dart)finfield (String, default'')bool get hasFin => fin.trim().isNotEmptyController (
patient_info_controller.dart)updateFin(String value),clearFin()updatePatientId()does NOT clear FINcreateSession()passes FIN to repository if non-emptyRepository (
prepare_repository.dart)createPreparation()gains optionalString? finparameter'fin': finin query parameters when non-nullSession creation screen (
patient_info_screen.dart)controller.updatePatientId(value)→controller.updateFin(value)Barcode scanner sheet (
barcode_scanner_sheet.dart)Edge Cases
Verification
POST /api/v1/session?subject_id=test01&fin=12345→ inspectdata/recordings.dband confirm aparticipant_finrow links toparticipants.name = test01. Verifydata/phi.dbis not created. Repeat with same participant + different FIN → verify upsert. Call withoutfinfor a fresh participant → verify no DB write.