Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion api/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -619,7 +619,7 @@ def create_events(self, request, queryset):
dtype=getattr(report, "dtype"),
disaster_start_date=getattr(report, "created_at"),
auto_generated=True,
source=models.Event.EventSource.REPORT_ADMIN,
source=models.Event.EventSource.FIELD_REPORT_ADMIN,
)
if getattr(report, "countries").exists():
for country in report.countries.all():
Expand Down
236 changes: 205 additions & 31 deletions api/drf_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@
Avg,
Case,
Count,
Exists,
ExpressionWrapper,
F,
OuterRef,
Prefetch,
Q,
Subquery,
Sum,
Value,
When,
)
from django.db.models.fields import IntegerField
Expand Down Expand Up @@ -59,6 +61,7 @@
from databank.serializers import CountryOverviewSerializer
from deployments.models import ERU, Personnel
from deployments.serializers import ListDeployedERUByEventSerializer
from dref.models import Dref, DrefFinalReport, DrefOperationalUpdate
from main.enums import GlobalEnumSerializer, get_enum_values
from main.filters import NullsLastOrderingFilter
from main.permissions import DenyGuestUserMutationPermission, DenyGuestUserPermission
Expand All @@ -73,6 +76,7 @@
Appeal,
AppealDocument,
AppealHistory,
AppealStatus,
AppealType,
Country,
CountryKeyDocument,
Expand All @@ -85,7 +89,9 @@
Event,
EventContact,
EventFeaturedDocument,
EventLink,
EventSeverityLevelHistory,
EventStage,
Export,
ExternalPartner,
FieldReport,
Expand Down Expand Up @@ -749,11 +755,12 @@ def get_queryset(self, *args, **kwargs):
qset = super().get_queryset()
if self.action == "mini_events":
# return Event.objects.filter(parent_event__isnull=True).select_related('dtype')
return qset.filter(parent_event__isnull=True).select_related("dtype")
return qset.filter(parent_event__isnull=True).select_related("dtype").prefetch_related("countries_for_preview")

if self.action == "response_activity_events":
return (
qset.filter(parent_event__isnull=True)
.filter(Q(auto_generated=False) | Q(source=Event.EventSource.NEW_REPORT))
.filter(Q(auto_generated=False) | Q(source=Event.EventSource.NEW_FIELD_REPORT))
.select_related("dtype")
)
return (
Expand Down Expand Up @@ -869,7 +876,11 @@ def retrieve(self, request, pk=None, *args, **kwargs):
)
@action(methods=["get"], detail=False, url_path="mini")
def mini_events(self, request):
queryset = self.filter_queryset(self.get_queryset())
queryset = self.filter_queryset(self.get_queryset()).annotate(
latest_field_report_id=Subquery(
FieldReport.objects.filter(event=OuterRef("pk")).order_by("-updated_at").values("id")[:1]
)
)
serializer = ListMiniEventSerializer(queryset, many=True)
page = self.paginate_queryset(queryset)
if page is not None:
Expand Down Expand Up @@ -1351,7 +1362,7 @@ class SupportedActivityViewset(viewsets.ReadOnlyModelViewSet):
# summary=report.description or "",
# disaster_start_date=report.start_date,
# auto_generated=True,
# source=Event.EventSource.NEW_REPORT,
# source=Event.EventSource.NEW_FIELD_REPORT,
# visibility=report.visibility,
# **{TRANSLATOR_ORIGINAL_LANGUAGE_FIELD_NAME: django_get_language()},
# )
Expand Down Expand Up @@ -1559,45 +1570,208 @@ def get_queryset(self):
return CountrySupportingPartner.objects.select_related("country")


class EmergencyViewset(ReadOnlyVisibilityViewset):
class EmergencyViewset(
mixins.RetrieveModelMixin,
viewsets.GenericViewSet,
ReadOnlyVisibilityViewsetMixin,
):
queryset = Event.objects.all()
lookup_field = "id"
serializer_class = DetailEmergencySerializer
filterset_class = EventFilter
visibility_model_class = Event

def get_queryset(self):
today = timezone.now().date()

appeal_priority_qs = (
Appeal.objects.filter(
event=OuterRef("pk"),
status__in=[AppealStatus.ACTIVE, AppealStatus.CLOSED],
)
.annotate(
priority=Case(
When(status=AppealStatus.ACTIVE, then=Value(1)),
When(status=AppealStatus.CLOSED, then=Value(2)),
output_field=IntegerField(),
)
)
.order_by("priority", "-start_date")
)

active_dref_appeal_qs = appeal_priority_qs.filter(
atype=AppealType.DREF,
)

active_emergency_appeal_qs = appeal_priority_qs.filter(
atype=AppealType.APPEAL,
)

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

approved_dref_qs = Dref.objects.filter(
event=OuterRef("pk"),
status=Dref.Status.APPROVED,
)

approved_ops_update_qs = DrefOperationalUpdate.objects.filter(
dref__event=OuterRef("pk"),
status=Dref.Status.APPROVED,
).order_by("-created_at")

approved_final_report_qs = DrefFinalReport.objects.filter(
dref__event=OuterRef("pk"),
status=Dref.Status.APPROVED,
).order_by("-created_at")

return (
super()
.get_queryset()
.select_related(
"dtype",
"parent_event",
)
.prefetch_related(
"regions",
"countries",
"countries_for_preview",
Prefetch("key_figures", queryset=KeyFigure.objects.all()),
Prefetch("contacts", queryset=EventContact.objects.all()),
.annotate(
# Aggregated Values
response_activity_count=Count(
"emergency_projects",
distinct=True,
),
active_deployments_count=Count(
"personneldeployment__personnel",
filter=Q(
personneldeployment__personnel__type=Personnel.TypeChoices.RR,
personneldeployment__personnel__start_date__date__lte=today,
personneldeployment__personnel__end_date__date__gte=today,
personneldeployment__personnel__is_active=True,
),
distinct=True,
),
surge_alerts_count=Count(
"surgealert",
distinct=True,
),
# Stage
stage=Case(
When(
Exists(active_emergency_appeal_qs),
then=Value(EventStage.EMERGENCY_APPEAL),
),
When(
Exists(approved_final_report_qs),
then=Value(EventStage.DREF_FINAL_REPORT),
),
When(
Exists(approved_ops_update_qs),
then=Value(EventStage.DREF_OPERATIONAL_UPDATE),
),
When(
Exists(approved_dref_qs),
then=Value(EventStage.DREF_APPLICATION),
),
# If there is an active appeal of DREF type, but no approved DREF yet,
# we consider the emergency to be in the Dref Appeal only stage.
# Reaches here only if no approved DREF/ops-update/final-report exists
# So an active appeal type DREF appeal with no approved DREF = DREF_APPEAL_ONLY stage.
When(
Exists(active_dref_appeal_qs),
then=Value(EventStage.DREF_APPEAL_ONLY),
),
When(
Exists(FieldReport.objects.filter(event=OuterRef("pk"))),
then=Value(EventStage.FIELD_REPORT),
),
default=Value(None),
output_field=IntegerField(null=True),
),
)
.annotate(
first_field_report_id=Subquery(
FieldReport.objects.filter(event=OuterRef("pk"))
.order_by(
"fr_num",
"updated_at",
)
.values("id")[:1]
# Passing values for the current stage's instance,
# to avoid extra queries in serializer.
stage_appeal_id=Case(
When(
stage=EventStage.EMERGENCY_APPEAL,
then=Subquery(active_emergency_appeal_qs.values("id")[:1]),
),
When(
stage=EventStage.DREF_APPEAL_ONLY,
then=Subquery(active_dref_appeal_qs.values("id")[:1]),
),
default=Value(None),
output_field=IntegerField(null=True),
),
latest_field_report_id=Subquery(
FieldReport.objects.filter(event=OuterRef("pk"))
.order_by(
"-fr_num",
"-updated_at",
)
.values("id")[:1]
stage_dref_id=Case(
When(
stage__in=[
EventStage.DREF_APPLICATION,
EventStage.DREF_OPERATIONAL_UPDATE,
EventStage.DREF_FINAL_REPORT,
],
then=Subquery(approved_dref_qs.values("id")[:1]),
),
default=Value(None),
output_field=IntegerField(null=True),
),
stage_final_report_id=Case(
When(
stage=EventStage.DREF_FINAL_REPORT,
then=Subquery(
DrefFinalReport.objects.filter(
dref__id=OuterRef("stage_dref_id"), status=Dref.Status.APPROVED
).values("id")[:1]
),
),
default=Value(None),
output_field=IntegerField(null=True),
),
stage_ops_update_id=Case(
When(
stage__in=[
EventStage.DREF_OPERATIONAL_UPDATE,
EventStage.DREF_FINAL_REPORT,
],
then=Subquery(
DrefOperationalUpdate.objects.filter(
dref__id=OuterRef("stage_dref_id"), status=Dref.Status.APPROVED
).values("id")[:1]
),
),
default=Value(None),
output_field=IntegerField(null=True),
),
stage_field_report_id=Case(
When(
stage=EventStage.FIELD_REPORT,
then=Subquery(field_report_qs.order_by("-updated_at", "-fr_num").values("id")[:1]),
),
default=Value(None),
output_field=IntegerField(null=True),
),
first_field_report_created_at=Subquery(field_report_qs.order_by("created_at", "fr_num").values("created_at")[:1]),
latest_field_report_created_at=Subquery(
field_report_qs.order_by("-created_at", "-fr_num").values("created_at")[:1]
),
)
.select_related("dtype")
.prefetch_related(
Prefetch(
"countries",
queryset=Country.objects.select_related("region"),
),
Prefetch(
"districts",
queryset=District.objects.select_related("country"),
),
Prefetch(
"key_figures",
queryset=KeyFigure.objects.all(),
),
Prefetch(
"contacts",
queryset=EventContact.objects.all(),
),
Prefetch(
"links",
queryset=EventLink.objects.all(),
),
Prefetch(
"featured_documents",
queryset=EventFeaturedDocument.objects.order_by("-id"),
),
appeal_id=Subquery(Appeal.objects.filter(event=OuterRef("pk")).order_by("-created_at").values("id")[:1]),
)
)
1 change: 1 addition & 0 deletions api/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@
"profile_org_types": models.Profile.OrgTypes,
"supporting_type": models.CountrySupportingPartner.SupportingPartnerType,
"event_source": models.Event.EventSource,
"emergency_stage": models.EventStage,
}
1 change: 1 addition & 0 deletions api/factories/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class Meta:
name = fuzzy.FuzzyText(length=50)
slug = fuzzy.FuzzyText(length=50)
dtype = factory.SubFactory(DisasterTypeFactory)
source = fuzzy.FuzzyChoice(Event.EventSource)

@factory.post_generation
def districts(self, create, extracted, **kwargs):
Expand Down
2 changes: 1 addition & 1 deletion api/management/commands/index_and_notify.py
Original file line number Diff line number Diff line change
Expand Up @@ -1035,7 +1035,7 @@ def handle(self, *args, **options):
condR = Q(real_data_update__gte=time_diff) # instead of modified at
cond2 = ~Q(previous_update__gte=time_diff_1_day) # negate (~) no previous_update in the last day, so send once a day
condF = Q(
source=Event.EventSource.NEW_REPORT
source=Event.EventSource.NEW_FIELD_REPORT
) # exclude those events that were generated from field reports, to avoid 2x notif.
condE = Q(status=CronJobStatus.ERRONEOUS)

Expand Down
2 changes: 1 addition & 1 deletion api/management/commands/ingest_mdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ def handle(self, *args, **options):
"dtype": report_dtype,
"disaster_start_date": datetime.utcnow().replace(tzinfo=timezone.utc),
"auto_generated": True,
"source": Event.EventSource.REPORT_INGEST,
"source": Event.EventSource.FIELD_REPORT_DMIS_INGEST,
}
event = Event(**event_record)
event.save()
Expand Down
28 changes: 0 additions & 28 deletions api/management/commands/migrate_event_source.py

This file was deleted.

Loading
Loading