Skip to content

Commit 12849d6

Browse files
committed
feat(dref): update dref status
1 parent 4738b32 commit 12849d6

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,
@@ -487,7 +487,7 @@ def test_event_date_in_dref(self):
487487
"title": "Dref test title",
488488
"type_of_onset": Dref.OnsetType.SLOW.value,
489489
"disaster_category": Dref.DisasterCategory.YELLOW.value,
490-
"status": Dref.Status.IN_PROGRESS.value,
490+
"status": Dref.Status.DRAFT.value,
491491
"national_society": national_society.id,
492492
"num_assisted": 5666,
493493
"num_affected": 23,
@@ -606,27 +606,27 @@ def test_filter_dref_status(self):
606606
"""
607607
Test to filter dref status
608608
"""
609-
DrefFactory.create(title="test", status=Dref.Status.COMPLETED, date_of_approval="2020-10-10", created_by=self.user)
610-
DrefFactory.create(status=Dref.Status.COMPLETED, date_of_approval="2020-10-10", created_by=self.user)
611-
DrefFactory.create(status=Dref.Status.COMPLETED, date_of_approval="2020-10-10", created_by=self.user)
612-
DrefFactory.create(status=Dref.Status.IN_PROGRESS, created_by=self.user)
613-
DrefFactory.create(status=Dref.Status.IN_PROGRESS, created_by=self.user)
609+
DrefFactory.create(title="test", status=Dref.Status.APPROVED, date_of_approval="2020-10-10", created_by=self.user)
610+
DrefFactory.create(status=Dref.Status.APPROVED, date_of_approval="2020-10-10", created_by=self.user)
611+
DrefFactory.create(status=Dref.Status.APPROVED, date_of_approval="2020-10-10", created_by=self.user)
612+
DrefFactory.create(status=Dref.Status.DRAFT, created_by=self.user)
613+
DrefFactory.create(status=Dref.Status.DRAFT, created_by=self.user)
614614

615615
# filter by `In Progress`
616-
url = f"/api/v2/dref/?status={Dref.Status.IN_PROGRESS.value}"
616+
url = f"/api/v2/dref/?status={Dref.Status.DRAFT.value}"
617617
self.client.force_authenticate(self.user)
618618
response = self.client.get(url)
619619
self.assertEqual(response.status_code, 200)
620-
self.assertEqual(len(response.data["results"]), 5)
620+
self.assertEqual(len(response.data["results"]), 2)
621621

622622
def test_dref_country_filter(self):
623623
country1 = Country.objects.create(name="country1")
624624
country2 = Country.objects.create(name="country2")
625-
DrefFactory.create(title="test", status=Dref.Status.COMPLETED, created_by=self.user, country=country1)
626-
DrefFactory.create(status=Dref.Status.COMPLETED, created_by=self.user)
627-
DrefFactory.create(status=Dref.Status.COMPLETED, created_by=self.user, country=country2)
628-
DrefFactory.create(status=Dref.Status.IN_PROGRESS, created_by=self.user, country=country1)
629-
DrefFactory.create(status=Dref.Status.IN_PROGRESS, created_by=self.user)
625+
DrefFactory.create(title="test", status=Dref.Status.APPROVED, created_by=self.user, country=country1)
626+
DrefFactory.create(status=Dref.Status.APPROVED, created_by=self.user)
627+
DrefFactory.create(status=Dref.Status.APPROVED, created_by=self.user, country=country2)
628+
DrefFactory.create(status=Dref.Status.DRAFT, created_by=self.user, country=country1)
629+
DrefFactory.create(status=Dref.Status.DRAFT, created_by=self.user)
630630
url = f"/api/v2/dref/?country={country1.id}"
631631
self.client.force_authenticate(self.user)
632632
response = self.client.get(url)
@@ -636,9 +636,10 @@ def test_dref_country_filter(self):
636636
@mock.patch("django.utils.timezone.now")
637637
def test_dref_is_published(self, mock_now):
638638
"""
639-
Test for dref if is_published = True
639+
Test DREF publishing flow:
640+
- Can only publish when status=FINALIZED
641+
- Publishing sets status=APPROVED and is_published=True
640642
"""
641-
642643
initial_now = datetime.now()
643644
mock_now.return_value = initial_now
644645

@@ -647,9 +648,12 @@ def test_dref_is_published(self, mock_now):
647648
dref = DrefFactory.create(
648649
title="test",
649650
created_by=self.user,
651+
country=country,
652+
status=Dref.Status.DRAFT,
650653
is_published=False,
651654
type_of_dref=Dref.DrefType.IMMINENT,
652655
)
656+
# Normal PATCH
653657
url = f"/api/v2/dref/{dref.id}/"
654658
data = {
655659
"title": "New Update Title",
@@ -658,7 +662,6 @@ def test_dref_is_published(self, mock_now):
658662
self.client.force_authenticate(self.user)
659663
response = self.client.patch(url, data)
660664
self.assert_200(response)
661-
662665
# create new dref with is_published = False
663666
not_published_dref = DrefFactory.create(
664667
title="test",
@@ -674,23 +677,28 @@ def test_dref_is_published(self, mock_now):
674677
data["modified_at"] = initial_now - timedelta(seconds=10)
675678
response = self.client.patch(url, data)
676679
self.assert_400(response)
677-
678-
# test dref published endpoint
679-
url = f"/api/v2/dref/{not_published_dref.id}/publish/"
680+
# ---- Test publishing ----
681+
publish_url = f"/api/v2/dref/{dref.id}/publish/"
680682
data = {}
681-
self.client.force_authenticate(self.user)
682-
response = self.client.post(url, data)
683+
response = self.client.post(publish_url, data)
683684
self.assert_403(response)
684-
685-
# add permission to request user
685+
# Add permission to user
686686
self.dref_permission = Permission.objects.create(
687687
codename="dref_region_admin_0",
688688
content_type=ContentType.objects.get_for_model(Region),
689689
name="Dref Admin for 0",
690690
)
691691
self.user.user_permissions.add(self.dref_permission)
692-
response = self.client.post(url, data)
692+
# Try again while DRAFT. Should fail(not finalized yet)
693+
response = self.client.post(publish_url, data)
694+
self.assert_400(response)
695+
# Update status to FINALIZED, then publish should succeed
696+
dref.status = Dref.Status.FINALIZED
697+
dref.save(update_fields=["status"])
698+
response = self.client.post(publish_url, data)
699+
dref.refresh_from_db()
693700
self.assert_200(response)
701+
self.assertEqual(response.data["status"], Dref.Status.APPROVED)
694702
self.assertEqual(response.data["is_published"], True)
695703

696704
def test_dref_operation_update_create(self):
@@ -922,6 +930,7 @@ def test_final_report_update_once_published(self):
922930
dref=dref,
923931
country=country,
924932
type_of_dref=Dref.DrefType.RESPONSE,
933+
status=Dref.Status.FINALIZED,
925934
)
926935
final_report.users.set([user1])
927936
# try to publish this report
@@ -941,7 +950,7 @@ def test_final_report_update_once_published(self):
941950
response = self.client.post(url, data)
942951
self.assert_200(response)
943952
self.assertEqual(response.data["is_published"], True)
944-
953+
self.assertEqual(response.data["status"], Dref.Status.APPROVED)
945954
# now try to patch to the final report
946955
url = f"/api/v2/dref-final-report/{final_report.id}/"
947956
data = {
@@ -959,7 +968,7 @@ def test_dref_for_assessment_report(self):
959968
"type_of_onset": Dref.OnsetType.SLOW.value,
960969
"type_of_dref": Dref.DrefType.ASSESSMENT,
961970
"disaster_category": Dref.DisasterCategory.YELLOW.value,
962-
"status": Dref.Status.IN_PROGRESS.value,
971+
"status": Dref.Status.DRAFT.value,
963972
"num_assisted": 5666,
964973
"num_affected": 23,
965974
"amount_requested": 127771111,
@@ -1547,7 +1556,7 @@ def test_dref_imminent(self):
15471556
"type_of_onset": Dref.OnsetType.SUDDEN.value,
15481557
"type_of_dref": Dref.DrefType.IMMINENT,
15491558
"disaster_category": Dref.DisasterCategory.YELLOW.value,
1550-
"status": Dref.Status.IN_PROGRESS.value,
1559+
"status": Dref.Status.DRAFT.value,
15511560
"num_assisted": 5666,
15521561
"num_affected": 23,
15531562
"amount_requested": 127771111,

dref/views.py

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

@@ -154,8 +156,10 @@ def get_queryset(self):
154156
)
155157
def get_published(self, request, pk=None, version=None):
156158
operational_update = self.get_object()
159+
if operational_update.status != Dref.Status.FINALIZED:
160+
raise serializers.ValidationError(gettext("Must be finalized before publishing."))
157161
operational_update.is_published = True
158-
operational_update.status = Dref.Status.COMPLETED
162+
operational_update.status = Dref.Status.APPROVED
159163
operational_update.save(update_fields=["is_published", "status"])
160164
serializer = DrefOperationalUpdateSerializer(operational_update, context={"request": request})
161165
return response.Response(serializer.data)
@@ -186,10 +190,12 @@ def get_queryset(self):
186190
)
187191
def get_published(self, request, pk=None, version=None):
188192
field_report = self.get_object()
193+
if field_report.status != Dref.Status.FINALIZED:
194+
raise serializers.ValidationError(gettext("Must be finalized before publishing."))
189195
if field_report.is_published:
190196
raise serializers.ValidationError(gettext("Final Report %s is already published" % field_report))
191197
field_report.is_published = True
192-
field_report.status = Dref.Status.COMPLETED
198+
field_report.status = Dref.Status.APPROVED
193199
field_report.save(update_fields=["is_published", "status"])
194200
field_report.dref.is_active = False
195201
field_report.date_of_approval = timezone.now().date()

0 commit comments

Comments
 (0)