Skip to content

Commit c07e80f

Browse files
committed
feat(dref): update dref status
1 parent 197783f commit c07e80f

File tree

4 files changed

+116
-38
lines changed

4 files changed

+116
-38
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Generated by Django 4.2.19 on 2025-09-10 08:48
2+
3+
from django.db import migrations, models
4+
5+
6+
def update_status(apps, schema_editor):
7+
"""
8+
Update old status to new workflow:
9+
- Not published or IN_PROGRESS → DRAFT
10+
- Published or COMPLETED → APPROVED
11+
Applies to Dref, DrefOperationalUpdate, and DrefFinalReport.
12+
"""
13+
models = [
14+
"Dref",
15+
"DrefOperationalUpdate",
16+
"DrefFinalReport",
17+
]
18+
for model_name in models:
19+
Model = apps.get_model("dref", model_name)
20+
Model.objects.filter(is_published=True).update(status=4) # Published → APPROVED
21+
Model.objects.filter(is_published=False).update(status=1) # Not published → DRAFT
22+
23+
24+
class Migration(migrations.Migration):
25+
26+
dependencies = [
27+
("dref", "0083_dreffinalreport_total_operation_timeframe_imminent"),
28+
]
29+
30+
operations = [
31+
migrations.AlterField(
32+
model_name="dref",
33+
name="status",
34+
field=models.IntegerField(
35+
blank=True,
36+
choices=[(1, "Draft"), (2, "Finalizing"), (3, "Finalized"), (4, "Approved")],
37+
null=True,
38+
verbose_name="status",
39+
),
40+
),
41+
migrations.AlterField(
42+
model_name="dreffinalreport",
43+
name="status",
44+
field=models.IntegerField(
45+
blank=True,
46+
choices=[(1, "Draft"), (2, "Finalizing"), (3, "Finalized"), (4, "Approved")],
47+
null=True,
48+
verbose_name="status",
49+
),
50+
),
51+
migrations.AlterField(
52+
model_name="drefoperationalupdate",
53+
name="status",
54+
field=models.IntegerField(
55+
blank=True,
56+
choices=[(1, "Draft"), (2, "Finalizing"), (3, "Finalized"), (4, "Approved")],
57+
null=True,
58+
verbose_name="status",
59+
),
60+
),
61+
migrations.RunPython(update_status, reverse_code=migrations.RunPython.noop),
62+
]

dref/models.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -264,8 +264,13 @@ class DisasterCategory(models.IntegerChoices):
264264
RED = 2, _("Red")
265265

266266
class Status(models.IntegerChoices):
267-
IN_PROGRESS = 0, _("In Progress")
268-
COMPLETED = 1, _("Completed")
267+
# NOTE: Updated statuses for clarity:
268+
# IN_PROGRESS → DRAFT, COMPLETED → APPROVED.
269+
# Added FINALIZING and FINALIZED for translation/finalization.
270+
DRAFT = 1, _("Draft")
271+
FINALIZING = 2, _("Finalizing")
272+
FINALIZED = 3, _("Finalized")
273+
APPROVED = 4, _("Approved")
269274

270275
created_at = models.DateTimeField(verbose_name=_("created at"), auto_now_add=True)
271276
modified_at = models.DateTimeField(verbose_name=_("modified at"), default=timezone.now, null=True)
@@ -748,8 +753,6 @@ def save(self, *args, **kwargs):
748753
self.budget_file_preview.save(filename, thumb_data, save=False)
749754
else:
750755
raise ValidationError({"budget_file": "Sorry cannot generate preview for empty pdf"})
751-
752-
self.status = Dref.Status.COMPLETED if self.is_published else Dref.Status.IN_PROGRESS
753756
self.__budget_file_id = self.budget_file_id
754757
super().save(*args, **kwargs)
755758

@@ -1232,7 +1235,6 @@ def save(self, *args, **kwargs):
12321235
raise ValidationError({"budget_file": "Sorry cannot generate preview for empty pdf"})
12331236

12341237
self.__budget_file_id = self.budget_file_id
1235-
self.status = Dref.Status.COMPLETED if self.is_published else Dref.Status.IN_PROGRESS
12361238
super().save(*args, **kwargs)
12371239

12381240
@staticmethod
@@ -1651,7 +1653,6 @@ def save(self, *args, **kwargs):
16511653
else:
16521654
raise ValidationError({"financial_report": "Sorry cannot generate preview for empty pdf"})
16531655

1654-
self.status = Dref.Status.COMPLETED if self.is_published else Dref.Status.IN_PROGRESS
16551656
self.__financial_report_id = self.financial_report_id
16561657
super().save(*args, **kwargs)
16571658

dref/test_views.py

Lines changed: 37 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ def test_post_dref_creation(self, send_notification):
121121
"title": "Dref test title",
122122
"type_of_onset": Dref.OnsetType.SLOW.value,
123123
"disaster_category": Dref.DisasterCategory.YELLOW.value,
124-
"status": Dref.Status.IN_PROGRESS.value,
124+
"status": Dref.Status.DRAFT.value,
125125
"num_assisted": 5666,
126126
"num_affected": 23,
127127
"amount_requested": 127771111,
@@ -237,7 +237,7 @@ def test_event_date_in_dref(self):
237237
"title": "Dref test title",
238238
"type_of_onset": Dref.OnsetType.SLOW.value,
239239
"disaster_category": Dref.DisasterCategory.YELLOW.value,
240-
"status": Dref.Status.IN_PROGRESS.value,
240+
"status": Dref.Status.DRAFT.value,
241241
"national_society": national_society.id,
242242
"num_assisted": 5666,
243243
"num_affected": 23,
@@ -356,27 +356,27 @@ def test_filter_dref_status(self):
356356
"""
357357
Test to filter dref status
358358
"""
359-
DrefFactory.create(title="test", status=Dref.Status.COMPLETED, date_of_approval="2020-10-10", created_by=self.user)
360-
DrefFactory.create(status=Dref.Status.COMPLETED, date_of_approval="2020-10-10", created_by=self.user)
361-
DrefFactory.create(status=Dref.Status.COMPLETED, date_of_approval="2020-10-10", created_by=self.user)
362-
DrefFactory.create(status=Dref.Status.IN_PROGRESS, created_by=self.user)
363-
DrefFactory.create(status=Dref.Status.IN_PROGRESS, created_by=self.user)
359+
DrefFactory.create(title="test", status=Dref.Status.APPROVED, date_of_approval="2020-10-10", created_by=self.user)
360+
DrefFactory.create(status=Dref.Status.APPROVED, date_of_approval="2020-10-10", created_by=self.user)
361+
DrefFactory.create(status=Dref.Status.APPROVED, date_of_approval="2020-10-10", created_by=self.user)
362+
DrefFactory.create(status=Dref.Status.DRAFT, created_by=self.user)
363+
DrefFactory.create(status=Dref.Status.DRAFT, created_by=self.user)
364364

365365
# filter by `In Progress`
366-
url = f"/api/v2/dref/?status={Dref.Status.IN_PROGRESS.value}"
366+
url = f"/api/v2/dref/?status={Dref.Status.DRAFT.value}"
367367
self.client.force_authenticate(self.user)
368368
response = self.client.get(url)
369369
self.assertEqual(response.status_code, 200)
370-
self.assertEqual(len(response.data["results"]), 5)
370+
self.assertEqual(len(response.data["results"]), 2)
371371

372372
def test_dref_country_filter(self):
373373
country1 = Country.objects.create(name="country1")
374374
country2 = Country.objects.create(name="country2")
375-
DrefFactory.create(title="test", status=Dref.Status.COMPLETED, created_by=self.user, country=country1)
376-
DrefFactory.create(status=Dref.Status.COMPLETED, created_by=self.user)
377-
DrefFactory.create(status=Dref.Status.COMPLETED, created_by=self.user, country=country2)
378-
DrefFactory.create(status=Dref.Status.IN_PROGRESS, created_by=self.user, country=country1)
379-
DrefFactory.create(status=Dref.Status.IN_PROGRESS, created_by=self.user)
375+
DrefFactory.create(title="test", status=Dref.Status.APPROVED, created_by=self.user, country=country1)
376+
DrefFactory.create(status=Dref.Status.APPROVED, created_by=self.user)
377+
DrefFactory.create(status=Dref.Status.APPROVED, created_by=self.user, country=country2)
378+
DrefFactory.create(status=Dref.Status.DRAFT, created_by=self.user, country=country1)
379+
DrefFactory.create(status=Dref.Status.DRAFT, created_by=self.user)
380380
url = f"/api/v2/dref/?country={country1.id}"
381381
self.client.force_authenticate(self.user)
382382
response = self.client.get(url)
@@ -386,9 +386,10 @@ def test_dref_country_filter(self):
386386
@mock.patch("django.utils.timezone.now")
387387
def test_dref_is_published(self, mock_now):
388388
"""
389-
Test for dref if is_published = True
389+
Test DREF publishing flow:
390+
- Can only publish when status=FINALIZED
391+
- Publishing sets status=APPROVED and is_published=True
390392
"""
391-
392393
initial_now = datetime.now()
393394
mock_now.return_value = initial_now
394395

@@ -397,9 +398,12 @@ def test_dref_is_published(self, mock_now):
397398
dref = DrefFactory.create(
398399
title="test",
399400
created_by=self.user,
401+
country=country,
402+
status=Dref.Status.DRAFT,
400403
is_published=False,
401404
type_of_dref=Dref.DrefType.IMMINENT,
402405
)
406+
# Normal PATCH
403407
url = f"/api/v2/dref/{dref.id}/"
404408
data = {
405409
"title": "New Update Title",
@@ -408,7 +412,6 @@ def test_dref_is_published(self, mock_now):
408412
self.client.force_authenticate(self.user)
409413
response = self.client.patch(url, data)
410414
self.assert_200(response)
411-
412415
# create new dref with is_published = False
413416
not_published_dref = DrefFactory.create(
414417
title="test",
@@ -424,23 +427,28 @@ def test_dref_is_published(self, mock_now):
424427
data["modified_at"] = initial_now - timedelta(seconds=10)
425428
response = self.client.patch(url, data)
426429
self.assert_400(response)
427-
428-
# test dref published endpoint
429-
url = f"/api/v2/dref/{not_published_dref.id}/publish/"
430+
# ---- Test publishing ----
431+
publish_url = f"/api/v2/dref/{dref.id}/publish/"
430432
data = {}
431-
self.client.force_authenticate(self.user)
432-
response = self.client.post(url, data)
433+
response = self.client.post(publish_url, data)
433434
self.assert_403(response)
434-
435-
# add permission to request user
435+
# Add permission to user
436436
self.dref_permission = Permission.objects.create(
437437
codename="dref_region_admin_0",
438438
content_type=ContentType.objects.get_for_model(Region),
439439
name="Dref Admin for 0",
440440
)
441441
self.user.user_permissions.add(self.dref_permission)
442-
response = self.client.post(url, data)
442+
# Try again while DRAFT. Should fail(not finalized yet)
443+
response = self.client.post(publish_url, data)
444+
self.assert_400(response)
445+
# Update status to FINALIZED, then publish should succeed
446+
dref.status = Dref.Status.FINALIZED
447+
dref.save(update_fields=["status"])
448+
response = self.client.post(publish_url, data)
449+
dref.refresh_from_db()
443450
self.assert_200(response)
451+
self.assertEqual(response.data["status"], Dref.Status.APPROVED)
444452
self.assertEqual(response.data["is_published"], True)
445453

446454
def test_dref_operation_update_create(self):
@@ -672,6 +680,7 @@ def test_final_report_update_once_published(self):
672680
dref=dref,
673681
country=country,
674682
type_of_dref=Dref.DrefType.RESPONSE,
683+
status=Dref.Status.FINALIZED,
675684
)
676685
final_report.users.set([user1])
677686
# try to publish this report
@@ -691,7 +700,7 @@ def test_final_report_update_once_published(self):
691700
response = self.client.post(url, data)
692701
self.assert_200(response)
693702
self.assertEqual(response.data["is_published"], True)
694-
703+
self.assertEqual(response.data["status"], Dref.Status.APPROVED)
695704
# now try to patch to the final report
696705
url = f"/api/v2/dref-final-report/{final_report.id}/"
697706
data = {
@@ -709,7 +718,7 @@ def test_dref_for_assessment_report(self):
709718
"type_of_onset": Dref.OnsetType.SLOW.value,
710719
"type_of_dref": Dref.DrefType.ASSESSMENT,
711720
"disaster_category": Dref.DisasterCategory.YELLOW.value,
712-
"status": Dref.Status.IN_PROGRESS.value,
721+
"status": Dref.Status.DRAFT.value,
713722
"num_assisted": 5666,
714723
"num_affected": 23,
715724
"amount_requested": 127771111,
@@ -1297,7 +1306,7 @@ def test_dref_imminent(self):
12971306
"type_of_onset": Dref.OnsetType.SUDDEN.value,
12981307
"type_of_dref": Dref.DrefType.IMMINENT,
12991308
"disaster_category": Dref.DisasterCategory.YELLOW.value,
1300-
"status": Dref.Status.IN_PROGRESS.value,
1309+
"status": Dref.Status.DRAFT.value,
13011310
"num_assisted": 5666,
13021311
"num_affected": 23,
13031312
"amount_requested": 127771111,

dref/views.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,11 @@ def get_queryset(self):
8888
)
8989
def get_published(self, request, pk=None, version=None):
9090
dref = self.get_object()
91+
if dref.status != Dref.Status.FINALIZED:
92+
raise serializers.ValidationError(gettext("Must be finalized before publishing."))
9193
dref.is_published = True
92-
dref.status = Dref.Status.COMPLETED
93-
dref.save(update_fields=["is_published", "status"])
94+
dref.status = Dref.Status.APPROVED
95+
dref.save(update_fields=["status", "is_published"])
9496
serializer = DrefSerializer(dref, context={"request": request})
9597
return response.Response(serializer.data)
9698

@@ -153,8 +155,10 @@ def get_queryset(self):
153155
)
154156
def get_published(self, request, pk=None, version=None):
155157
operational_update = self.get_object()
158+
if operational_update.status != Dref.Status.FINALIZED:
159+
raise serializers.ValidationError(gettext("Must be finalized before publishing."))
156160
operational_update.is_published = True
157-
operational_update.status = Dref.Status.COMPLETED
161+
operational_update.status = Dref.Status.APPROVED
158162
operational_update.save(update_fields=["is_published", "status"])
159163
serializer = DrefOperationalUpdateSerializer(operational_update, context={"request": request})
160164
return response.Response(serializer.data)
@@ -185,10 +189,12 @@ def get_queryset(self):
185189
)
186190
def get_published(self, request, pk=None, version=None):
187191
field_report = self.get_object()
192+
if field_report.status != Dref.Status.FINALIZED:
193+
raise serializers.ValidationError(gettext("Must be finalized before publishing."))
188194
if field_report.is_published:
189195
raise serializers.ValidationError(gettext("Final Report %s is already published" % field_report))
190196
field_report.is_published = True
191-
field_report.status = Dref.Status.COMPLETED
197+
field_report.status = Dref.Status.APPROVED
192198
field_report.save(update_fields=["is_published", "status"])
193199
field_report.dref.is_active = False
194200
field_report.date_of_approval = timezone.now().date()

0 commit comments

Comments
 (0)