From 49a6a0f5955da5809599f94703b52184cbefd80a Mon Sep 17 00:00:00 2001
From: Sushil Tiwari <susiltiwari750@gmail.com>
Date: Tue, 10 Dec 2024 12:59:35 +0545
Subject: [PATCH] Update logics on latest-change-request api

---
 ...localunitchangerequest_options_and_more.py | 26 ++++++++
 ...ve_localunit_status_localunit_is_locked.py | 22 -------
 local_units/models.py                         |  4 ++
 local_units/serializers.py                    | 21 ++++++
 local_units/test_views.py                     | 20 +-----
 local_units/utils.py                          |  2 +-
 local_units/views.py                          | 64 +++++++++++--------
 7 files changed, 91 insertions(+), 68 deletions(-)
 create mode 100644 local_units/migrations/0020_alter_localunitchangerequest_options_and_more.py
 delete mode 100644 local_units/migrations/0020_remove_localunit_status_localunit_is_locked.py

diff --git a/local_units/migrations/0020_alter_localunitchangerequest_options_and_more.py b/local_units/migrations/0020_alter_localunitchangerequest_options_and_more.py
new file mode 100644
index 0000000000..8d6ce40592
--- /dev/null
+++ b/local_units/migrations/0020_alter_localunitchangerequest_options_and_more.py
@@ -0,0 +1,26 @@
+# Generated by Django 4.2.16 on 2024-12-10 09:09
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('local_units', '0019_alter_localunit_is_deprecated_alter_localunit_status_and_more'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='localunitchangerequest',
+            options={'ordering': ('id',)},
+        ),
+        migrations.RemoveField(
+            model_name='localunit',
+            name='status',
+        ),
+        migrations.AddField(
+            model_name='localunit',
+            name='is_locked',
+            field=models.BooleanField(default=False, verbose_name='Is locked?'),
+        ),
+    ]
diff --git a/local_units/migrations/0020_remove_localunit_status_localunit_is_locked.py b/local_units/migrations/0020_remove_localunit_status_localunit_is_locked.py
deleted file mode 100644
index 0543c57264..0000000000
--- a/local_units/migrations/0020_remove_localunit_status_localunit_is_locked.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# Generated by Django 4.2.16 on 2024-12-04 09:36
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ("local_units", "0019_alter_localunit_is_deprecated_alter_localunit_status_and_more"),
-    ]
-
-    operations = [
-        migrations.RemoveField(
-            model_name="localunit",
-            name="status",
-        ),
-        migrations.AddField(
-            model_name="localunit",
-            name="is_locked",
-            field=models.BooleanField(default=False, verbose_name="Is locked?"),
-        ),
-    ]
diff --git a/local_units/models.py b/local_units/models.py
index 8725da9caa..99ff42f355 100644
--- a/local_units/models.py
+++ b/local_units/models.py
@@ -404,7 +404,11 @@ class Validator(models.IntegerChoices):
     rejected_data = models.JSONField(verbose_name=_("Rejected data"), default=dict)
     rejected_reason = models.TextField(verbose_name=_("Rejected reason"), blank=True, null=True)
 
+    class Meta:
+        ordering = ("id",)
+
     def __str__(self):
+        # NOTE: N+1, make sure to use select_related
         branch_name = self.local_unit.local_branch_name or self.local_unit.english_branch_name
         return f"{branch_name}-Change Request-{self.id}"
 
diff --git a/local_units/serializers.py b/local_units/serializers.py
index 0e6861c684..1629e7d4cf 100644
--- a/local_units/serializers.py
+++ b/local_units/serializers.py
@@ -22,6 +22,7 @@
     HealthData,
     HospitalType,
     LocalUnit,
+    LocalUnitChangeRequest,
     LocalUnitLevel,
     LocalUnitType,
     PrimaryHCC,
@@ -529,3 +530,23 @@ class Meta:
 
 class RejectedReasonSerialzier(serializers.Serializer):
     reason = serializers.CharField(required=True)
+
+
+class LocalUnitChangeRequestSerializer(serializers.ModelSerializer):
+    local_unit_details = PrivateLocalUnitDetailSerializer(source="local_unit", read_only=True)
+    created_by_details = LocalUnitMiniUserSerializer(source="created_by", read_only=True)
+    status_details = serializers.CharField(source="get_status_display", read_only=True)
+    current_validator_details = serializers.CharField(source="get_current_validator_display", read_only=True)
+
+    class Meta:
+        model = LocalUnitChangeRequest
+        fields = (
+            "id",
+            "local_unit_details",
+            "status",
+            "status_details",
+            "current_validator",
+            "current_validator_details",
+            "created_by_details",
+            "previous_data",
+        )
diff --git a/local_units/test_views.py b/local_units/test_views.py
index 7b5695b3be..541c82939c 100644
--- a/local_units/test_views.py
+++ b/local_units/test_views.py
@@ -181,19 +181,6 @@ def test_get_updated_at_updated_by(self):
         self.assertIsNotNone(response.data["modified_at"])
         self.assertEqual(response.data["modified_by_details"]["id"], user_2.id)
 
-    def test_validate_local_units(self):
-        local_unit = LocalUnit.objects.all().first()
-        self.authenticate()
-        url = f"/api/v2/local-units/{local_unit.id}/validate/"
-        data = {}
-        response = self.client.post(url, data=data)
-        self.assert_403(response)
-
-        # authenticate with super user
-        self.client.force_authenticate(self.root_user)
-        response = self.client.post(url, data=data)
-        self.assert_200(response)
-
 
 class DelegationOfficeFactory(factory.django.DjangoModelFactory):
     location = Point(2.2, 3.3)
@@ -337,7 +324,6 @@ def test_create_local_unit_administrative(self):
         # Checking the request changes for the local unit is created or not
         request_change = LocalUnitChangeRequest.objects.all()
         self.assertEqual(request_change.count(), 1)
-        print(request_change.first().previous_data)
         self.assertEqual(request_change.first().previous_data["id"], response.data["id"])
 
     def test_create_update_local_unit_health(self):
@@ -598,7 +584,7 @@ def test_latest_changes(self):
         self.assert_200(response)
 
         # Checking the latest changes
-        response = self.client.post(f"/api/v2/local-units/{local_unit_id}/latest-changes/")
+        response = self.client.post(f"/api/v2/local-units/{local_unit_id}/latest-change-request/")
         self.assert_200(response)
-        self.assertEqual(response.data["local_branch_name"], previous_data["local_branch_name"])
-        self.assertEqual(response.data["english_branch_name"], previous_data["english_branch_name"])
+        self.assertEqual(response.data["previous_data"]["local_branch_name"], previous_data["local_branch_name"])
+        self.assertEqual(response.data["previous_data"]["english_branch_name"], previous_data["english_branch_name"])
diff --git a/local_units/utils.py b/local_units/utils.py
index 54edf16c97..5530dc94e7 100644
--- a/local_units/utils.py
+++ b/local_units/utils.py
@@ -1,4 +1,4 @@
-def format_local_unit(data: dict) -> dict:
+def get_local_unit_snapshot_data(data: dict) -> dict:
     """
     Formatting the json local unit data
     """
diff --git a/local_units/views.py b/local_units/views.py
index 5a6fa5925b..10d624dba4 100644
--- a/local_units/views.py
+++ b/local_units/views.py
@@ -29,6 +29,7 @@
 )
 from local_units.serializers import (
     DelegationOfficeSerializer,
+    LocalUnitChangeRequestSerializer,
     LocalUnitDetailSerializer,
     LocalUnitOptionsSerializer,
     LocalUnitSerializer,
@@ -36,7 +37,7 @@
     PrivateLocalUnitSerializer,
     RejectedReasonSerialzier,
 )
-from local_units.utils import format_local_unit
+from local_units.utils import get_local_unit_snapshot_data
 from main.permissions import DenyGuestUserPermission
 
 
@@ -67,12 +68,10 @@ def create(self, request, *args, **kwargs):
         serializer.is_valid(raise_exception=True)
         self.perform_create(serializer)
 
-        clean_data = format_local_unit(serializer.data)
-
         # Creating a new change request for the local unit
         LocalUnitChangeRequest.objects.create(
             local_unit=serializer.instance,
-            previous_data=clean_data,
+            previous_data=get_local_unit_snapshot_data(serializer.data),
             status=LocalUnitChangeRequest.Status.PENDING,
             triggered_by=request.user,
         )
@@ -92,12 +91,10 @@ def update(self, request, *args, **kwargs):
         serializer.is_valid(raise_exception=True)
         self.perform_update(serializer)
 
-        clean_data = format_local_unit(serializer.data)
-
         # Creating a new change request for the local unit
         LocalUnitChangeRequest.objects.create(
             local_unit=local_unit,
-            previous_data=clean_data,
+            previous_data=get_local_unit_snapshot_data(serializer.data),
             status=LocalUnitChangeRequest.Status.PENDING,
             triggered_by=request.user,
         )
@@ -114,17 +111,19 @@ def update(self, request, *args, **kwargs):
     def get_validate(self, request, pk=None, version=None):
         local_unit = self.get_object()
 
-        serializer = PrivateLocalUnitDetailSerializer(local_unit, context={"request": request})
-        clean_data = format_local_unit(serializer.data)
-
-        LocalUnitChangeRequest.objects.create(
+        # NOTE: Updating the change request with the approval status
+        change_request_instance = LocalUnitChangeRequest.objects.filter(
             local_unit=local_unit,
-            previous_data=clean_data,
-            status=LocalUnitChangeRequest.Status.APPROVED,
-            triggered_by=request.user,
-            updated_by=request.user,
-            updated_at=timezone.now(),
-        )
+            status=LocalUnitChangeRequest.Status.PENDING,
+        ).last()
+
+        if not change_request_instance:
+            return bad_request("No change request found to validate")
+
+        change_request_instance.status = LocalUnitChangeRequest.Status.APPROVED
+        change_request_instance.updated_by = request.user
+        change_request_instance.updated_at = timezone.now()
+        change_request_instance.save(update_fields=["status", "updated_by", "updated_at"])
 
         # Validate the local unit
         local_unit.validated = True
@@ -148,19 +147,26 @@ def get_revert(self, request, pk=None, version=None):
             return bad_request("Local unit is already validated and cannot be reverted")
 
         full_serializer = PrivateLocalUnitDetailSerializer(local_unit, context={"request": request})
-        clean_data = format_local_unit(full_serializer.data)
 
         serializer = RejectedReasonSerialzier(data=request.data)
         serializer.is_valid(raise_exception=True)
         reason = serializer.validated_data["reason"]
 
-        LocalUnitChangeRequest.objects.create(
+        # NOTE: Updating the change request with the rejection reason
+        change_request_instance = LocalUnitChangeRequest.objects.filter(
             local_unit=local_unit,
-            previous_data=clean_data,
-            rejected_reason=reason,
-            status=LocalUnitChangeRequest.Status.REVERT,
-            triggered_by=request.user,
-        )
+            status=LocalUnitChangeRequest.Status.PENDING,
+        ).last()
+
+        if not change_request_instance:
+            return bad_request("No change request found to revert")
+
+        change_request_instance.status = LocalUnitChangeRequest.Status.REVERT
+        change_request_instance.rejected_reason = reason
+        change_request_instance.updated_by = request.user
+        change_request_instance.updated_at = timezone.now()
+        change_request_instance.rejected_data = get_local_unit_snapshot_data(full_serializer.data)
+        change_request_instance.save(update_fields=["status", "rejected_reason", "updated_at", "updated_by", "rejected_data"])
 
         # Reverting the last change request related to this local unit
         last_change_request = LocalUnitChangeRequest.objects.filter(
@@ -174,6 +180,7 @@ def get_revert(self, request, pk=None, version=None):
         # NOTE: Unlocking the reverted local unit
         local_unit.is_locked = False
         local_unit.save(update_fields=["is_locked"])
+
         # reverting the previous data of change request to local unit by passing through serializer
         serializer = PrivateLocalUnitDetailSerializer(
             local_unit,
@@ -185,12 +192,12 @@ def get_revert(self, request, pk=None, version=None):
         self.perform_update(serializer)
         return response.Response(serializer.data)
 
-    @extend_schema(request=None, responses=PrivateLocalUnitDetailSerializer)
+    @extend_schema(request=None, responses=LocalUnitChangeRequestSerializer)
     @action(
         detail=True,
-        url_path="latest-changes",
+        url_path="latest-change-request",
         methods=["post"],
-        serializer_class=PrivateLocalUnitDetailSerializer,
+        serializer_class=LocalUnitChangeRequestSerializer,
         permission_classes=[permissions.IsAuthenticated, IsAuthenticatedForLocalUnit, DenyGuestUserPermission],
     )
     def get_latest_changes(self, request, pk=None, version=None):
@@ -204,7 +211,8 @@ def get_latest_changes(self, request, pk=None, version=None):
         if not change_request:
             return bad_request("Last change request not found")
 
-        return response.Response(change_request.previous_data)
+        serializer = LocalUnitChangeRequestSerializer(change_request, context={"request": request})
+        return response.Response(serializer.data)
 
 
 class LocalUnitViewSet(viewsets.ModelViewSet):