Skip to content

Commit 95f5c3c

Browse files
committed
Merge pull request #2736 from IFRCGo/feature/attach-dref-emergency-page
Feature: Emergency page stage and attach event on dref
2 parents fd4bf27 + 7288ddc commit 95f5c3c

23 files changed

Lines changed: 1548 additions & 151 deletions

api/admin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -619,7 +619,7 @@ def create_events(self, request, queryset):
619619
dtype=getattr(report, "dtype"),
620620
disaster_start_date=getattr(report, "created_at"),
621621
auto_generated=True,
622-
source=models.Event.EventSource.REPORT_ADMIN,
622+
source=models.Event.EventSource.FIELD_REPORT_ADMIN,
623623
)
624624
if getattr(report, "countries").exists():
625625
for country in report.countries.all():

api/drf_views.py

Lines changed: 205 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@
66
Avg,
77
Case,
88
Count,
9+
Exists,
910
ExpressionWrapper,
1011
F,
1112
OuterRef,
1213
Prefetch,
1314
Q,
1415
Subquery,
1516
Sum,
17+
Value,
1618
When,
1719
)
1820
from django.db.models.fields import IntegerField
@@ -59,6 +61,7 @@
5961
from databank.serializers import CountryOverviewSerializer
6062
from deployments.models import ERU, Personnel
6163
from deployments.serializers import ListDeployedERUByEventSerializer
64+
from dref.models import Dref, DrefFinalReport, DrefOperationalUpdate
6265
from main.enums import GlobalEnumSerializer, get_enum_values
6366
from main.filters import NullsLastOrderingFilter
6467
from main.permissions import DenyGuestUserMutationPermission, DenyGuestUserPermission
@@ -73,6 +76,7 @@
7376
Appeal,
7477
AppealDocument,
7578
AppealHistory,
79+
AppealStatus,
7680
AppealType,
7781
Country,
7882
CountryKeyDocument,
@@ -85,7 +89,9 @@
8589
Event,
8690
EventContact,
8791
EventFeaturedDocument,
92+
EventLink,
8893
EventSeverityLevelHistory,
94+
EventStage,
8995
Export,
9096
ExternalPartner,
9197
FieldReport,
@@ -749,11 +755,12 @@ def get_queryset(self, *args, **kwargs):
749755
qset = super().get_queryset()
750756
if self.action == "mini_events":
751757
# return Event.objects.filter(parent_event__isnull=True).select_related('dtype')
752-
return qset.filter(parent_event__isnull=True).select_related("dtype")
758+
return qset.filter(parent_event__isnull=True).select_related("dtype").prefetch_related("countries_for_preview")
759+
753760
if self.action == "response_activity_events":
754761
return (
755762
qset.filter(parent_event__isnull=True)
756-
.filter(Q(auto_generated=False) | Q(source=Event.EventSource.NEW_REPORT))
763+
.filter(Q(auto_generated=False) | Q(source=Event.EventSource.NEW_FIELD_REPORT))
757764
.select_related("dtype")
758765
)
759766
return (
@@ -869,7 +876,11 @@ def retrieve(self, request, pk=None, *args, **kwargs):
869876
)
870877
@action(methods=["get"], detail=False, url_path="mini")
871878
def mini_events(self, request):
872-
queryset = self.filter_queryset(self.get_queryset())
879+
queryset = self.filter_queryset(self.get_queryset()).annotate(
880+
latest_field_report_id=Subquery(
881+
FieldReport.objects.filter(event=OuterRef("pk")).order_by("-updated_at").values("id")[:1]
882+
)
883+
)
873884
serializer = ListMiniEventSerializer(queryset, many=True)
874885
page = self.paginate_queryset(queryset)
875886
if page is not None:
@@ -1351,7 +1362,7 @@ class SupportedActivityViewset(viewsets.ReadOnlyModelViewSet):
13511362
# summary=report.description or "",
13521363
# disaster_start_date=report.start_date,
13531364
# auto_generated=True,
1354-
# source=Event.EventSource.NEW_REPORT,
1365+
# source=Event.EventSource.NEW_FIELD_REPORT,
13551366
# visibility=report.visibility,
13561367
# **{TRANSLATOR_ORIGINAL_LANGUAGE_FIELD_NAME: django_get_language()},
13571368
# )
@@ -1559,45 +1570,208 @@ def get_queryset(self):
15591570
return CountrySupportingPartner.objects.select_related("country")
15601571

15611572

1562-
class EmergencyViewset(ReadOnlyVisibilityViewset):
1573+
class EmergencyViewset(
1574+
mixins.RetrieveModelMixin,
1575+
viewsets.GenericViewSet,
1576+
ReadOnlyVisibilityViewsetMixin,
1577+
):
15631578
queryset = Event.objects.all()
15641579
lookup_field = "id"
15651580
serializer_class = DetailEmergencySerializer
15661581
filterset_class = EventFilter
1567-
visibility_model_class = Event
15681582

15691583
def get_queryset(self):
1584+
today = timezone.now().date()
1585+
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")
1599+
)
1600+
1601+
active_dref_appeal_qs = appeal_priority_qs.filter(
1602+
atype=AppealType.DREF,
1603+
)
1604+
1605+
active_emergency_appeal_qs = appeal_priority_qs.filter(
1606+
atype=AppealType.APPEAL,
1607+
)
1608+
1609+
field_report_qs = FieldReport.objects.filter(event=OuterRef("pk"))
1610+
1611+
approved_dref_qs = Dref.objects.filter(
1612+
event=OuterRef("pk"),
1613+
status=Dref.Status.APPROVED,
1614+
)
1615+
1616+
approved_ops_update_qs = DrefOperationalUpdate.objects.filter(
1617+
dref__event=OuterRef("pk"),
1618+
status=Dref.Status.APPROVED,
1619+
).order_by("-created_at")
1620+
1621+
approved_final_report_qs = DrefFinalReport.objects.filter(
1622+
dref__event=OuterRef("pk"),
1623+
status=Dref.Status.APPROVED,
1624+
).order_by("-created_at")
1625+
15701626
return (
15711627
super()
15721628
.get_queryset()
1573-
.select_related(
1574-
"dtype",
1575-
"parent_event",
1576-
)
1577-
.prefetch_related(
1578-
"regions",
1579-
"countries",
1580-
"countries_for_preview",
1581-
Prefetch("key_figures", queryset=KeyFigure.objects.all()),
1582-
Prefetch("contacts", queryset=EventContact.objects.all()),
1629+
.annotate(
1630+
# Aggregated Values
1631+
response_activity_count=Count(
1632+
"emergency_projects",
1633+
distinct=True,
1634+
),
1635+
active_deployments_count=Count(
1636+
"personneldeployment__personnel",
1637+
filter=Q(
1638+
personneldeployment__personnel__type=Personnel.TypeChoices.RR,
1639+
personneldeployment__personnel__start_date__date__lte=today,
1640+
personneldeployment__personnel__end_date__date__gte=today,
1641+
personneldeployment__personnel__is_active=True,
1642+
),
1643+
distinct=True,
1644+
),
1645+
surge_alerts_count=Count(
1646+
"surgealert",
1647+
distinct=True,
1648+
),
1649+
# Stage
1650+
stage=Case(
1651+
When(
1652+
Exists(active_emergency_appeal_qs),
1653+
then=Value(EventStage.EMERGENCY_APPEAL),
1654+
),
1655+
When(
1656+
Exists(approved_final_report_qs),
1657+
then=Value(EventStage.DREF_FINAL_REPORT),
1658+
),
1659+
When(
1660+
Exists(approved_ops_update_qs),
1661+
then=Value(EventStage.DREF_OPERATIONAL_UPDATE),
1662+
),
1663+
When(
1664+
Exists(approved_dref_qs),
1665+
then=Value(EventStage.DREF_APPLICATION),
1666+
),
1667+
# If there is an active appeal of DREF type, but no approved DREF yet,
1668+
# we consider the emergency to be in the Dref Appeal only stage.
1669+
# Reaches here only if no approved DREF/ops-update/final-report exists
1670+
# So an active appeal type DREF appeal with no approved DREF = DREF_APPEAL_ONLY stage.
1671+
When(
1672+
Exists(active_dref_appeal_qs),
1673+
then=Value(EventStage.DREF_APPEAL_ONLY),
1674+
),
1675+
When(
1676+
Exists(FieldReport.objects.filter(event=OuterRef("pk"))),
1677+
then=Value(EventStage.FIELD_REPORT),
1678+
),
1679+
default=Value(None),
1680+
output_field=IntegerField(null=True),
1681+
),
15831682
)
15841683
.annotate(
1585-
first_field_report_id=Subquery(
1586-
FieldReport.objects.filter(event=OuterRef("pk"))
1587-
.order_by(
1588-
"fr_num",
1589-
"updated_at",
1590-
)
1591-
.values("id")[:1]
1684+
# Passing values for the current stage's instance,
1685+
# to avoid extra queries in serializer.
1686+
stage_appeal_id=Case(
1687+
When(
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]),
1694+
),
1695+
default=Value(None),
1696+
output_field=IntegerField(null=True),
15921697
),
1593-
latest_field_report_id=Subquery(
1594-
FieldReport.objects.filter(event=OuterRef("pk"))
1595-
.order_by(
1596-
"-fr_num",
1597-
"-updated_at",
1598-
)
1599-
.values("id")[:1]
1698+
stage_dref_id=Case(
1699+
When(
1700+
stage__in=[
1701+
EventStage.DREF_APPLICATION,
1702+
EventStage.DREF_OPERATIONAL_UPDATE,
1703+
EventStage.DREF_FINAL_REPORT,
1704+
],
1705+
then=Subquery(approved_dref_qs.values("id")[:1]),
1706+
),
1707+
default=Value(None),
1708+
output_field=IntegerField(null=True),
1709+
),
1710+
stage_final_report_id=Case(
1711+
When(
1712+
stage=EventStage.DREF_FINAL_REPORT,
1713+
then=Subquery(
1714+
DrefFinalReport.objects.filter(
1715+
dref__id=OuterRef("stage_dref_id"), status=Dref.Status.APPROVED
1716+
).values("id")[:1]
1717+
),
1718+
),
1719+
default=Value(None),
1720+
output_field=IntegerField(null=True),
1721+
),
1722+
stage_ops_update_id=Case(
1723+
When(
1724+
stage__in=[
1725+
EventStage.DREF_OPERATIONAL_UPDATE,
1726+
EventStage.DREF_FINAL_REPORT,
1727+
],
1728+
then=Subquery(
1729+
DrefOperationalUpdate.objects.filter(
1730+
dref__id=OuterRef("stage_dref_id"), status=Dref.Status.APPROVED
1731+
).values("id")[:1]
1732+
),
1733+
),
1734+
default=Value(None),
1735+
output_field=IntegerField(null=True),
1736+
),
1737+
stage_field_report_id=Case(
1738+
When(
1739+
stage=EventStage.FIELD_REPORT,
1740+
then=Subquery(field_report_qs.order_by("-updated_at", "-fr_num").values("id")[:1]),
1741+
),
1742+
default=Value(None),
1743+
output_field=IntegerField(null=True),
1744+
),
1745+
first_field_report_created_at=Subquery(field_report_qs.order_by("created_at", "fr_num").values("created_at")[:1]),
1746+
latest_field_report_created_at=Subquery(
1747+
field_report_qs.order_by("-created_at", "-fr_num").values("created_at")[:1]
1748+
),
1749+
)
1750+
.select_related("dtype")
1751+
.prefetch_related(
1752+
Prefetch(
1753+
"countries",
1754+
queryset=Country.objects.select_related("region"),
1755+
),
1756+
Prefetch(
1757+
"districts",
1758+
queryset=District.objects.select_related("country"),
1759+
),
1760+
Prefetch(
1761+
"key_figures",
1762+
queryset=KeyFigure.objects.all(),
1763+
),
1764+
Prefetch(
1765+
"contacts",
1766+
queryset=EventContact.objects.all(),
1767+
),
1768+
Prefetch(
1769+
"links",
1770+
queryset=EventLink.objects.all(),
1771+
),
1772+
Prefetch(
1773+
"featured_documents",
1774+
queryset=EventFeaturedDocument.objects.order_by("-id"),
16001775
),
1601-
appeal_id=Subquery(Appeal.objects.filter(event=OuterRef("pk")).order_by("-created_at").values("id")[:1]),
16021776
)
16031777
)

api/enums.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,5 @@
2121
"profile_org_types": models.Profile.OrgTypes,
2222
"supporting_type": models.CountrySupportingPartner.SupportingPartnerType,
2323
"event_source": models.Event.EventSource,
24+
"emergency_stage": models.EventStage,
2425
}

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/index_and_notify.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1035,7 +1035,7 @@ def handle(self, *args, **options):
10351035
condR = Q(real_data_update__gte=time_diff) # instead of modified at
10361036
cond2 = ~Q(previous_update__gte=time_diff_1_day) # negate (~) no previous_update in the last day, so send once a day
10371037
condF = Q(
1038-
source=Event.EventSource.NEW_REPORT
1038+
source=Event.EventSource.NEW_FIELD_REPORT
10391039
) # exclude those events that were generated from field reports, to avoid 2x notif.
10401040
condE = Q(status=CronJobStatus.ERRONEOUS)
10411041

api/management/commands/ingest_mdb.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ def handle(self, *args, **options):
248248
"dtype": report_dtype,
249249
"disaster_start_date": datetime.utcnow().replace(tzinfo=timezone.utc),
250250
"auto_generated": True,
251-
"source": Event.EventSource.REPORT_INGEST,
251+
"source": Event.EventSource.FIELD_REPORT_DMIS_INGEST,
252252
}
253253
event = Event(**event_record)
254254
event.save()

api/management/commands/migrate_event_source.py

Lines changed: 0 additions & 28 deletions
This file was deleted.

0 commit comments

Comments
 (0)