Skip to content

Commit 4829ff5

Browse files
committed
feat(emergency): add staging logic for emergency page
- priority based (Appeal, Drefs, Dref_type Appeal, Field report) - Add test cases
1 parent 1a3001e commit 4829ff5

13 files changed

Lines changed: 952 additions & 109 deletions

File tree

api/drf_views.py

Lines changed: 93 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1580,30 +1580,48 @@ class EmergencyViewset(
15801580
filterset_class = EventFilter
15811581

15821582
def get_queryset(self):
1583-
today = timezone.now().date().strftime("%Y-%m-%d")
1583+
today = timezone.now().date()
15841584

1585-
# We might need to use joins intead of doing with appeal code and dref appeal code
1586-
_dref_code = Subquery(
1587-
Appeal.objects.filter(
1588-
event=OuterRef(OuterRef("pk")),
1589-
status=AppealStatus.ACTIVE,
1590-
atype=AppealType.DREF,
1591-
)
1592-
.order_by("-start_date")
1593-
.values("code")[:1]
1585+
active_appeal_qs = Appeal.objects.filter(
1586+
event=OuterRef("pk"),
1587+
status=AppealStatus.ACTIVE,
1588+
)
1589+
1590+
active_dref_appeal_qs = active_appeal_qs.filter(
1591+
atype=AppealType.DREF,
1592+
)
1593+
1594+
active_emergency_appeal_qs = active_appeal_qs.filter(
1595+
atype=AppealType.APPEAL,
15941596
)
15951597

1598+
field_report_qs = FieldReport.objects.filter(event=OuterRef("pk"))
1599+
1600+
latest_appeal_qs = active_emergency_appeal_qs.order_by("-start_date")
1601+
1602+
approved_dref_qs = Dref.objects.filter(
1603+
event=OuterRef("pk"),
1604+
status=Dref.Status.APPROVED,
1605+
)
1606+
1607+
approved_ops_update_qs = DrefOperationalUpdate.objects.filter(
1608+
dref__event=OuterRef("pk"),
1609+
status=Dref.Status.APPROVED,
1610+
).order_by("-created_at")
1611+
1612+
approved_final_report_qs = DrefFinalReport.objects.filter(
1613+
dref__event=OuterRef("pk"),
1614+
status=Dref.Status.APPROVED,
1615+
).order_by("-created_at")
1616+
15961617
return (
15971618
super()
15981619
.get_queryset()
15991620
.annotate(
16001621
# Aggregated Values
1601-
response_activity_count=Subquery(
1602-
EmergencyProject.objects.filter(event=OuterRef("pk"))
1603-
.values("event")
1604-
.annotate(count=Count("id"))
1605-
.values("count")[:1],
1606-
output_field=IntegerField(),
1622+
response_activity_count=Count(
1623+
"emergency_projects",
1624+
distinct=True,
16071625
),
16081626
active_deployments_count=Count(
16091627
"personneldeployment__personnel",
@@ -1615,26 +1633,35 @@ def get_queryset(self):
16151633
),
16161634
distinct=True,
16171635
),
1618-
surge_alerts_count=Count("surgealert", distinct=True),
1619-
# stages
1636+
surge_alerts_count=Count(
1637+
"surgealert",
1638+
distinct=True,
1639+
),
1640+
# Stage
16201641
stage=Case(
16211642
When(
1622-
Exists(Appeal.objects.filter(event=OuterRef("pk"), status=AppealStatus.ACTIVE, atype=AppealType.APPEAL)),
1643+
Exists(active_emergency_appeal_qs),
16231644
then=Value(EventStage.EMERGENCY_APPEAL),
16241645
),
16251646
When(
1626-
Exists(Appeal.objects.filter(event=OuterRef("pk"), status=AppealStatus.ACTIVE, atype=AppealType.DREF))
1627-
& Exists(DrefFinalReport.objects.filter(appeal_code=_dref_code, status=Dref.Status.APPROVED)),
1647+
Exists(approved_final_report_qs),
16281648
then=Value(EventStage.DREF_FINAL_REPORT),
16291649
),
16301650
When(
1631-
Exists(Appeal.objects.filter(event=OuterRef("pk"), status=AppealStatus.ACTIVE, atype=AppealType.DREF))
1632-
& Exists(DrefOperationalUpdate.objects.filter(appeal_code=_dref_code, status=Dref.Status.APPROVED)),
1651+
Exists(approved_ops_update_qs),
16331652
then=Value(EventStage.DREF_OPERATIONAL_UPDATE),
16341653
),
16351654
When(
1636-
Exists(Appeal.objects.filter(event=OuterRef("pk"), status=AppealStatus.ACTIVE, atype=AppealType.DREF)),
1637-
then=Value(EventStage.DREF),
1655+
Exists(approved_dref_qs),
1656+
then=Value(EventStage.DREF_APPLICATION),
1657+
),
1658+
# If there is an active appeal of DREF type, but no approved DREF yet,
1659+
# we consider the emergency to be in the Emergency Appeal stage.
1660+
# Reaches here only if no approved DREF/ops-update/final-report exists
1661+
# So an active appeal type DREF appeal with no approved DREF = Emergency Appeal
1662+
When(
1663+
Exists(active_dref_appeal_qs),
1664+
then=Value(EventStage.EMERGENCY_APPEAL),
16381665
),
16391666
When(
16401667
Exists(FieldReport.objects.filter(event=OuterRef("pk"))),
@@ -1645,31 +1672,36 @@ def get_queryset(self):
16451672
),
16461673
)
16471674
.annotate(
1648-
# Passing values for the current stage's instance, to avoid extra queries in the serializer.
1649-
stage_field_report_id=Case(
1675+
# Passing values for the current stage's instance,
1676+
# to avoid extra queries in serializer.
1677+
stage_appeal_id=Case(
16501678
When(
1651-
stage=EventStage.FIELD_REPORT,
1652-
then=Subquery(FieldReport.objects.filter(event=OuterRef("pk")).order_by("-created_at").values("id")[:1]),
1679+
stage=EventStage.EMERGENCY_APPEAL,
1680+
then=Subquery(latest_appeal_qs.values("id")[:1]),
16531681
),
16541682
default=Value(None),
16551683
output_field=IntegerField(null=True),
16561684
),
1657-
stage_appeal_id=Case(
1685+
stage_dref_id=Case(
16581686
When(
1659-
stage=EventStage.EMERGENCY_APPEAL,
1660-
then=Subquery(
1661-
Appeal.objects.filter(event=OuterRef("pk"), status=AppealStatus.ACTIVE, atype=AppealType.APPEAL)
1662-
.order_by("-start_date")
1663-
.values("id")[:1]
1664-
),
1687+
stage__in=[
1688+
EventStage.DREF_APPLICATION,
1689+
EventStage.DREF_OPERATIONAL_UPDATE,
1690+
EventStage.DREF_FINAL_REPORT,
1691+
],
1692+
then=Subquery(approved_dref_qs.values("id")[:1]),
16651693
),
16661694
default=Value(None),
16671695
output_field=IntegerField(null=True),
16681696
),
1669-
stage_dref_id=Case(
1697+
stage_final_report_id=Case(
16701698
When(
1671-
stage=EventStage.DREF,
1672-
then=Subquery(Dref.objects.filter(appeal_code=_dref_code, status=Dref.Status.APPROVED).values("id")[:1]),
1699+
stage=EventStage.DREF_FINAL_REPORT,
1700+
then=Subquery(
1701+
DrefFinalReport.objects.filter(
1702+
dref__id=OuterRef("stage_dref_id"), status=Dref.Status.APPROVED
1703+
).values("id")[:1]
1704+
),
16731705
),
16741706
default=Value(None),
16751707
output_field=IntegerField(null=True),
@@ -1678,33 +1710,40 @@ def get_queryset(self):
16781710
When(
16791711
stage=EventStage.DREF_OPERATIONAL_UPDATE,
16801712
then=Subquery(
1681-
DrefOperationalUpdate.objects.filter(appeal_code=_dref_code, status=Dref.Status.APPROVED)
1682-
.order_by("-created_at")
1683-
.values("id")[:1]
1713+
DrefOperationalUpdate.objects.filter(
1714+
dref__id=OuterRef("stage_dref_id"), status=Dref.Status.APPROVED
1715+
).values("id")[:1]
16841716
),
16851717
),
16861718
default=Value(None),
16871719
output_field=IntegerField(null=True),
16881720
),
1689-
stage_final_report_id=Case(
1721+
stage_field_report_id=Case(
16901722
When(
1691-
stage=EventStage.DREF_FINAL_REPORT,
1692-
then=Subquery(
1693-
DrefFinalReport.objects.filter(appeal_code=_dref_code, status=Dref.Status.APPROVED)
1694-
.order_by("-created_at")
1695-
.values("id")[:1]
1696-
),
1723+
stage=EventStage.FIELD_REPORT,
1724+
then=Subquery(field_report_qs.order_by("-updated_at", "-fr_num").values("id")[:1]),
16971725
),
16981726
default=Value(None),
16991727
output_field=IntegerField(null=True),
17001728
),
1729+
first_field_report_created_at=Subquery(field_report_qs.order_by("created_at", "fr_num").values("created_at")[:1]),
1730+
latest_field_report_created_at=Subquery(
1731+
field_report_qs.order_by("-created_at", "-fr_num").values("created_at")[:1]
1732+
),
17011733
)
17021734
.select_related("dtype")
17031735
.prefetch_related(
1704-
"regions",
1705-
Prefetch("countries", queryset=Country.objects.select_related("region")),
1706-
Prefetch("countries_for_preview", queryset=Country.objects.select_related("region")),
1707-
Prefetch("key_figures", queryset=KeyFigure.objects.all()),
1708-
Prefetch("contacts", queryset=EventContact.objects.all()),
1736+
Prefetch(
1737+
"countries",
1738+
queryset=Country.objects.select_related("region"),
1739+
),
1740+
Prefetch(
1741+
"key_figures",
1742+
queryset=KeyFigure.objects.all(),
1743+
),
1744+
Prefetch(
1745+
"contacts",
1746+
queryset=EventContact.objects.all(),
1747+
),
17091748
)
17101749
)

api/factories/event.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class Meta:
2626
name = fuzzy.FuzzyText(length=50)
2727
slug = fuzzy.FuzzyText(length=50)
2828
dtype = factory.SubFactory(DisasterTypeFactory)
29+
source = fuzzy.FuzzyChoice(Event.EventSource)
2930

3031
@factory.post_generation
3132
def districts(self, create, extracted, **kwargs):

api/management/commands/migrate_event_source.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ def handle(self, *args, **options):
1919
event.source = Event.EventSource.GDACS
2020
self.stdout.write(self.style.NOTICE(f"Updating {event.name} source to {Event.EventSource.GDACS.label}"))
2121
else:
22-
event.source = Event.EventSource.Manual_Input
23-
self.stdout.write(self.style.NOTICE(f"Updating {event.name} source to {Event.EventSource.Manual_Input.label}"))
22+
event.source = Event.EventSource.MANUAL_INPUT
23+
self.stdout.write(self.style.NOTICE(f"Updating {event.name} source to {Event.EventSource.MANUAL_INPUT.label}"))
2424
event.auto_generated = False
2525
to_update.append(event)
2626

api/models.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -764,7 +764,7 @@ def snippet_image_path(instance, filename):
764764
# NOTE: Stage for the emergency timeline
765765
class EventStage(models.IntegerChoices):
766766
EMERGENCY_APPEAL = 1, _("Emergency Appeal")
767-
DREF = 2, _("DREF")
767+
DREF_APPLICATION = 2, _("DREF Application")
768768
DREF_OPERATIONAL_UPDATE = 3, _("DREF Operational Update")
769769
DREF_FINAL_REPORT = 4, _("DREF Final Report")
770770
FIELD_REPORT = 5, _("Field Report")
@@ -778,7 +778,8 @@ class Event(models.Model):
778778

779779
class EventSource(models.IntegerChoices):
780780

781-
Manual_Input = 100, _("Manual input")
781+
# TODO(Susilnem): use upper case
782+
MANUAL_INPUT = 100, _("Manual input")
782783
"""MANUAL_INPUT: Event data manually entered by a user through the event administration interface."""
783784

784785
GDACS = 110, _("GDACs scraper")
@@ -862,7 +863,7 @@ class EventSource(models.IntegerChoices):
862863
previous_update = models.DateTimeField(verbose_name=_("previous update"), null=True, blank=True)
863864

864865
auto_generated = models.BooleanField(verbose_name=_("auto generated"), default=False, editable=False)
865-
source = models.IntegerField(choices=EventSource.choices, default=EventSource.Manual_Input, verbose_name=_("Event source"))
866+
source = models.IntegerField(choices=EventSource.choices, default=EventSource.MANUAL_INPUT, verbose_name=_("Event source"))
866867

867868
# Meant to give the organization a way of highlighting certain, important events.
868869
is_featured = models.BooleanField(default=False, verbose_name=_("is featured on home page"))

0 commit comments

Comments
 (0)