Skip to content

Commit 378167e

Browse files
authored
Merge pull request #2723 from IFRCGo/feat/add-emergency-api-endpoint
DEP: Add emergency api endpoint
2 parents 9182eed + fc79caf commit 378167e

18 files changed

Lines changed: 360 additions & 67 deletions

api/admin.py

Lines changed: 7 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626

2727
import api.models as models
2828
from api.admin_classes import RegionRestrictedAdmin
29-
from api.event_sources import SOURCES
3029
from api.management.commands.index_and_notify import Command as Notify
3130
from lang.admin import TranslationAdmin, TranslationInlineModelAdmin
3231
from notifications.models import RecordType, SubscriptionType
@@ -158,38 +157,6 @@ def queryset(self, request, queryset):
158157
return queryset.filter(is_featured=False)
159158

160159

161-
class EventSourceFilter(admin.SimpleListFilter):
162-
title = _("source")
163-
parameter_name = "event_source"
164-
165-
def lookups(self, request, model_admin):
166-
return (
167-
("input", _("Manual input")),
168-
("gdacs", _("GDACs scraper")),
169-
("who", _("WHO scraper")),
170-
("report_ingest", _("Field report ingest")),
171-
("report_admin", _("Field report admin")),
172-
("appeal_admin", _("Appeals admin")),
173-
("unknown", _("Unknown automated")),
174-
)
175-
176-
def queryset(self, request, queryset):
177-
if self.value() == "input":
178-
return queryset.filter(auto_generated=False)
179-
elif self.value() == "gdacs":
180-
return queryset.filter(auto_generated_source=SOURCES["gdacs"])
181-
elif self.value() == "who":
182-
return queryset.filter(auto_generated_source__startswith="www.who.int")
183-
elif self.value() == "report_ingest":
184-
return queryset.filter(auto_generated_source=SOURCES["report_ingest"])
185-
elif self.value() == "report_admin":
186-
return queryset.filter(auto_generated_source=SOURCES["report_admin"])
187-
elif self.value() == "appeal_admin":
188-
return queryset.filter(auto_generated_source=SOURCES["appeal_admin"])
189-
elif self.value() == "unknown":
190-
return queryset.filter(auto_generated=True).filter(auto_generated_source__isnull=True)
191-
192-
193160
class DisasterTypeAdmin(CompareVersionAdmin, TranslationAdmin, admin.ModelAdmin):
194161
search_fields = ("name",)
195162

@@ -246,9 +213,9 @@ def level_updated_at(self, obj):
246213
"cc_status",
247214
"glide",
248215
"auto_generated",
249-
"auto_generated_source",
216+
"source",
250217
)
251-
list_filter = [IsFeaturedFilter, EventSourceFilter]
218+
list_filter = [IsFeaturedFilter, "source"]
252219
actions = ["create_field_reports"]
253220
search_fields = (
254221
"name",
@@ -369,7 +336,7 @@ def changeform_view(self, request, object_id=None, form_url="", extra_context=No
369336
self.readonly_fields = (
370337
"appeals",
371338
"field_reports",
372-
"auto_generated_source",
339+
"source",
373340
"parent_event",
374341
"created_at",
375342
"updated_at",
@@ -378,9 +345,10 @@ def changeform_view(self, request, object_id=None, form_url="", extra_context=No
378345
self.readonly_fields = (
379346
"appeals",
380347
"field_reports",
381-
"auto_generated_source",
348+
"source",
382349
"created_at",
383350
"updated_at",
351+
"who_guid",
384352
)
385353

386354
# Set severity level from GET parameter
@@ -651,7 +619,7 @@ def create_events(self, request, queryset):
651619
dtype=getattr(report, "dtype"),
652620
disaster_start_date=getattr(report, "created_at"),
653621
auto_generated=True,
654-
auto_generated_source=SOURCES["report_admin"],
622+
source=models.Event.EventSource.REPORT_ADMIN,
655623
)
656624
if getattr(report, "countries").exists():
657625
for country in report.countries.all():
@@ -760,7 +728,7 @@ def create_events(self, request, queryset):
760728
dtype=getattr(appeal, "dtype"),
761729
disaster_start_date=getattr(appeal, "start_date"),
762730
auto_generated=True,
763-
auto_generated_source=SOURCES["appeal_admin"],
731+
source=models.Event.EventSource.APPEAL_ADMIN,
764732
)
765733
if appeal.country is not None:
766734
event.countries.add(appeal.country)

api/drf_views.py

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
Export,
9090
ExternalPartner,
9191
FieldReport,
92+
KeyFigure,
9293
MainContact,
9394
Profile,
9495
Region,
@@ -122,6 +123,7 @@
122123
CountrySupportingPartnerSerializer,
123124
CountryTableauSerializer,
124125
DeploymentsByEventSerializer,
126+
DetailEmergencySerializer,
125127
DetailEventSerializer,
126128
DisasterTypeSerializer,
127129
DistrictSerializer,
@@ -751,7 +753,7 @@ def get_queryset(self, *args, **kwargs):
751753
if self.action == "response_activity_events":
752754
return (
753755
qset.filter(parent_event__isnull=True)
754-
.filter(Q(auto_generated=False) | Q(auto_generated_source="New field report"))
756+
.filter(Q(auto_generated=False) | Q(source=Event.EventSource.NEW_REPORT))
755757
.select_related("dtype")
756758
)
757759
return (
@@ -1344,7 +1346,7 @@ class SupportedActivityViewset(viewsets.ReadOnlyModelViewSet):
13441346
# summary=report.description or "",
13451347
# disaster_start_date=report.start_date,
13461348
# auto_generated=True,
1347-
# auto_generated_source=SOURCES["new_report"],
1349+
# source=Event.EventSource.NEW_REPORT,
13481350
# visibility=report.visibility,
13491351
# **{TRANSLATOR_ORIGINAL_LANGUAGE_FIELD_NAME: django_get_language()},
13501352
# )
@@ -1542,3 +1544,47 @@ class CountrySupportingPartnerViewSet(viewsets.ModelViewSet):
15421544

15431545
def get_queryset(self):
15441546
return CountrySupportingPartner.objects.select_related("country")
1547+
1548+
1549+
class EmergencyViewset(ReadOnlyVisibilityViewset):
1550+
queryset = Event.objects.all()
1551+
lookup_field = "id"
1552+
serializer_class = DetailEmergencySerializer
1553+
filterset_class = EventFilter
1554+
visibility_model_class = Event
1555+
1556+
def get_queryset(self):
1557+
return (
1558+
super()
1559+
.get_queryset()
1560+
.select_related(
1561+
"dtype",
1562+
"parent_event",
1563+
)
1564+
.prefetch_related(
1565+
"regions",
1566+
"countries",
1567+
"countries_for_preview",
1568+
Prefetch("key_figures", queryset=KeyFigure.objects.all()),
1569+
Prefetch("contacts", queryset=EventContact.objects.all()),
1570+
)
1571+
.annotate(
1572+
first_field_report_id=Subquery(
1573+
FieldReport.objects.filter(event=OuterRef("pk"))
1574+
.order_by(
1575+
"fr_num",
1576+
"updated_at",
1577+
)
1578+
.values("id")[:1]
1579+
),
1580+
latest_field_report_id=Subquery(
1581+
FieldReport.objects.filter(event=OuterRef("pk"))
1582+
.order_by(
1583+
"-fr_num",
1584+
"-updated_at",
1585+
)
1586+
.values("id")[:1]
1587+
),
1588+
appeal_id=Subquery(Appeal.objects.filter(event=OuterRef("pk")).order_by("-created_at").values("id")[:1]),
1589+
)
1590+
)

api/enums.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@
2020
"action_category": models.ActionCategory,
2121
"profile_org_types": models.Profile.OrgTypes,
2222
"supporting_type": models.CountrySupportingPartner.SupportingPartnerType,
23+
"event_source": models.Event.EventSource,
2324
}

api/factories/event.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
AppealHistory,
1313
AppealType,
1414
Event,
15+
EventContact,
1516
EventFeaturedDocument,
1617
EventLink,
1718
)
@@ -72,7 +73,7 @@ def regions(self, create, extracted, **kwargs):
7273
previous_update = fuzzy.FuzzyDateTime(datetime.datetime(2008, 1, 1, tzinfo=pytz.utc))
7374

7475
auto_generated = fuzzy.FuzzyChoice([True, False])
75-
auto_generated_source = fuzzy.FuzzyText(length=50)
76+
source = fuzzy.FuzzyChoice(Event.EventSource.values)
7677

7778
is_featured = fuzzy.FuzzyChoice([True, False])
7879
is_featured_region = fuzzy.FuzzyChoice([True, False])
@@ -133,3 +134,9 @@ class AppealHistoryFactory(factory.django.DjangoModelFactory):
133134

134135
class Meta:
135136
model = AppealHistory
137+
138+
139+
class EventContactFactory(factory.django.DjangoModelFactory):
140+
141+
class Meta:
142+
model = EventContact

api/filter_set.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
from django.contrib.auth.models import User
33
from django.db import models
44

5-
from api.event_sources import SOURCES
65
from api.models import (
76
Admin2,
87
Appeal,
@@ -182,10 +181,8 @@ class EventFilter(filters.FilterSet):
182181
countries__in = ListFilter(field_name="countries__id")
183182
regions__in = ListFilter(field_name="regions__id")
184183
id = filters.NumberFilter(field_name="id", lookup_expr="exact")
185-
auto_generated_source = filters.ChoiceFilter(
186-
label="Auto generated source choices",
187-
choices=[(v, v) for v in SOURCES.values()],
188-
)
184+
source = filters.ChoiceFilter(choices=Event.EventSource.choices, lookup_expr="exact")
185+
189186
is_subscribed = filters.BooleanFilter(label="is_subscribed", method="get_is_subcribed_event")
190187

191188
class Meta:

api/management/commands/create_events.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from django.core.management.base import BaseCommand
22

3-
from api.event_sources import SOURCES
43
from api.models import Appeal, Event
54

65

@@ -18,7 +17,7 @@ def handle(self, *args, **options):
1817
"dtype": appeal.dtype,
1918
"disaster_start_date": appeal.start_date,
2019
"auto_generated": True,
21-
"auto_generated_source": SOURCES["appeal_admin"],
20+
"source": Event.EventSource.APPEAL_ADMIN,
2221
}
2322
event = Event.objects.create(**fields)
2423
if appeal.country is not None:

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-
auto_generated_source="New field report"
1038+
source=Event.EventSource.NEW_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_gdacs.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
from dateutil.parser import parse
44
from django.core.management.base import BaseCommand
55

6-
from api.event_sources import SOURCES
76
from api.logger import logger
87
from api.models import Country, CronJob, CronJobStatus, DisasterType, Event, GDACSEvent
98

@@ -104,7 +103,7 @@ def handle(self, *args, **options):
104103
"summary": data["description"],
105104
"disaster_start_date": data["publication_date"],
106105
"auto_generated": True,
107-
"auto_generated_source": SOURCES["gdacs"],
106+
"source": Event.EventSource.GDACS,
108107
"ifrc_severity_level": data["alert_level"],
109108
}
110109
event = Event.objects.create(**fields)

api/management/commands/ingest_mdb.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
from django.core.management.base import BaseCommand
1414
from django.utils import timezone
1515

16-
from api.event_sources import SOURCES
1716
from api.fixtures.dtype_map import PK_MAP
1817
from api.logger import logger
1918
from api.models import (
@@ -249,7 +248,7 @@ def handle(self, *args, **options):
249248
"dtype": report_dtype,
250249
"disaster_start_date": datetime.utcnow().replace(tzinfo=timezone.utc),
251250
"auto_generated": True,
252-
"auto_generated_source": SOURCES["report_ingest"],
251+
"source": Event.EventSource.REPORT_INGEST,
253252
}
254253
event = Event(**event_record)
255254
event.save()

api/management/commands/ingest_who.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ class Command(BaseCommand):
1111
help = "Add new event (=emergency) entries from WHO API"
1212

1313
def handle(self, *args, **options):
14-
guids = [e.auto_generated_source for e in Event.objects.filter(auto_generated_source__startswith="www.who.int")]
14+
# Change this at the top
15+
guids = list(Event.objects.filter(source=Event.EventSource.WHO).values_list("who_guid", flat=True))
1516

1617
logger.info("Querying WHO RSS feed for new emergency data")
1718
# get latest
@@ -48,10 +49,15 @@ def handle(self, *args, **options):
4849
"category": row.pop("category"),
4950
"pubDate": row.pop("pubDate"),
5051
}
51-
if data["guid"] in guids:
52-
continue
5352
if data["guid"] in ["WeDontWantThis", "NeitherThis"]:
5453
continue
54+
try:
55+
who_guid = int(data["guid"].strip().split(".")[-1])
56+
except (ValueError, IndexError):
57+
who_guid = None
58+
59+
if who_guid is None or who_guid in guids:
60+
continue
5561

5662
title = data["title"] # for csr link
5763
short = title.replace(" (ex-China)", "")
@@ -131,7 +137,8 @@ def handle(self, *args, **options):
131137
"summary": summary,
132138
"disaster_start_date": date,
133139
"auto_generated": True,
134-
"auto_generated_source": data["guid"],
140+
"source": Event.EventSource.WHO,
141+
"who_guid": who_guid,
135142
"ifrc_severity_level": alert_level,
136143
}
137144
# TODO: fields['name'] sometimes exceeds 100 maxlength, so will need some altering if this will be used

0 commit comments

Comments
 (0)