Skip to content

Commit 7288ddc

Browse files
committed
feat(emergency): add previous data on field report and dref ops update
1 parent 02f7b18 commit 7288ddc

6 files changed

Lines changed: 198 additions & 138 deletions

File tree

api/drf_views.py

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1583,23 +1583,31 @@ class EmergencyViewset(
15831583
def get_queryset(self):
15841584
today = timezone.now().date()
15851585

1586-
active_appeal_qs = Appeal.objects.filter(
1587-
event=OuterRef("pk"),
1588-
status=AppealStatus.ACTIVE,
1586+
appeal_priority_qs = (
1587+
Appeal.objects.filter(
1588+
event=OuterRef("pk"),
1589+
status__in=[AppealStatus.ACTIVE, AppealStatus.CLOSED],
1590+
)
1591+
.annotate(
1592+
priority=Case(
1593+
When(status=AppealStatus.ACTIVE, then=Value(1)),
1594+
When(status=AppealStatus.CLOSED, then=Value(2)),
1595+
output_field=IntegerField(),
1596+
)
1597+
)
1598+
.order_by("priority", "-start_date")
15891599
)
15901600

1591-
active_dref_appeal_qs = active_appeal_qs.filter(
1601+
active_dref_appeal_qs = appeal_priority_qs.filter(
15921602
atype=AppealType.DREF,
15931603
)
15941604

1595-
active_emergency_appeal_qs = active_appeal_qs.filter(
1605+
active_emergency_appeal_qs = appeal_priority_qs.filter(
15961606
atype=AppealType.APPEAL,
15971607
)
15981608

15991609
field_report_qs = FieldReport.objects.filter(event=OuterRef("pk"))
16001610

1601-
latest_appeal_qs = active_emergency_appeal_qs.order_by("-start_date")
1602-
16031611
approved_dref_qs = Dref.objects.filter(
16041612
event=OuterRef("pk"),
16051613
status=Dref.Status.APPROVED,
@@ -1657,7 +1665,7 @@ def get_queryset(self):
16571665
then=Value(EventStage.DREF_APPLICATION),
16581666
),
16591667
# If there is an active appeal of DREF type, but no approved DREF yet,
1660-
# we consider the emergency to be in the Emergency Appeal stage.
1668+
# we consider the emergency to be in the Dref Appeal only stage.
16611669
# Reaches here only if no approved DREF/ops-update/final-report exists
16621670
# So an active appeal type DREF appeal with no approved DREF = DREF_APPEAL_ONLY stage.
16631671
When(
@@ -1677,11 +1685,12 @@ def get_queryset(self):
16771685
# to avoid extra queries in serializer.
16781686
stage_appeal_id=Case(
16791687
When(
1680-
stage__in=[
1681-
EventStage.EMERGENCY_APPEAL,
1682-
EventStage.DREF_APPEAL_ONLY,
1683-
],
1684-
then=Subquery(latest_appeal_qs.values("id")[:1]),
1688+
stage=EventStage.EMERGENCY_APPEAL,
1689+
then=Subquery(active_emergency_appeal_qs.values("id")[:1]),
1690+
),
1691+
When(
1692+
stage=EventStage.DREF_APPEAL_ONLY,
1693+
then=Subquery(active_dref_appeal_qs.values("id")[:1]),
16851694
),
16861695
default=Value(None),
16871696
output_field=IntegerField(null=True),
@@ -1712,7 +1721,10 @@ def get_queryset(self):
17121721
),
17131722
stage_ops_update_id=Case(
17141723
When(
1715-
stage=EventStage.DREF_OPERATIONAL_UPDATE,
1724+
stage__in=[
1725+
EventStage.DREF_OPERATIONAL_UPDATE,
1726+
EventStage.DREF_FINAL_REPORT,
1727+
],
17161728
then=Subquery(
17171729
DrefOperationalUpdate.objects.filter(
17181730
dref__id=OuterRef("stage_dref_id"), status=Dref.Status.APPROVED

api/migrations/0232_remove_event_auto_generated_source_event_source_and_more.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def migrate_sources(apps, _):
4646
source = 160 # NEW_FIELD_REPORT
4747
else:
4848
# Using title for backward compatibility, as some events have auto_generated_source empty but title containing "GDACS"
49-
if obj.title and obj.title.lower().startswith("gdacs"):
49+
if obj.name and obj.name.lower().startswith("gdacs"):
5050
source = 110 # GDACS
5151
else:
5252
source = 100 # MANUAL_INPUT

api/serializers.py

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2142,6 +2142,22 @@ class Meta:
21422142

21432143
# NOTE: Using this specific serializer for emergency page schema
21442144
# Contains information from latest field report mostly and some fields from first field report
2145+
class EmergencyPreviousFieldReportSerializer(
2146+
serializers.ModelSerializer,
2147+
):
2148+
class Meta:
2149+
model = FieldReport
2150+
fields = (
2151+
"id",
2152+
"event_id",
2153+
"fr_num",
2154+
"start_date",
2155+
"report_date",
2156+
"created_at",
2157+
"updated_at",
2158+
)
2159+
2160+
21452161
class EmergencyFieldReportSerializer(
21462162
serializers.ModelSerializer,
21472163
):
@@ -2783,6 +2799,17 @@ class Meta:
27832799
fields = "__all__"
27842800

27852801

2802+
class TimelineEmergencyFieldReportSerializer(serializers.ModelSerializer):
2803+
class Meta:
2804+
model = FieldReport
2805+
fields = (
2806+
"id",
2807+
"report_date",
2808+
"fr_num",
2809+
"start_date",
2810+
)
2811+
2812+
27862813
class DetailEmergencySerializer(ModelSerializer):
27872814
from dref.serializers import EmergencyDrefSerializer
27882815

@@ -2815,6 +2842,8 @@ class DetailEmergencySerializer(ModelSerializer):
28152842
first_field_report_created_at = serializers.DateTimeField(read_only=True)
28162843
latest_field_report_created_at = serializers.DateTimeField(read_only=True)
28172844

2845+
timeline_field_reports = serializers.SerializerMethodField()
2846+
28182847
class Meta:
28192848
model = Event
28202849
fields = (
@@ -2868,15 +2897,29 @@ class Meta:
28682897
"dref",
28692898
"first_field_report_created_at",
28702899
"latest_field_report_created_at",
2900+
"timeline_field_reports",
28712901
)
28722902

2873-
def _get_stage_instance(self, event):
2874-
if hasattr(self, "_stage_instance_cache"):
2875-
return self._stage_instance_cache
2903+
@extend_schema_field(TimelineEmergencyFieldReportSerializer(many=True))
2904+
def get_timeline_field_reports(self, event):
2905+
field_reports = (
2906+
FieldReport.objects.filter(event=event)
2907+
.order_by(
2908+
"-updated_at",
2909+
"-fr_num",
2910+
)
2911+
.values(
2912+
"id",
2913+
"report_date",
2914+
"fr_num",
2915+
"start_date",
2916+
)
2917+
)
2918+
serializer = TimelineEmergencyFieldReportSerializer(field_reports, many=True)
2919+
return serializer.data
28762920

2921+
def _get_stage_instance(self, event):
28772922
stage = getattr(event, "stage")
2878-
instance = None
2879-
28802923
if stage == EventStage.FIELD_REPORT and event.stage_field_report_id:
28812924
_first_field_report_queryset = FieldReport.objects.filter(event_id=OuterRef("event_id")).order_by(
28822925
"created_at", "fr_num"
@@ -2891,16 +2934,17 @@ def _get_stage_instance(self, event):
28912934
first_fr_request_assistance=Subquery(_first_field_report_queryset.values("request_assistance")[:1]),
28922935
)
28932936
.prefetch_related(
2894-
Prefetch("countries", queryset=Country.objects.select_related("region")),
2895-
Prefetch("districts", queryset=District.objects.select_related("country")),
2937+
"contacts",
2938+
"countries",
2939+
"districts",
28962940
Prefetch(
28972941
"actions_taken",
28982942
queryset=ActionsTaken.objects.prefetch_related("actions"),
28992943
),
2900-
"contacts",
29012944
)
29022945
.get(pk=event.stage_field_report_id)
29032946
)
2947+
return instance
29042948

29052949
elif (
29062950
stage
@@ -2911,6 +2955,7 @@ def _get_stage_instance(self, event):
29112955
and event.stage_appeal_id
29122956
):
29132957
instance = Appeal.objects.get(pk=event.stage_appeal_id)
2958+
return instance
29142959
elif (
29152960
stage
29162961
in [
@@ -2938,7 +2983,7 @@ def _get_stage_instance(self, event):
29382983
),
29392984
)
29402985
.prefetch_related(
2941-
Prefetch("district", queryset=District.objects.select_related("country")),
2986+
"district",
29422987
Prefetch(
29432988
"planned_interventions",
29442989
queryset=PlannedIntervention.objects.prefetch_related("indicators"),
@@ -2950,8 +2995,7 @@ def _get_stage_instance(self, event):
29502995
)
29512996
.get(pk=event.stage_dref_id)
29522997
)
2953-
self._stage_instance_cache = instance
2954-
return instance
2998+
return instance
29552999

29563000
def get_stage_display(self, event):
29573001
stage = getattr(event, "stage", None)

api/test_views.py

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1340,13 +1340,31 @@ def test_inactive_appeal_does_not_trigger_emergency_appeal_stage(self):
13401340
AppealFactory.create(
13411341
event=event,
13421342
dtype=self.disaster_type,
1343-
status=AppealStatus.CLOSED,
1343+
status=AppealStatus.ARCHIVED,
13441344
atype=AppealType.APPEAL,
13451345
)
13461346

13471347
data = self._get(event).data
13481348
self.assertNotEqual(data["stage"], EventStage.EMERGENCY_APPEAL)
13491349

1350+
# CLOSED one also considered but ACTIVE takes over
1351+
AppealFactory.create(
1352+
event=event,
1353+
dtype=self.disaster_type,
1354+
status=AppealStatus.CLOSED,
1355+
atype=AppealType.APPEAL,
1356+
)
1357+
data = self._get(event).data
1358+
self.assertEqual(data["stage"], EventStage.EMERGENCY_APPEAL)
1359+
self.assertEqual(data["appeal"]["status"], AppealStatus.CLOSED)
1360+
1361+
self._active_emergency_appeal(event)
1362+
data = self._get(event).data
1363+
self.assertEqual(data["stage"], EventStage.EMERGENCY_APPEAL)
1364+
1365+
# Active should take priority over closed and archived
1366+
self.assertEqual(data["appeal"]["status"], AppealStatus.ACTIVE)
1367+
13501368
def test_emergency_appeal_takes_priority_over_entire_dref_chain(self):
13511369
event = EventFactory.create(dtype=self.disaster_type)
13521370
dref = self._approved_dref(event)
@@ -1466,3 +1484,47 @@ def fallback_to_dref_appeal_for_no_approved_dref(self):
14661484

14671485
self.assertEqual(parse_datetime(data["first_field_report_created_at"]), first_fr.created_at)
14681486
self.assertEqual(parse_datetime(data["latest_field_report_created_at"]), latest_fr.created_at)
1487+
1488+
def test_field_report_with_timeline_field_reports(self):
1489+
event = EventFactory.create(dtype=self.disaster_type)
1490+
FieldReportFactory.create_batch(
1491+
3,
1492+
event=event,
1493+
dtype=self.disaster_type,
1494+
)
1495+
data = self._get(event).data
1496+
self.assertEqual(data["stage"], EventStage.FIELD_REPORT, data)
1497+
self.assertIsNotNone(data["field_report"], data)
1498+
self.assertEqual(len(data["timeline_field_reports"]), 3)
1499+
1500+
def test_dref_operational_update_with_timeline_ops_updates(self):
1501+
event = EventFactory.create(dtype=self.disaster_type)
1502+
dref = self._approved_dref(event)
1503+
DrefOperationalUpdateFactory.create_batch(
1504+
3,
1505+
dref=dref,
1506+
status=Dref.Status.APPROVED,
1507+
disaster_type=self.disaster_type,
1508+
country=self.country,
1509+
)
1510+
data = self._get(event).data
1511+
self.assertEqual(data["stage"], EventStage.DREF_OPERATIONAL_UPDATE, data)
1512+
1513+
dref_data = data.get("dref")
1514+
self.assertIsNotNone(dref_data, data)
1515+
1516+
self.assertEqual(len(dref_data["timeline_operational_updates"]), 3)
1517+
1518+
# NOTE: if Final report is created, stage should be DREF_FINAL_REPORT and previous operational updates should also show
1519+
DrefFinalReportFactory.create(
1520+
dref=dref,
1521+
status=Dref.Status.APPROVED,
1522+
disaster_type=self.disaster_type,
1523+
country=self.country,
1524+
)
1525+
data = self._get(event).data
1526+
self.assertEqual(data["stage"], EventStage.DREF_FINAL_REPORT)
1527+
dref_data = data.get("dref")
1528+
self.assertIsNotNone(dref_data)
1529+
self.assertIsNotNone(dref_data["final_report_details"])
1530+
self.assertEqual(len(dref_data["timeline_operational_updates"]), 3)

0 commit comments

Comments
 (0)