Skip to content

Commit 02f7b18

Browse files
committed
feat(emergency): Add new stage for appeal dref type
- update on stage logic generation - rename enums of the EventSource - update test cases - add source field from auto generated source on event - generate new migrations for the emergency, dref and deployments
1 parent 4829ff5 commit 02f7b18

12 files changed

Lines changed: 152 additions & 68 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: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
Event,
9090
EventContact,
9191
EventFeaturedDocument,
92+
EventLink,
9293
EventSeverityLevelHistory,
9394
EventStage,
9495
Export,
@@ -759,7 +760,7 @@ def get_queryset(self, *args, **kwargs):
759760
if self.action == "response_activity_events":
760761
return (
761762
qset.filter(parent_event__isnull=True)
762-
.filter(Q(auto_generated=False) | Q(source=Event.EventSource.NEW_REPORT))
763+
.filter(Q(auto_generated=False) | Q(source=Event.EventSource.NEW_FIELD_REPORT))
763764
.select_related("dtype")
764765
)
765766
return (
@@ -1361,7 +1362,7 @@ class SupportedActivityViewset(viewsets.ReadOnlyModelViewSet):
13611362
# summary=report.description or "",
13621363
# disaster_start_date=report.start_date,
13631364
# auto_generated=True,
1364-
# source=Event.EventSource.NEW_REPORT,
1365+
# source=Event.EventSource.NEW_FIELD_REPORT,
13651366
# visibility=report.visibility,
13661367
# **{TRANSLATOR_ORIGINAL_LANGUAGE_FIELD_NAME: django_get_language()},
13671368
# )
@@ -1658,10 +1659,10 @@ def get_queryset(self):
16581659
# If there is an active appeal of DREF type, but no approved DREF yet,
16591660
# we consider the emergency to be in the Emergency Appeal stage.
16601661
# 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+
# So an active appeal type DREF appeal with no approved DREF = DREF_APPEAL_ONLY stage.
16621663
When(
16631664
Exists(active_dref_appeal_qs),
1664-
then=Value(EventStage.EMERGENCY_APPEAL),
1665+
then=Value(EventStage.DREF_APPEAL_ONLY),
16651666
),
16661667
When(
16671668
Exists(FieldReport.objects.filter(event=OuterRef("pk"))),
@@ -1676,7 +1677,10 @@ def get_queryset(self):
16761677
# to avoid extra queries in serializer.
16771678
stage_appeal_id=Case(
16781679
When(
1679-
stage=EventStage.EMERGENCY_APPEAL,
1680+
stage__in=[
1681+
EventStage.EMERGENCY_APPEAL,
1682+
EventStage.DREF_APPEAL_ONLY,
1683+
],
16801684
then=Subquery(latest_appeal_qs.values("id")[:1]),
16811685
),
16821686
default=Value(None),
@@ -1737,6 +1741,10 @@ def get_queryset(self):
17371741
"countries",
17381742
queryset=Country.objects.select_related("region"),
17391743
),
1744+
Prefetch(
1745+
"districts",
1746+
queryset=District.objects.select_related("country"),
1747+
),
17401748
Prefetch(
17411749
"key_figures",
17421750
queryset=KeyFigure.objects.all(),
@@ -1745,5 +1753,13 @@ def get_queryset(self):
17451753
"contacts",
17461754
queryset=EventContact.objects.all(),
17471755
),
1756+
Prefetch(
1757+
"links",
1758+
queryset=EventLink.objects.all(),
1759+
),
1760+
Prefetch(
1761+
"featured_documents",
1762+
queryset=EventFeaturedDocument.objects.order_by("-id"),
1763+
),
17481764
)
17491765
)

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/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.
Lines changed: 79 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,93 @@
1-
# Generated by Django 4.2.29 on 2026-05-12 08:28
1+
# Generated by Django 4.2.30 on 2026-05-21 04:49
22

33
from django.db import migrations, models
44

55

6-
class Migration(migrations.Migration):
6+
def migrate_sources(apps, _):
7+
"""
8+
Populate the source field for Event records:
9+
- Non auto generated events are mapped to MANUAL_INPUT.
10+
- Events that are auto generated and have auto_generated_source are mapped to the corresponding enum value.
11+
- Events that are auto generated but do not have auto_generated_source are mapped to MANUAL_INPUT.
12+
- For WHO, the who_guid field is populated with the id extracted from the auto_generated_source field.
13+
"""
14+
15+
Event = apps.get_model("api", "Event")
16+
17+
for obj in Event.objects.iterator():
18+
update_fields = ["source"]
19+
20+
if not obj.auto_generated:
21+
source = 100 # MANUAL_INPUT
22+
23+
else:
24+
raw_source = (obj.auto_generated_source or "").lower()
25+
if "gdacs" in raw_source:
26+
source = 110 # GDACS
27+
28+
elif "who.int" in raw_source:
29+
source = 120
30+
31+
parts = raw_source.strip().split(".")
32+
who_id = parts[-1].strip()
733

34+
try:
35+
obj.who_guid = int(who_id)
36+
update_fields.append("who_guid")
37+
except (ValueError, TypeError):
38+
pass
39+
elif "field report dmis ingest" in raw_source:
40+
source = 130 # FIELD_REPORT_DMIS_INGEST
41+
elif "field report admin" in raw_source:
42+
source = 140 # FIELD_REPORT_ADMIN
43+
elif "appeal" in raw_source:
44+
source = 150 # APPEAL_ADMIN
45+
elif "new field report" in raw_source:
46+
source = 160 # NEW_FIELD_REPORT
47+
else:
48+
# 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"):
50+
source = 110 # GDACS
51+
else:
52+
source = 100 # MANUAL_INPUT
53+
obj.auto_generated = False
54+
update_fields.append("auto_generated")
55+
obj.source = source
56+
obj.save(update_fields=update_fields)
57+
58+
59+
class Migration(migrations.Migration):
860
dependencies = [
9-
('api', '0231_alter_export_export_type'),
61+
("api", "0231_alter_export_export_type"),
1062
]
1163

1264
operations = [
13-
migrations.RemoveField(
14-
model_name='event',
15-
name='auto_generated_source',
16-
),
1765
migrations.AddField(
18-
model_name='event',
19-
name='source',
20-
field=models.IntegerField(choices=[(100, 'Manual input'), (110, 'GDACs scraper'), (120, 'WHO scraper'), (130, 'Field report DMIS ingest'), (140, 'Field report admin'), (150, 'Appeal admin'), (160, 'New field report'), (170, 'DREF')], default=100, verbose_name='Event source'),
66+
model_name="event",
67+
name="source",
68+
field=models.IntegerField(
69+
choices=[
70+
(100, "Manual input"),
71+
(110, "GDACs scraper"),
72+
(120, "WHO scraper"),
73+
(130, "Field report DMIS ingest"),
74+
(140, "Field report admin"),
75+
(150, "Appeal admin"),
76+
(160, "New field report"),
77+
(170, "DREF"),
78+
],
79+
default=100,
80+
verbose_name="Event source",
81+
),
2182
),
2283
migrations.AddField(
23-
model_name='event',
24-
name='who_guid',
25-
field=models.IntegerField(blank=True, null=True, verbose_name='Who guid'),
84+
model_name="event",
85+
name="who_guid",
86+
field=models.IntegerField(blank=True, null=True, verbose_name="Who guid"),
87+
),
88+
migrations.RunPython(migrate_sources, reverse_code=migrations.RunPython.noop),
89+
migrations.RemoveField(
90+
model_name="event",
91+
name="auto_generated_source",
2692
),
2793
]

api/models.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -768,6 +768,7 @@ class EventStage(models.IntegerChoices):
768768
DREF_OPERATIONAL_UPDATE = 3, _("DREF Operational Update")
769769
DREF_FINAL_REPORT = 4, _("DREF Final Report")
770770
FIELD_REPORT = 5, _("Field Report")
771+
DREF_APPEAL_ONLY = 6, _("DREF")
771772

772773

773774
# NOTE: If ever in future we need to create an api to update the event table
@@ -778,7 +779,6 @@ class Event(models.Model):
778779

779780
class EventSource(models.IntegerChoices):
780781

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

@@ -788,17 +788,17 @@ class EventSource(models.IntegerChoices):
788788
WHO = 120, _("WHO scraper")
789789
"""WHO: Event data automatically ingested from the (WHO) scraper."""
790790

791-
REPORT_INGEST = 130, _("Field report DMIS ingest")
792-
"""REPORT_INGEST: Event data imported through the DMIS field report."""
791+
FIELD_REPORT_DMIS_INGEST = 130, _("Field report DMIS ingest")
792+
"""FIELD_REPORT_DMIS_INGEST: Event data imported through the DMIS field report."""
793793

794-
REPORT_ADMIN = 140, _("Field report admin")
795-
"""REPORT_ADMIN: Event data created or modified via the field report administration interface."""
794+
FIELD_REPORT_ADMIN = 140, _("Field report admin")
795+
"""FIELD_REPORT_ADMIN: Event data created or modified via the field report administration interface."""
796796

797797
APPEAL_ADMIN = 150, _("Appeal admin")
798798
"""APPEAL_ADMIN: Event data created or managed through the appeal administration interface."""
799799

800-
NEW_REPORT = 160, _("New field report")
801-
"""NEW_REPORT: Event data originating from newly created field reports."""
800+
NEW_FIELD_REPORT = 160, _("New field report")
801+
"""NEW_FIELD_REPORT: Event data originating from newly created field reports."""
802802

803803
DREF = 170, _("DREF")
804804
"""DREF: Event originating records."""

0 commit comments

Comments
 (0)