Skip to content

Commit e82d4b3

Browse files
committed
feat(eap): update revise workflow with locked feature
- Update admin panel - Add test for edge cases
1 parent a32239a commit e82d4b3

5 files changed

Lines changed: 123 additions & 19 deletions

File tree

eap/admin.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,13 @@ class SimplifiedEAPAdmin(admin.ModelAdmin):
8585
"eap_registration__country__name",
8686
"eap_registration__disaster_type__name",
8787
)
88-
list_display = ("simplifed_eap_application", "version", "is_locked")
88+
list_display = ("simplifed_eap_application", "eap_registration", "version", "is_locked")
8989
autocomplete_fields = (
9090
"eap_registration",
9191
"created_by",
9292
"modified_by",
9393
"admin2",
94+
"partners",
9495
)
9596
readonly_fields = (
9697
"cover_image",
@@ -159,6 +160,7 @@ def get_queryset(self, request):
159160
)
160161
.prefetch_related(
161162
"admin2",
163+
"partners",
162164
"partner_contacts",
163165
)
164166
)
@@ -176,12 +178,13 @@ class FullEAPAdmin(admin.ModelAdmin):
176178
"eap_registration__country__name",
177179
"eap_registration__disaster_type__name",
178180
)
179-
list_display = ("eap_registration",)
181+
list_display = ("full_eap_application", "eap_registration", "version", "is_locked")
180182
autocomplete_fields = (
181183
"eap_registration",
182184
"created_by",
183185
"modified_by",
184186
"admin2",
187+
"partners",
185188
)
186189
readonly_fields = (
187190
"partner_contacts",
@@ -244,6 +247,9 @@ def regenerate_diff_pdf_file(self, request, queryset):
244247

245248
regenerate_diff_pdf_file.short_description = "Regenerate EAP diff PDF files for selected Full EAPs"
246249

250+
def full_eap_application(self, obj):
251+
return f"{obj.eap_registration.national_society.society_name} - {obj.eap_registration.disaster_type.name}"
252+
247253
def get_queryset(self, request):
248254
return (
249255
super()
@@ -258,6 +264,7 @@ def get_queryset(self, request):
258264
)
259265
.prefetch_related(
260266
"admin2",
267+
"partners",
261268
"partner_contacts",
262269
"key_actors",
263270
"risk_analysis_source_of_information",

eap/models.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1275,7 +1275,7 @@ class Meta:
12751275
]
12761276

12771277
def __str__(self):
1278-
return f"Simplified EAP for {self.eap_registration}- version:{self.version}"
1278+
return f"{self.eap_registration} (VERSION {self.version})"
12791279

12801280
def generate_snapshot(self):
12811281
"""
@@ -1291,6 +1291,7 @@ def generate_snapshot(self):
12911291
overrides={
12921292
"parent_id": self.id,
12931293
"version": self.version + 1,
1294+
"is_locked": False,
12941295
"created_by_id": self.created_by_id,
12951296
"modified_by_id": self.modified_by_id,
12961297
"review_checklist_file": None,
@@ -1823,7 +1824,7 @@ class Meta:
18231824
]
18241825

18251826
def __str__(self):
1826-
return f"Full EAP for {self.eap_registration}- version:{self.version}"
1827+
return f"{self.eap_registration} (VERSION {self.version})"
18271828

18281829
def generate_snapshot(self):
18291830
"""
@@ -1838,6 +1839,7 @@ def generate_snapshot(self):
18381839
overrides={
18391840
"parent_id": self.id,
18401841
"version": self.version + 1,
1842+
"is_locked": False,
18411843
"created_by_id": self.created_by_id,
18421844
"modified_by_id": self.modified_by_id,
18431845
"review_checklist_file": None,

eap/serializers.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -918,14 +918,19 @@ def _validate_status(self, validated_data: dict[str, typing.Any]) -> dict[str, t
918918
EAPRegistration.Status.UNDER_REVIEW,
919919
):
920920
if self.instance.get_eap_type_enum == EAPType.SIMPLIFIED_EAP:
921-
# NOTE: Generating PDF asynchronously
921+
self.instance.latest_simplified_eap.is_locked = True
922+
self.instance.latest_simplified_eap.save(update_fields=["is_locked"])
923+
# NOTE: Generating export PDF asynchronously
922924
transaction.on_commit(
923925
lambda: generate_export_eap_pdf.delay(
924926
eap_registration_id=self.instance.id,
925927
version=self.instance.latest_simplified_eap.version,
926928
)
927929
)
928930
else:
931+
self.instance.latest_full_eap.is_locked = True
932+
self.instance.latest_full_eap.save(update_fields=["is_locked"])
933+
# NOTE: Generate export PDF asynchronously
929934
transaction.on_commit(
930935
lambda: generate_export_eap_pdf.delay(
931936
eap_registration_id=self.instance.id,
@@ -1000,6 +1005,13 @@ def _validate_status(self, validated_data: dict[str, typing.Any]) -> dict[str, t
10001005

10011006
# Check latest EAP has NS Addressing Comments file uploaded
10021007
if self.instance.get_eap_type_enum == EAPType.SIMPLIFIED_EAP:
1008+
# NOTE: If EAP is locked, NS has to revise the EAP to address the comments before resubmitting.
1009+
if self.instance.latest_simplified_eap.is_locked:
1010+
raise serializers.ValidationError(
1011+
gettext("Cannot change status to %s, Please revise the EAP to address the comments.")
1012+
% EAPRegistration.Status(new_status).label
1013+
)
1014+
10031015
if not (self.instance.latest_simplified_eap and self.instance.latest_simplified_eap.updated_checklist_file):
10041016
raise serializers.ValidationError(
10051017
{
@@ -1010,6 +1022,10 @@ def _validate_status(self, validated_data: dict[str, typing.Any]) -> dict[str, t
10101022
},
10111023
)
10121024

1025+
# Lock the latest eap
1026+
self.instance.latest_simplified_eap.is_locked = True
1027+
self.instance.latest_simplified_eap.save(update_fields=["is_locked"])
1028+
10131029
# Generating PDFs asynchronously
10141030
transaction.on_commit(
10151031
lambda: group(
@@ -1025,6 +1041,12 @@ def _validate_status(self, validated_data: dict[str, typing.Any]) -> dict[str, t
10251041
)
10261042

10271043
else:
1044+
if self.instance.latest_full_eap.is_locked:
1045+
raise serializers.ValidationError(
1046+
gettext("Cannot change status to %s, Please revise the EAP to address the comments.")
1047+
% EAPRegistration.Status(new_status).label
1048+
)
1049+
10281050
if not (self.instance.latest_full_eap and self.instance.latest_full_eap.updated_checklist_file):
10291051
raise serializers.ValidationError(
10301052
{
@@ -1035,6 +1057,10 @@ def _validate_status(self, validated_data: dict[str, typing.Any]) -> dict[str, t
10351057
},
10361058
)
10371059

1060+
# Lock the latest full eap
1061+
self.instance.latest_full_eap.is_locked = True
1062+
self.instance.latest_full_eap.save(update_fields=["is_locked"])
1063+
10381064
# Generating PDFs asynchronously
10391065
transaction.on_commit(
10401066
lambda: group(

eap/test_views.py

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1274,6 +1274,8 @@ def test_status_transition(self):
12741274
response = self.client.post(self.url, data, format="json")
12751275
self.assertEqual(response.status_code, 200, response.data)
12761276
self.assertEqual(response.data["status"], EAPStatus.UNDER_REVIEW)
1277+
simplified_eap.refresh_from_db()
1278+
self.assertTrue(simplified_eap.is_locked)
12771279

12781280
# NOTE: Check if the NS can update after changing to UNDER_REVIEW
12791281
# FAILS: As simplified EAP is in UNDER_REVIEW, cannot update
@@ -1283,6 +1285,7 @@ def test_status_transition(self):
12831285
"readiness_budget": 5000,
12841286
"pre_positioning_budget": 5000,
12851287
"early_action_budget": 5000,
1288+
"modified_at": datetime.now(),
12861289
}
12871290
url = f"/api/v2/simplified-eap/{simplified_eap.id}/"
12881291
response = self.client.patch(url, update_data, format="json")
@@ -1321,12 +1324,25 @@ def test_status_transition(self):
13211324
self.eap_registration.latest_simplified_eap.review_checklist_file,
13221325
)
13231326

1327+
# Fails, As NS has to revise the EAP before transitioning to UNDER_REVIEW.
1328+
status_data = {
1329+
"status": EAPStatus.UNDER_REVIEW,
1330+
}
1331+
response = self.client.post(self.url, status_data)
1332+
self.assertEqual(response.status_code, 400, response.data)
1333+
1334+
# Fails, As User only can revise after NS_ADDRESSING_COMMENTS, cannot update directly as it's locked
1335+
update_data["modified_at"] = datetime.now()
1336+
response = self.client.patch(url, update_data, format="json")
1337+
self.assertEqual(response.status_code, 400, response.data)
1338+
13241339
# NOTE: NS revise which creates a snapshot of simplified eap
13251340
# updates the latest simplified eap with updated checklist file. So, there should be two snapshots of SimplifiedEAP now.
13261341
revise_url = f"/api/v2/simplified-eap/{simplified_eap.id}/revise/"
13271342
self.authenticate(self.country_admin)
13281343
response = self.client.post(revise_url)
1329-
self.assertEqual(response.status_code, 200)
1344+
self.assertEqual(response.status_code, 200, response.data)
1345+
self.assertFalse(response.data["is_locked"], response.data)
13301346

13311347
# NOTE: Check if snapshot is created or not
13321348
# First SimplifedEAP should be locked
@@ -1371,13 +1387,21 @@ def test_status_transition(self):
13711387
self.eap_registration.latest_simplified_eap.id,
13721388
second_snapshot.id,
13731389
)
1390+
# Second snapshot should not be locked.
1391+
self.assertFalse(second_snapshot.is_locked, "Latest snapshot shouldn't be locked."),
13741392

1375-
# NOTE: Cannot create another snapshot from locked snapshot
1393+
# NOTE: Cannot create revise as this eap has already been revised once.
13761394
revise_url = f"/api/v2/simplified-eap/{simplified_eap.id}/revise/"
13771395
self.authenticate(self.country_admin)
13781396
response = self.client.post(revise_url)
13791397
self.assertEqual(response.status_code, 400, response.data)
13801398

1399+
# NOTE: Cannot update in previous snapshot
1400+
url = f"/api/v2/simplified-eap/{simplified_eap.id}/"
1401+
update_data["modified_at"] = datetime.now()
1402+
response = self.client.patch(url, update_data, format="json")
1403+
self.assertEqual(response.status_code, 400, response.data)
1404+
13811405
# NOTE: Transition to UNDER_REVIEW
13821406
# NS_ADDRESSING_COMMENTS -> UNDER_REVIEW
13831407
data = {
@@ -1411,6 +1435,8 @@ def test_status_transition(self):
14111435
response = self.client.post(self.url, data, format="json")
14121436
self.assertEqual(response.status_code, 200, response.data)
14131437
self.assertEqual(response.data["status"], EAPStatus.UNDER_REVIEW)
1438+
second_snapshot.refresh_from_db()
1439+
self.assertTrue(second_snapshot.is_locked, "Should be locked after transition to UNDER_REVIEW.")
14141440

14151441
# AGAIN NOTE: Transition to NS_ADDRESSING_COMMENTS
14161442
# UNDER_REVIEW -> NS_ADDRESSING_COMMENTS
@@ -1433,10 +1459,23 @@ def test_status_transition(self):
14331459
self.assertEqual(response.status_code, 200, response.data)
14341460
self.assertEqual(response.data["status"], EAPStatus.NS_ADDRESSING_COMMENTS)
14351461

1462+
# Fails, As NS has to revise the EAP before transitioning to UNDER_REVIEW.
1463+
status_data = {
1464+
"status": EAPStatus.UNDER_REVIEW,
1465+
}
1466+
response = self.client.post(self.url, status_data)
1467+
self.assertEqual(response.status_code, 400, response.data)
1468+
1469+
# Cannot update as it is in NS_ADDRESSING_COMMENTS, only revise is allowed
1470+
update_data["modified_at"] = datetime.now()
1471+
response = self.client.patch(url, update_data, format="json")
1472+
self.assertEqual(response.status_code, 400, response.data)
1473+
14361474
revise_url = f"/api/v2/simplified-eap/{second_snapshot.id}/revise/"
14371475
self.authenticate(self.country_admin)
14381476
response = self.client.post(revise_url)
1439-
self.assertEqual(response.status_code, 200)
1477+
self.assertEqual(response.status_code, 200, response.data)
1478+
self.assertFalse(response.data["is_locked"])
14401479

14411480
# Check if three snapshots are created now
14421481
self.eap_registration.refresh_from_db()
@@ -1482,12 +1521,18 @@ def test_status_transition(self):
14821521
third_snapshot.id,
14831522
)
14841523

1485-
# NOTE: Cannot create another snapshot from locked snapshot
1524+
# NOTE: Cannot create another snapshot as this eap has already been revised
14861525
revise_url = f"/api/v2/simplified-eap/{second_snapshot.id}/revise/"
14871526
self.authenticate(self.country_admin)
14881527
response = self.client.post(revise_url)
14891528
self.assertEqual(response.status_code, 400, response.data)
14901529

1530+
# NOTE: Cannot update the previous snapshot
1531+
url = f"/api/v2/simplified-eap/{second_snapshot.id}/"
1532+
update_data["modified_at"] = datetime.now()
1533+
response = self.client.patch(url, update_data, format="json")
1534+
self.assertEqual(response.status_code, 400, response.data)
1535+
14911536
# NOTE: Again Transition to UNDER_REVIEW
14921537
# NS_ADDRESSING_COMMENTS -> UNDER_REVIEW
14931538
data = {
@@ -1556,6 +1601,13 @@ def test_status_transition(self):
15561601
self.assertEqual(response.status_code, 200, response.data)
15571602
self.assertEqual(response.data["status"], EAPStatus.NS_ADDRESSING_COMMENTS)
15581603

1604+
# Fails, As NS has to revise the EAP before transitioning to UNDER_REVIEW.
1605+
status_data = {
1606+
"status": EAPStatus.UNDER_REVIEW,
1607+
}
1608+
response = self.client.post(self.url, status_data)
1609+
self.assertEqual(response.status_code, 400, response.data)
1610+
15591611
revise_url = f"/api/v2/simplified-eap/{third_snapshot.id}/revise/"
15601612
self.authenticate(self.country_admin)
15611613
response = self.client.post(revise_url)
@@ -1706,7 +1758,7 @@ def test_status_transition(self):
17061758
# FAILS As simplified EAP is in PENDING_PFA, cannot updated
17071759
url = f"/api/v2/simplified-eap/{simplified_eap.id}/"
17081760
response = self.client.patch(url, update_data, format="json")
1709-
self.assertEqual(response.status_code, 400)
1761+
self.assertEqual(response.status_code, 400, response.data)
17101762

17111763
# NOTE: Transition to APPROVED
17121764
# PENDING_PFA -> APPROVED
@@ -1736,7 +1788,7 @@ def test_status_transition(self):
17361788
self.authenticate(self.country_admin)
17371789
url = f"/api/v2/simplified-eap/{simplified_eap.id}/"
17381790
response = self.client.patch(url, update_data, format="json")
1739-
self.assertEqual(response.status_code, 400)
1791+
self.assertEqual(response.status_code, 400, response.data)
17401792

17411793
@mock.patch("eap.serializers.generate_export_eap_pdf")
17421794
@mock.patch("eap.serializers.group")

eap/views.py

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -296,14 +296,23 @@ def revise(
296296
id: int,
297297
):
298298
simplified_eap_instance = self.get_object()
299-
if simplified_eap_instance.is_locked:
299+
if simplified_eap_instance.eap_registration.status != EAPStatus.NS_ADDRESSING_COMMENTS:
300300
return response.Response(
301-
{"detail": "This version is locked and cannot be revised again."},
301+
{"detail": f"Only EAPs with status {EAPStatus.NS_ADDRESSING_COMMENTS} can be revised."},
302302
status=status.HTTP_400_BAD_REQUEST,
303303
)
304-
if simplified_eap_instance.eap_registration.status != EAPStatus.NS_ADDRESSING_COMMENTS:
304+
305+
if simplified_eap_instance.is_locked is False:
305306
return response.Response(
306-
{"detail": f"Only EAPs with status {EAPStatus.NS_ADDRESSING_COMMENTS} can be revised."},
307+
{"detail": "EAP can only be revised when it is locked."},
308+
status=status.HTTP_400_BAD_REQUEST,
309+
)
310+
311+
# Check if new version of EAP can be created (i.e. if the current version is locked)
312+
eap_registration_instance = EAPRegistration.objects.filter(id=simplified_eap_instance.eap_registration_id).first()
313+
if eap_registration_instance and eap_registration_instance.latest_simplified_eap_id != simplified_eap_instance.id:
314+
return response.Response(
315+
{"detail": "A new version of the EAP has already been created. Please revise the latest version of the EAP."},
307316
status=status.HTTP_400_BAD_REQUEST,
308317
)
309318

@@ -409,15 +418,23 @@ def revise(
409418
id: int,
410419
):
411420
full_eap_instance = self.get_object()
412-
if full_eap_instance.is_locked:
421+
if full_eap_instance.eap_registration.status != EAPStatus.NS_ADDRESSING_COMMENTS:
413422
return response.Response(
414-
{"detail": "This version is locked and cannot be revised again."},
423+
{"detail": f"Only EAPs with status {EAPStatus.NS_ADDRESSING_COMMENTS} can be revised."},
415424
status=status.HTTP_400_BAD_REQUEST,
416425
)
417426

418-
if full_eap_instance.eap_registration.status != EAPStatus.NS_ADDRESSING_COMMENTS:
427+
if full_eap_instance.is_locked is False:
419428
return response.Response(
420-
{"detail": f"Only EAPs with status {EAPStatus.NS_ADDRESSING_COMMENTS} can be revised."},
429+
{"detail": "EAP can only be revised when it is locked."},
430+
status=status.HTTP_400_BAD_REQUEST,
431+
)
432+
433+
# Check if new version of EAP can be created (i.e. if the current version is locked)
434+
eap_registration_instance = EAPRegistration.objects.filter(id=full_eap_instance.eap_registration_id).first()
435+
if eap_registration_instance and eap_registration_instance.latest_full_eap_id != full_eap_instance.id:
436+
return response.Response(
437+
{"detail": "A new version of the EAP has already been created. Please revise the latest version of the EAP."},
421438
status=status.HTTP_400_BAD_REQUEST,
422439
)
423440

0 commit comments

Comments
 (0)