-
Notifications
You must be signed in to change notification settings - Fork 542
feat: add availability status to FacilityLocation #3469
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
📝 WalkthroughWalkthroughIntroduces a new Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@care/emr/api/viewsets/location.py`:
- Around line 360-367: The lock in perform_destroy is acquired after calling
super().perform_destroy, allowing a race where a concurrent create/update can
change location.availability_status and be clobbered; move the
Lock(f"facility_location:{instance.location.id}") context to wrap the deletion
and the subsequent update/reset so that super().perform_destroy(instance),
setting instance.location.availability_status to available,
instance.location.save(update_fields=["availability_status"]), and
self.reset_encounter_location_association(instance.location) all execute while
holding the same lock (referencing perform_destroy, Lock, and
reset_encounter_location_association to locate the code).
In `@care/emr/models/location.py`:
- Around line 12-16: Update the FacilityLocation model to match migration 0057
by adding the missing default for availability_status: modify the
availability_status field on class FacilityLocation (in models.location) to
include default='available' so new instances get the same default as the
migration; ensure any model imports or validations remain unchanged and run
makemigrations/migrate if needed to keep model state in sync with migration
0057.
🧹 Nitpick comments (1)
care/emr/api/viewsets/location.py (1)
337-345: Unify availability mapping so create/update can’t drift.
Right nowcompleted → availableis only applied in update. If someone ever creates a completed encounter, you’ll storecompletedinstead ofavailable. A tiny helper keeps this consistent and future-proof.♻️ Suggested refactor
class FacilityLocationEncounterViewSet(EMRModelViewSet): + def _to_availability_status(self, status): + if status == LocationEncounterAvailabilityStatusChoices.completed.value: + return LocationEncounterAvailabilityStatusChoices.available.value + return status + def perform_create(self, instance): location = self.get_location_obj() with Lock(f"facility_location:{location.id}"): instance.location = location self._validate_data(instance) super().perform_create(instance) - location.availability_status = instance.status + location.availability_status = self._to_availability_status(instance.status) location.save(update_fields=["availability_status"]) self.reset_encounter_location_association(location) def perform_update(self, instance): location = self.get_location_obj() with Lock(f"facility_location:{location.id}"): # Keep in mind that instance here is an ORM instance and not pydantic self._validate_data(instance, self.get_object()) super().perform_update(instance) - status = instance.status - if status == LocationEncounterAvailabilityStatusChoices.completed.value: - status = LocationEncounterAvailabilityStatusChoices.available.value - location.availability_status = status + location.availability_status = self._to_availability_status(instance.status) location.save(update_fields=["availability_status"]) self.reset_encounter_location_association(location)Also applies to: 347-357
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
care/emr/api/viewsets/location.pycare/emr/management/commands/load_fixtures.pycare/emr/migrations/0057_facilitylocation_availability_status.pycare/emr/models/location.pycare/emr/resources/location/spec.pycare/emr/tests/test_location_api.py
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py
📄 CodeRabbit inference engine (.cursorrules)
**/*.py: Prioritize readability and maintainability; follow Django's coding style guide (PEP 8 compliance).
Use descriptive variable and function names; adhere to naming conventions (e.g., lowercase with underscores for functions and variables).
Files:
care/emr/api/viewsets/location.pycare/emr/models/location.pycare/emr/tests/test_location_api.pycare/emr/resources/location/spec.pycare/emr/management/commands/load_fixtures.pycare/emr/migrations/0057_facilitylocation_availability_status.py
**/{models,views,management/commands}/*.py
📄 CodeRabbit inference engine (.cursorrules)
Leverage Django’s ORM for database interactions; avoid raw SQL queries unless necessary for performance.
Files:
care/emr/models/location.pycare/emr/management/commands/load_fixtures.py
**/tests/**/*.py
📄 CodeRabbit inference engine (.cursorrules)
Use Django’s built-in tools for testing (unittest and pytest-django) to ensure code quality and reliability.
Files:
care/emr/tests/test_location_api.py
🧠 Learnings (2)
📚 Learning: 2025-07-08T13:27:05.363Z
Learnt from: NikhilA8606
Repo: ohcnetwork/care PR: 3124
File: care/emr/resources/medication/administration/spec.py:163-164
Timestamp: 2025-07-08T13:27:05.363Z
Learning: In the care codebase, all EMR models inherit from EMRBaseModel which inherits from BaseModel (in care/utils/models/base.py). BaseModel provides common fields including created_date and modified_date with auto_now_add=True and auto_now=True respectively. These fields are available on all EMR models through inheritance.
Applied to files:
care/emr/models/location.py
📚 Learning: 2024-11-28T06:16:31.373Z
Learnt from: DraKen0009
Repo: ohcnetwork/care PR: 2620
File: care/facility/models/facility.py:306-311
Timestamp: 2024-11-28T06:16:31.373Z
Learning: In the 'care' project, moving imports of models like `Asset`, `AssetLocation`, `FacilityDefaultAssetLocation`, and `PatientSample` to the module level in `care/facility/models/facility.py` causes circular imports. Therefore, it's acceptable to keep these imports inside the `delete` method of the `Facility` class.
Applied to files:
care/emr/tests/test_location_api.py
🧬 Code graph analysis (3)
care/emr/api/viewsets/location.py (3)
care/emr/resources/location/spec.py (1)
LocationEncounterAvailabilityStatusChoices(13-18)care/emr/api/viewsets/base.py (1)
perform_destroy(250-252)care/utils/lock.py (1)
Lock(12-30)
care/emr/tests/test_location_api.py (1)
care/emr/models/location.py (1)
FacilityLocation(12-128)
care/emr/migrations/0057_facilitylocation_availability_status.py (2)
care/emr/models/location.py (1)
FacilityLocation(12-128)care/emr/api/viewsets/location.py (1)
filter(40-45)
🪛 Ruff (0.14.11)
care/emr/migrations/0057_facilitylocation_availability_status.py
6-6: Unused function argument: schema_editor
(ARG001)
13-15: Mutable class attributes should be annotated with typing.ClassVar
(RUF012)
17-25: Mutable class attributes should be annotated with typing.ClassVar
(RUF012)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Test / Test
🔇 Additional comments (12)
care/emr/management/commands/load_fixtures.py (1)
623-632: Operational status update looks fine.
Assuming"U"is the intended default operational status, this change is consistent and harmless.care/emr/resources/location/spec.py (2)
13-18: Enum extension is clean and consistent.
Addingavailablefits the lifecycle states and keeps the enum cohesive.
117-122: Typed availability_status is a good tighten-up.
The list spec now reflects the real domain values instead of a plain string.care/emr/migrations/0057_facilitylocation_availability_status.py (1)
6-25: Migration logic is straightforward and sane.
Backfillingactivefor rows withcurrent_encounteris the right move.care/emr/api/viewsets/location.py (1)
480-486: Close flow update is solid.
Settingavailability_statustoavailablealongside clearingcurrent_encounteris exactly what you want here.care/emr/tests/test_location_api.py (7)
773-777: Good, we actually verify the location flips to active now.
This assertion pins the new availability_status behavior after create.
794-799: Nice to see the state asserted before the conflict path.
Covers both current_encounter and availability_status.
829-833: Yep, this makes the overlap test actually prove the location is active.
869-873: Good—precondition is now explicit instead of implied.
1046-1050: Solid post-delete verification for availability_status.
1108-1112: Good coverage of the completed → available transition.
1189-1194: Finally checking both current_encounter and availability_status—nice.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
| def perform_destroy(self, instance): | ||
| super().perform_destroy(instance) | ||
| self.reset_encounter_location_association(instance.location) | ||
| with Lock(f"facility_location:{instance.location.id}"): | ||
| instance.location.availability_status = ( | ||
| LocationEncounterAvailabilityStatusChoices.available.value | ||
| ) | ||
| instance.location.save(update_fields=["availability_status"]) | ||
| self.reset_encounter_location_association(instance.location) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lock acquisition happens too late in destroy.
Because the lock is taken after deletion, a concurrent create/update can set availability_status and then get overwritten by this destroy path. Wrap the delete + update in the same lock.
🔒 Proposed fix
def perform_destroy(self, instance):
- super().perform_destroy(instance)
- with Lock(f"facility_location:{instance.location.id}"):
+ with Lock(f"facility_location:{instance.location.id}"):
+ super().perform_destroy(instance)
instance.location.availability_status = (
LocationEncounterAvailabilityStatusChoices.available.value
)
instance.location.save(update_fields=["availability_status"])
self.reset_encounter_location_association(instance.location)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| def perform_destroy(self, instance): | |
| super().perform_destroy(instance) | |
| self.reset_encounter_location_association(instance.location) | |
| with Lock(f"facility_location:{instance.location.id}"): | |
| instance.location.availability_status = ( | |
| LocationEncounterAvailabilityStatusChoices.available.value | |
| ) | |
| instance.location.save(update_fields=["availability_status"]) | |
| self.reset_encounter_location_association(instance.location) | |
| def perform_destroy(self, instance): | |
| with Lock(f"facility_location:{instance.location.id}"): | |
| super().perform_destroy(instance) | |
| instance.location.availability_status = ( | |
| LocationEncounterAvailabilityStatusChoices.available.value | |
| ) | |
| instance.location.save(update_fields=["availability_status"]) | |
| self.reset_encounter_location_association(instance.location) |
🤖 Prompt for AI Agents
In `@care/emr/api/viewsets/location.py` around lines 360 - 367, The lock in
perform_destroy is acquired after calling super().perform_destroy, allowing a
race where a concurrent create/update can change location.availability_status
and be clobbered; move the Lock(f"facility_location:{instance.location.id}")
context to wrap the deletion and the subsequent update/reset so that
super().perform_destroy(instance), setting instance.location.availability_status
to available, instance.location.save(update_fields=["availability_status"]), and
self.reset_encounter_location_association(instance.location) all execute while
holding the same lock (referencing perform_destroy, Lock, and
reset_encounter_location_association to locate the code).
| class FacilityLocation(EMRBaseModel): | ||
| status = models.CharField(max_length=255) | ||
| operational_status = models.CharField(max_length=255) | ||
| availability_status = models.CharField(max_length=255) | ||
| name = models.CharField(max_length=255) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# Find the location.py file
fd -t f "location.py" --path "*emr/models*"Repository: ohcnetwork/care
Length of output: 292
🏁 Script executed:
# Check the actual model definition
cat -n care/emr/models/location.py | head -30Repository: ohcnetwork/care
Length of output: 1560
🏁 Script executed:
# Find migration files related to this change
fd -t f "*.py" --path "*migrations*" | grep -E "(location|facility)" | head -20Repository: ohcnetwork/care
Length of output: 292
🏁 Script executed:
# Find migration files - corrected syntax
fd -t f "\.py$" care/emr/migrations | head -30Repository: ohcnetwork/care
Length of output: 1825
🏁 Script executed:
# Search for migrations related to FacilityLocation
rg "availability_status" care/emr/migrations/ -A 3 -B 3Repository: ohcnetwork/care
Length of output: 5469
🏁 Script executed:
# Check if there are any recent changes to this file
git log --oneline care/emr/models/location.py | head -10Repository: ohcnetwork/care
Length of output: 117
🏁 Script executed:
# Look at the full model to understand the context better
cat -n care/emr/models/location.py | tail -20Repository: ohcnetwork/care
Length of output: 889
Add default value to match migration 0057.
The availability_status field definition is missing the default='available' that migration 0057 specifies. Without it, object creation will fail unless the field is explicitly set—fun times debugging that later.
🩹 Proposed fix
- availability_status = models.CharField(max_length=255)
+ availability_status = models.CharField(max_length=255, default="available")📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| class FacilityLocation(EMRBaseModel): | |
| status = models.CharField(max_length=255) | |
| operational_status = models.CharField(max_length=255) | |
| availability_status = models.CharField(max_length=255) | |
| name = models.CharField(max_length=255) | |
| class FacilityLocation(EMRBaseModel): | |
| status = models.CharField(max_length=255) | |
| operational_status = models.CharField(max_length=255) | |
| availability_status = models.CharField(max_length=255, default="available") | |
| name = models.CharField(max_length=255) |
🤖 Prompt for AI Agents
In `@care/emr/models/location.py` around lines 12 - 16, Update the
FacilityLocation model to match migration 0057 by adding the missing default for
availability_status: modify the availability_status field on class
FacilityLocation (in models.location) to include default='available' so new
instances get the same default as the migration; ensure any model imports or
validations remain unchanged and run makemigrations/migrate if needed to keep
model state in sync with migration 0057.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #3469 +/- ##
===========================================
+ Coverage 74.82% 74.84% +0.01%
===========================================
Files 465 465
Lines 21115 21127 +12
Branches 2183 2184 +1
===========================================
+ Hits 15800 15812 +12
Misses 4835 4835
Partials 480 480 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| class LocationEncounterAvailabilityStatusChoices(str, Enum): | ||
| available = "available" | ||
| planned = "planned" | ||
| active = "active" | ||
| reserved = "reserved" | ||
| active = "active" | ||
| completed = "completed" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Proposed Changes
Merge Checklist
/docsOnly PR's with test cases included and passing lint and test pipelines will be reviewed
@ohcnetwork/care-backend-maintainers @ohcnetwork/care-backend-admins
Summary by CodeRabbit
Release Notes
✏️ Tip: You can customize this high-level summary in your review settings.