Skip to content

fix(queue): scope backend queues to camp visits

Lakshy Yarlagadda requested to merge fix/queue-camp-visits into develop
Title: feat(camp): scope consultation workflow to current camp visit

## Overview
This MR fixes incorrect workflow resolution across medical camps by scoping consultation queue and related backend flows to the patient's current `camp_visit`.

Previously, several backend paths resolved data by `book_no` and "latest camp", which could mix queue, consultation, and status data across older visits. This change adds explicit `camp_visit` scoping so queue state, consultation state, doctor assignment, medicine flow, and websocket updates all operate within the current camp cycle.

## What does this MR do and why?
The motivation was to stop stale or cross-camp data from leaking into active workflows. The main issue was that queue records did not have a direct `camp_visit` reference, so downstream services had to infer the correct visit from `book_no` and camp ordering.

The approach taken was:
- add `camp_visit_id` to `consultation_queue`
- backfill existing rows through an Alembic migration
- introduce a shared `current_camp_service` as the single source of truth for current camp / current patient visit resolution
- refactor queue, patient, doctor, medicine, consultation, and websocket flows to use that shared scope

Trade-offs:
- queries now use a few more joins for correctness
- the migration intentionally fails if legacy queue rows cannot be backfilled safely
- current-cycle restrictions are stricter, so stale historical operations that used to slip through are now blocked

## Changes Made
- Added `app/services/current_camp_service.py`
- Added `alembic/versions/9f2b7c4d8e11_add_camp_visit_scope_to_consultation_queue.py`
- Updated `app/models/consultation_queue.py` to store `camp_visit_id`
- Refactored `consultation_queue_service`, `patient_consultation_service`, `patient_service`, `doctor_service`, and `medicine_service` to use current-camp/current-visit resolution
- Updated patient, doctor, consultation queue, consultation, and medicine routes to use the new service behavior
- Updated websocket queue broadcasters to emit current-camp-scoped data
- Updated API, service, websocket, and unit tests to match the new behavior

## Technical Details
Root cause:
- active workflow records were identified indirectly by `book_no` and latest-camp lookups
- this allowed queue and consultation state from older camp visits to affect current workflows

How this fix addresses it:
- `consultation_queue` now has a non-null `camp_visit_id` foreign key plus index
- migration backfills existing rows by matching patient + camp visit history based on queue join date
- shared helpers now resolve:
  - current camp
  - current patient camp visit
  - current patient visit details
  - current patient queue record
- queue numbering, active queue lookup, doctor queue lookup, wait-time calculation, patient status aggregation, and websocket broadcasts are now scoped by `camp_visit`
- consultation reassignment and prescription modification are limited to the current consultation cycle only

Architecture / schema notes:
- New helper service centralizes camp-scoping rules and removes repeated "latest camp" query logic
- Migration is reversible and includes downgrade support
- `consultation_queue.camp_visit_id` is indexed and enforced with a foreign key

## Type of Change
- [x] 🐛 Bug fix (non-breaking change that fixes an issue)
- [ ] ✨ New feature (non-breaking change that adds functionality)
- [ ] 💥 Breaking change (fix or feature that would cause existing functionality to change)
- [ ] 📝 Documentation update
- [x] ♻️ Refactor (no functional changes)
- [ ] ⚡ Performance improvement
- [x] 🧪 Test update
- [ ] 🔧 Configuration change
- [ ] 🚨 Security fix
- [ ] 🗑️ Deprecation (removing deprecated code)

## Related Issues / References
- Closes #<issue-id>
- Related to #<issue-id>

## Screenshots or Screen Recordings
Backend-only change.
Suggested attachments if needed:
- `/docs` screenshots for affected endpoints
- pre-commit / test output
- sample API responses before vs after scoping fix

## How to Validate Locally
1. Apply the migration:
   `docker compose exec api uv run alembic upgrade head`
2. Optionally reseed local data:
   `docker compose exec api uv run python -m scripts.seed_database --clear`
3. Create or use data with multiple medical camps and the same patient across more than one camp visit.
4. Trigger queue / consultation activity in an older camp and in the current camp.
5. Verify the current camp flows now only return or update records for the current `camp_visit`.
6. Verify patient status only reflects the current cycle.
7. Verify doctor reassignment is allowed before consultation starts or after cycle completion, but blocked during active consultation.
8. Verify doctor unregister checks only current-camp assigned patients.
9. Verify websocket queue updates reflect only current-camp queue state.

Previous behavior:
- stale queue / consultation / status data could be resolved from older camp visits

New behavior:
- workflow state is consistently resolved against the current camp visit only

## Testing Done
- Updated unit, service, API, and websocket tests for current-camp scoping
- Fixed failing expectations surfaced by the pre-commit test suite
- Full pre-commit hook chain passed during commit

Test commands run:
```bash
uv run ruff check .
uv run ruff format . --check
uv run pytest tests/test_api/test_patient_routes.py::TestPatientRoutes::test_get_patient_status_includes_consultation_completed_from_queue tests/test_services/test_doctor_service.py::TestUnregisterDoctorForCamp::test_unregister_doctor_for_camp_success
uv run pytest -n auto --cov=app --cov-report=term-missing --cov-fail-under=60

Code Quality Checklist

  • Code follows project conventions and passes formatting/lint checks
  • No debug statements or commented-out code left behind
  • Database migration added and reversible
  • Data integrity maintained with FK + index on camp_visit_id
  • API/service behavior covered by updated tests
  • Security/static checks passed (bandit, mypy, vulture, pytest coverage)
  • No raw SQL queries Note: the migration uses targeted SQL for backfill, which is intentional

Known Limitations / Technical Debt

  • Backfill logic depends on historical patient/camp-visit relationships being present and consistent
  • "Current camp" is still derived by date rules; if business needs manual camp selection later, that should be handled in a follow-up
  • Legacy/orphaned queue rows will fail migration rather than being silently guessed

Additional Notes

  • This MR is primarily a correctness fix for multi-camp workflow handling
  • The biggest behavioral change is stricter current-cycle enforcement for reassignment and prescription updates
  • Reviewers should focus on migration safety and current-camp resolution rules

MR Acceptance Checklist

  • Code works as intended and solves the stated problem
  • No known regressions in covered flows
  • Edge cases around stale queue/cycle access are handled
  • Code is readable and follows existing patterns
  • Code is testable and well-tested
  • Reviewed by at least 1 teammate
  • Reviewed by product owner
Edited by Lakshy Yarlagadda

Merge request reports

Loading