From 56759710ef8c17c48ffdb264136ae63d7c1aab84 Mon Sep 17 00:00:00 2001 From: Abhiuday Date: Fri, 17 Nov 2023 12:59:04 +0530 Subject: [PATCH] fix(asset_location): added custom through model --- care/facility/api/serializers/asset.py | 48 +++----------- care/facility/api/viewsets/asset.py | 41 ++++++++---- .../0393_assetlocation_duty_staff.py | 19 ------ ...etlocationdutystaff_assetlocation_users.py | 62 +++++++++++++++++++ care/facility/models/asset.py | 31 +++++++++- 5 files changed, 128 insertions(+), 73 deletions(-) delete mode 100644 care/facility/migrations/0393_assetlocation_duty_staff.py create mode 100644 care/facility/migrations/0393_assetlocationdutystaff_assetlocation_users.py diff --git a/care/facility/api/serializers/asset.py b/care/facility/api/serializers/asset.py index 2ccdd8fa85..e317eac532 100644 --- a/care/facility/api/serializers/asset.py +++ b/care/facility/api/serializers/asset.py @@ -2,7 +2,6 @@ from django.core.cache import cache from django.db import transaction -from django.db.models import Q from django.shortcuts import get_object_or_404 from django.utils.timezone import now from rest_framework import serializers @@ -33,7 +32,6 @@ UserAssignedSerializer, UserBaseMinimumSerializer, ) -from care.users.models import User from care.utils.assetintegration.hl7monitor import HL7MonitorAsset from care.utils.assetintegration.onvif import OnvifAsset from care.utils.assetintegration.ventilator import VentilatorAsset @@ -45,12 +43,11 @@ class AssetLocationSerializer(ModelSerializer): facility = FacilityBareMinimumSerializer(read_only=True) id = UUIDField(source="external_id", read_only=True) + users = serializers.SerializerMethodField() - duty_staff_objects = UserAssignedSerializer( - many=True, - read_only=True, - source="duty_staff", - ) + def get_users(self, obj): + users = obj.users.filter(assetlocationdutystaff__deleted=False) + return UserAssignedSerializer(users, many=True, read_only=True).data def validate_middleware_address(self, value): value = (value or "").strip() @@ -87,39 +84,6 @@ class Meta: read_only_fields = TIMESTAMP_FIELDS -class AssetLocationDutyStaffSerializer(ModelSerializer): - duty_staff_objects = UserAssignedSerializer( - many=True, - read_only=True, - source="duty_staff", - ) - - class Meta: - model = AssetLocation - fields = ( - "duty_staff_objects", - "duty_staff", - ) - - def validate_duty_staff(self, value): - if not value: - raise ValidationError({"duty_staff": "Duty staff cannot be empty"}) - - duty_staff_ids = [user.id for user in value] - facility_id = self.instance.facility.id - - count = User.objects.filter( - Q(home_facility__id=facility_id) & Q(id__in=duty_staff_ids) - ).count() - - if count != len(duty_staff_ids): - raise ValidationError( - {"duty_staff": "Only Home Facility Doctors and Staffs are allowed"} - ) - - return value - - class AssetBareMinimumSerializer(ModelSerializer): id = UUIDField(source="external_id", read_only=True) @@ -241,7 +205,9 @@ def create(self, validated_data): asset_instance = super().create(validated_data) if last_serviced_on or note: asset_service = AssetService( - asset=asset_instance, serviced_on=last_serviced_on, note=note + asset=asset_instance, + serviced_on=last_serviced_on, + note=note, ) asset_service.save() asset_instance.last_service = asset_service diff --git a/care/facility/api/viewsets/asset.py b/care/facility/api/viewsets/asset.py index 393b626867..399fffad13 100644 --- a/care/facility/api/viewsets/asset.py +++ b/care/facility/api/viewsets/asset.py @@ -1,5 +1,6 @@ from django.conf import settings from django.core.cache import cache +from django.db import transaction from django.db.models import Exists, OuterRef, Q from django.db.models.signals import post_save from django.dispatch import receiver @@ -30,7 +31,6 @@ from care.facility.api.serializers.asset import ( AssetAvailabilitySerializer, - AssetLocationDutyStaffSerializer, AssetLocationSerializer, AssetSerializer, AssetServiceSerializer, @@ -43,6 +43,7 @@ Asset, AssetAvailabilityRecord, AssetLocation, + AssetLocationDutyStaff, AssetService, AssetTransaction, ConsultationBedAsset, @@ -87,11 +88,6 @@ class AssetLocationViewSet( filter_backends = (drf_filters.SearchFilter,) search_fields = ["name"] - def get_serializer_class(self): - if self.action == "duty_staff": - return AssetLocationDutyStaffSerializer - return super().get_serializer_class() - def get_serializer_context(self): facility = self.get_facility() context = super().get_serializer_context() @@ -131,14 +127,35 @@ def duty_staff(self, request, facility_external_id, external_id): Endpoint for assigning staffs to asset location """ - asset = self.get_object() - serializer = AssetLocationDutyStaffSerializer(asset, data=request.data) - serializer.is_valid(raise_exception=True) + asset: AssetLocation = self.get_object() + duty_staff = request.data.get("duty_staff", []) + + count = User.objects.filter( + Q(home_facility=asset.facility) & Q(id__in=duty_staff) + ).count() + + if count != len(duty_staff): + raise ValidationError( + {"duty_staff": "Only Home Facility Doctors ayynd Staffs are allowed"} + ) + + AssetLocationDutyStaff.objects.filter(asset_location=asset).update(deleted=True) + + users = User.objects.filter(id__in=duty_staff) + with transaction.atomic(): + duty_staff_objects = [] + for user in users: + duty_staff_objects.append( + AssetLocationDutyStaff( + asset_location=asset, + user=user, + created_by=request.user, + ) + ) - staffs = serializer.validated_data["duty_staff"] - asset.duty_staff.set(staffs) + AssetLocationDutyStaff.objects.bulk_create(duty_staff_objects) - return Response(self.get_serializer(asset).data) + return Response(status=status.HTTP_201_CREATED) class AssetFilter(filters.FilterSet): diff --git a/care/facility/migrations/0393_assetlocation_duty_staff.py b/care/facility/migrations/0393_assetlocation_duty_staff.py deleted file mode 100644 index 2242bdb607..0000000000 --- a/care/facility/migrations/0393_assetlocation_duty_staff.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 4.2.5 on 2023-10-26 14:45 - -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ("facility", "0392_alter_dailyround_consciousness_level"), - ] - - operations = [ - migrations.AddField( - model_name="assetlocation", - name="duty_staff", - field=models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL), - ), - ] diff --git a/care/facility/migrations/0393_assetlocationdutystaff_assetlocation_users.py b/care/facility/migrations/0393_assetlocationdutystaff_assetlocation_users.py new file mode 100644 index 0000000000..b58a548534 --- /dev/null +++ b/care/facility/migrations/0393_assetlocationdutystaff_assetlocation_users.py @@ -0,0 +1,62 @@ +# Generated by Django 4.2.5 on 2023-11-17 06:36 + +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", "0392_alter_dailyround_consciousness_level"), + ] + + operations = [ + migrations.CreateModel( + name="AssetLocationDutyStaff", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("deleted", models.BooleanField(default=False)), + ( + "asset_location", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="facility.assetlocation", + ), + ), + ( + "created_by", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + related_name="assigned_duty_staff", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), + ], + ), + migrations.AddField( + model_name="assetlocation", + name="users", + field=models.ManyToManyField( + blank=True, + related_name="duty_staff", + through="facility.AssetLocationDutyStaff", + to=settings.AUTH_USER_MODEL, + ), + ), + ] diff --git a/care/facility/models/asset.py b/care/facility/models/asset.py index 2d63e54706..347a628f25 100644 --- a/care/facility/models/asset.py +++ b/care/facility/models/asset.py @@ -46,8 +46,11 @@ class RoomType(enum.Enum): Facility, on_delete=models.PROTECT, null=False, blank=False ) - duty_staff = models.ManyToManyField( + users = models.ManyToManyField( User, + through="AssetLocationDutyStaff", + related_name="duty_staff", + through_fields=("asset_location", "user"), blank=True, ) @@ -56,6 +59,32 @@ class RoomType(enum.Enum): ) +class AssetLocationDutyStaff(models.Model): + asset_location = models.ForeignKey( + AssetLocation, on_delete=models.CASCADE, null=False, blank=False + ) + user = models.ForeignKey(User, on_delete=models.CASCADE, null=False, blank=False) + + deleted = models.BooleanField(default=False) + + created_by = models.ForeignKey( + User, + on_delete=models.PROTECT, + null=False, + blank=False, + related_name="assigned_duty_staff", + ) + + def __str__(self): + return f"{self.user} under {self.asset_location.name}" + + CSV_MAPPING = { + "asset_location__name": "Asset Location Name", + "user__username": "User", + "created_by__username": "Assigned By", + } + + class AssetType(enum.Enum): INTERNAL = 50 EXTERNAL = 100