diff --git a/care/abdm/utils/fhir.py b/care/abdm/utils/fhir.py index c14b723ab1..214257bc02 100644 --- a/care/abdm/utils/fhir.py +++ b/care/abdm/utils/fhir.py @@ -88,7 +88,11 @@ def _practioner(self): id = str(uuid()) name = ( - self.consultation.verified_by + ( + self.consultation.verified_by + and f"{self.consultation.verified_by.first_name} {self.consultation.verified_by.last_name}" + ) + or self.consultation.deprecated_verified_by or f"{self.consultation.created_by.first_name} {self.consultation.created_by.last_name}" ) self._practitioner_profile = Practitioner( diff --git a/care/facility/api/serializers/patient_consultation.py b/care/facility/api/serializers/patient_consultation.py index 32a77ee55c..e5281bd55f 100644 --- a/care/facility/api/serializers/patient_consultation.py +++ b/care/facility/api/serializers/patient_consultation.py @@ -67,11 +67,15 @@ class PatientConsultationSerializer(serializers.ModelSerializer): facility = ExternalIdSerializerField(read_only=True) assigned_to_object = UserAssignedSerializer(source="assigned_to", read_only=True) - assigned_to = serializers.PrimaryKeyRelatedField( queryset=User.objects.all(), required=False, allow_null=True ) + verified_by_object = UserBaseMinimumSerializer(source="verified_by", read_only=True) + verified_by = serializers.PrimaryKeyRelatedField( + queryset=User.objects.all(), required=True, allow_null=False + ) + discharge_reason = serializers.ChoiceField( choices=DISCHARGE_REASON_CHOICES, read_only=True, required=False ) @@ -132,6 +136,7 @@ class Meta: "last_edited_by", "created_by", "kasp_enabled_date", + "deprecated_verified_by", ) exclude = ("deleted", "external_id") @@ -313,6 +318,20 @@ def validate(self, attrs): validated = super().validate(attrs) # TODO Add Bed Authorisation Validation + if not validated["verified_by"].user_type == User.TYPE_VALUE_MAP["Doctor"]: + raise ValidationError("Only Doctors can verify a Consultation") + + facility = ( + self.instance and self.instance.facility or validated["patient"].facility + ) + if ( + validated["verified_by"].home_facility + and validated["verified_by"].home_facility != facility + ): + raise ValidationError( + "Home Facility of the Doctor must be the same as the Consultation Facility" + ) + if "suggestion" in validated: if validated["suggestion"] is SuggestionChoices.R: if not validated.get("referred_to") and not validated.get( diff --git a/care/facility/api/viewsets/facility_users.py b/care/facility/api/viewsets/facility_users.py index 2d6ea85329..578f326849 100644 --- a/care/facility/api/viewsets/facility_users.py +++ b/care/facility/api/viewsets/facility_users.py @@ -1,6 +1,7 @@ from django.db.models import Prefetch from django_filters import rest_framework as filters from drf_spectacular.utils import extend_schema, extend_schema_view +from rest_framework import filters as drf_filters from rest_framework import mixins from rest_framework.exceptions import ValidationError from rest_framework.permissions import IsAuthenticated @@ -28,7 +29,11 @@ class FacilityUserViewSet(GenericViewSet, mixins.ListModelMixin): filterset_class = UserFilter queryset = User.objects.all() permission_classes = [IsAuthenticated] - filter_backends = [filters.DjangoFilterBackend] + filter_backends = [ + filters.DjangoFilterBackend, + drf_filters.SearchFilter, + ] + search_fields = ["first_name", "last_name", "username"] def get_queryset(self): try: diff --git a/care/facility/migrations/0384_patientconsultation_verified_by.py b/care/facility/migrations/0384_patientconsultation_verified_by.py new file mode 100644 index 0000000000..dbb85f5795 --- /dev/null +++ b/care/facility/migrations/0384_patientconsultation_verified_by.py @@ -0,0 +1,30 @@ +# Generated by Django 4.2.2 on 2023-08-31 04:31 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ("facility", "0383_patientconsultation_icd11_principal_diagnosis"), + ] + + operations = [ + migrations.RenameField( + model_name="patientconsultation", + old_name="verified_by", + new_name="deprecated_verified_by", + ), + migrations.AddField( + model_name="patientconsultation", + name="verified_by", + field=models.ForeignKey( + null=True, + blank=False, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), + ), + ] diff --git a/care/facility/models/patient_consultation.py b/care/facility/models/patient_consultation.py index bc259921b1..75f588d1f9 100644 --- a/care/facility/models/patient_consultation.py +++ b/care/facility/models/patient_consultation.py @@ -142,7 +142,10 @@ class PatientConsultation(PatientBaseModel, PatientRelatedPermissionMixin): related_name="patient_assigned_to", ) - verified_by = models.TextField(default="", null=True, blank=True) + deprecated_verified_by = models.TextField(default="", null=True, blank=True) + verified_by = models.ForeignKey( + User, on_delete=models.SET_NULL, null=True, blank=False + ) created_by = models.ForeignKey( User, on_delete=models.SET_NULL, null=True, related_name="created_user" diff --git a/care/facility/tests/test_patient_consultation_api.py b/care/facility/tests/test_patient_consultation_api.py index 48de90f695..c98ed783ac 100644 --- a/care/facility/tests/test_patient_consultation_api.py +++ b/care/facility/tests/test_patient_consultation_api.py @@ -7,7 +7,7 @@ from care.facility.api.viewsets.facility_users import FacilityUserViewSet from care.facility.api.viewsets.patient_consultation import PatientConsultationViewSet -from care.facility.models.facility import Facility +from care.facility.models import Facility, User from care.facility.models.patient_consultation import ( CATEGORY_CHOICES, PatientConsultation, @@ -58,17 +58,25 @@ def test_get_queryset_with_prefetching(self): class TestPatientConsultation(TestBase, TestClassMixin, APITestCase): - default_data = { - "symptoms": [1], - "category": CATEGORY_CHOICES[0][0], - "examination_details": "examination_details", - "history_of_present_illness": "history_of_present_illness", - "treatment_plan": "treatment_plan", - "suggestion": PatientConsultation.SUGGESTION_CHOICES[0][0], - } + def get_default_data(self): + return { + "symptoms": [1], + "category": CATEGORY_CHOICES[0][0], + "examination_details": "examination_details", + "history_of_present_illness": "history_of_present_illness", + "treatment_plan": "treatment_plan", + "suggestion": PatientConsultation.SUGGESTION_CHOICES[0][0], + "verified_by": self.doctor.id, + } def setUp(self): self.factory = APIRequestFactory() + self.doctor = self.create_user( + username="doctor1", + district=self.district, + user_type=User.TYPE_VALUE_MAP["Doctor"], + ) + self.consultation = self.create_consultation( suggestion="A", admission_date=make_aware(datetime.datetime(2020, 4, 1, 15, 30, 00)), @@ -76,7 +84,7 @@ def setUp(self): def create_admission_consultation(self, patient=None, **kwargs): patient = patient or self.create_patient(facility_id=self.facility.id) - data = self.default_data.copy() + data = self.get_default_data() kwargs.update( { "patient": patient.external_id, @@ -93,6 +101,15 @@ def create_admission_consultation(self, patient=None, **kwargs): ) return PatientConsultation.objects.get(external_id=res.data["id"]) + def update_consultation(self, consultation, **kwargs): + return self.new_request( + (self.get_url(consultation), kwargs, "json"), + {"patch": "partial_update"}, + PatientConsultationViewSet, + self.state_admin, + {"external_id": consultation.external_id}, + ) + def get_url(self, consultation=None): if consultation: return f"/api/v1/consultation/{consultation.external_id}" @@ -107,6 +124,12 @@ def discharge(self, consultation, **kwargs): {"external_id": consultation.external_id}, ) + def test_create_consultation_verified_by_invalid_user(self): + res = self.update_consultation( + self.consultation, verified_by=self.state_admin.id + ) + self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST) + def test_discharge_as_recovered_preadmission(self): consultation = self.create_admission_consultation( suggestion="A", diff --git a/care/templates/reports/patient_discharge_summary_pdf.html b/care/templates/reports/patient_discharge_summary_pdf.html index a2263c25a4..7e3b83e09b 100644 --- a/care/templates/reports/patient_discharge_summary_pdf.html +++ b/care/templates/reports/patient_discharge_summary_pdf.html @@ -945,7 +945,11 @@

Verified By
- {{consultation.verified_by|linebreaks}} + {% if consultation.verified_by %} + {{ consultation.verified_by.first_name }} {{ consultation.verified_by.last_name }} + {% else %} + - + {% endif %}
diff --git a/care/users/api/viewsets/users.py b/care/users/api/viewsets/users.py index bd0faa6ecb..9d590b546d 100644 --- a/care/users/api/viewsets/users.py +++ b/care/users/api/viewsets/users.py @@ -55,6 +55,9 @@ class UserFilterSet(filters.FilterSet): ) last_login = filters.DateFromToRangeFilter(field_name="last_login") district_id = filters.NumberFilter(field_name="district_id", lookup_expr="exact") + home_facility = filters.UUIDFilter( + field_name="home_facility__external_id", lookup_expr="exact" + ) def get_user_type( self, diff --git a/data/dummy/facility.json b/data/dummy/facility.json index 6f7cb4146c..c4a05f5ee4 100644 --- a/data/dummy/facility.json +++ b/data/dummy/facility.json @@ -199,7 +199,6 @@ "is_telemedicine": false, "last_updated_by_telemedicine": false, "assigned_to": null, - "verified_by": "", "created_by": 2, "last_edited_by": 2, "last_daily_round": null,