Skip to content

Commit d6b5ff6

Browse files
committed
feat(api): include external source fields in auto generated event from field report
- Added auto_generated_external_source field to Event - Added auto_generated_external_source_id field to Event - Set both fields during automatic event creation from a FieldReport
1 parent 61b8481 commit d6b5ff6

6 files changed

Lines changed: 164 additions & 4 deletions

File tree

api/admin.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,8 @@ def changeform_view(self, request, object_id=None, form_url="", extra_context=No
381381
"auto_generated_source",
382382
"created_at",
383383
"updated_at",
384+
"auto_generated_external_source",
385+
"auto_generated_external_source_id",
384386
)
385387

386388
# Set severity level from GET parameter
@@ -634,7 +636,15 @@ def assist(self, obj):
634636
"districts",
635637
)
636638

637-
readonly_fields = ("report_date", "created_at", "updated_at", "summary", "fr_num")
639+
readonly_fields = (
640+
"report_date",
641+
"created_at",
642+
"updated_at",
643+
"summary",
644+
"fr_num",
645+
"external_source",
646+
"external_source_id",
647+
)
638648
list_filter = [MembershipFilter, "ns_request_assistance"]
639649
actions = [
640650
"create_events",
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Generated by Django 5.2.14 on 2026-06-10 04:46
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('api', '0231_alter_export_export_type'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='event',
15+
name='auto_generated_external_source',
16+
field=models.CharField(blank=True, help_text='External source or system from which the original event data originated.', max_length=100, null=True, verbose_name='external source'),
17+
),
18+
migrations.AddField(
19+
model_name='event',
20+
name='auto_generated_external_source_id',
21+
field=models.CharField(blank=True, help_text='Unique identifier of the event record in the external source system.', max_length=100, null=True, verbose_name='external source ID'),
22+
),
23+
migrations.AddField(
24+
model_name='fieldreport',
25+
name='external_source',
26+
field=models.CharField(blank=True, help_text='External source system from which this field report created.', max_length=100, null=True, verbose_name='external source'),
27+
),
28+
migrations.AddField(
29+
model_name='fieldreport',
30+
name='external_source_id',
31+
field=models.CharField(blank=True, help_text='Unique identifier of this field report in the external source system.', max_length=100, null=True, verbose_name='external source ID'),
32+
),
33+
]

api/models.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -828,7 +828,22 @@ class Event(models.Model):
828828
auto_generated_source = models.CharField(
829829
verbose_name=_("auto generated source"), max_length=50, null=True, blank=True, editable=False
830830
)
831-
831+
# External system or source from which the event data originated
832+
auto_generated_external_source = models.CharField(
833+
verbose_name=_("external source"),
834+
max_length=100,
835+
null=True,
836+
blank=True,
837+
help_text=_("External source or system from which the original event data originated."),
838+
)
839+
# Identifier of the originating event record in the external system
840+
auto_generated_external_source_id = models.CharField(
841+
verbose_name=_("external source ID"),
842+
max_length=100,
843+
null=True,
844+
blank=True,
845+
help_text=_("Unique identifier of the event record in the external source system."),
846+
)
832847
# Meant to give the organization a way of highlighting certain, important events.
833848
is_featured = models.BooleanField(default=False, verbose_name=_("is featured on home page"))
834849

@@ -2473,7 +2488,22 @@ class RecentAffected(models.IntegerChoices):
24732488
default=0,
24742489
help_text='<a target="_blank" href="/api/v2/recentaffected">Key/value pairs</a>',
24752490
)
2476-
2491+
# External system from which this Field Report created.
2492+
external_source = models.CharField(
2493+
verbose_name=_("external source"),
2494+
max_length=100,
2495+
null=True,
2496+
blank=True,
2497+
help_text=_("External source system from which this field report created."),
2498+
)
2499+
# Identifier of the report in the external system.
2500+
external_source_id = models.CharField(
2501+
verbose_name=_("external source ID"),
2502+
max_length=100,
2503+
null=True,
2504+
blank=True,
2505+
help_text=_("Unique identifier of this field report in the external source system."),
2506+
)
24772507
# start_date is now what the user explicitly sets while filling the Field Report form.
24782508
start_date = models.DateTimeField(verbose_name=_("start date"), blank=True, null=True)
24792509

api/serializers.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2219,6 +2219,8 @@ def create_event(self, report):
22192219
disaster_start_date=report.start_date,
22202220
auto_generated=True,
22212221
auto_generated_source=SOURCES["new_report"],
2222+
auto_generated_external_source=report.external_source or None,
2223+
auto_generated_external_source_id=report.external_source_id or None,
22222224
visibility=report.visibility,
22232225
**{TRANSLATOR_ORIGINAL_LANGUAGE_FIELD_NAME: django_get_language()},
22242226
)

api/test_views.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
from django.utils import timezone
1313

1414
import api.models as models
15+
from api.factories.country import CountryFactory
16+
from api.factories.disaster_type import DisasterTypeFactory
1517
from api.factories.event import (
1618
AppealFactory,
1719
AppealType,
@@ -20,6 +22,7 @@
2022
EventLinkFactory,
2123
)
2224
from api.factories.field_report import FieldReportFactory
25+
from api.factories.region import RegionFactory
2326
from api.models import Profile, VisibilityChoices
2427
from deployments.factories.user import UserFactory
2528
from dref.models import DrefFile
@@ -542,6 +545,88 @@ def test_create_and_update(self):
542545
# )
543546

544547

548+
class FieldReportEventCreationTest(APITestCase):
549+
550+
def setUp(self):
551+
self.user = UserFactory()
552+
self.region = RegionFactory.create(name=1)
553+
self.country1 = CountryFactory(name="test abc", region=self.region)
554+
self.country2 = CountryFactory(name="test xyz", region=self.region)
555+
self.disaster_type = DisasterTypeFactory.create(name="disaster 1")
556+
self.url = "/api/v2/field-report/"
557+
558+
# Test 1 without external source
559+
def test_event_creation(self):
560+
data = {
561+
"countries": [self.country1.id, self.country2.id],
562+
"dtype": self.disaster_type.id,
563+
"summary": "test",
564+
"description": "this is a test description",
565+
"num_assisted": 100,
566+
"visibility": 2,
567+
}
568+
self.client.force_authenticate(self.user)
569+
response = self.client.post(
570+
self.url,
571+
data=data,
572+
format="json",
573+
)
574+
self.assert_201(response)
575+
field_report_id = response.data["id"]
576+
577+
field_report = models.FieldReport.objects.get(id=field_report_id)
578+
self.assertIsNotNone(field_report.event)
579+
580+
event = field_report.event
581+
self.assertEqual(event.name, field_report.summary)
582+
self.assertEqual(event.dtype, field_report.dtype)
583+
self.assertTrue(event.auto_generated)
584+
585+
self.assertIn(self.country1, event.countries.all())
586+
self.assertIn(self.country2, event.countries.all())
587+
self.assertIn(self.region, event.regions.all())
588+
589+
self.assertIsNone(event.auto_generated_external_source)
590+
self.assertIsNone(event.auto_generated_external_source_id)
591+
592+
# Test with external source
593+
def test_event_creation_with_external_source(self):
594+
595+
external_source_id = str(uuid.uuid4())
596+
data = {
597+
"countries": [self.country1.id, self.country2.id],
598+
"dtype": self.disaster_type.id,
599+
"summary": "test",
600+
"description": "this is a test description",
601+
"num_assisted": 100,
602+
"visibility": 2,
603+
"external_source": "mrcs",
604+
"external_source_id": external_source_id,
605+
}
606+
self.client.force_authenticate(self.user)
607+
response = self.client.post(
608+
self.url,
609+
data=data,
610+
format="json",
611+
)
612+
self.assert_201(response)
613+
field_report_id = response.data["id"]
614+
field_report = models.FieldReport.objects.get(id=field_report_id)
615+
616+
self.assertIsNotNone(field_report.event)
617+
618+
event = field_report.event
619+
self.assertEqual(event.name, field_report.summary)
620+
self.assertTrue(event.auto_generated)
621+
622+
self.assertEqual(event.auto_generated_external_source, "mrcs")
623+
self.assertEqual(event.auto_generated_external_source_id, external_source_id)
624+
625+
self.assertIn(self.country1, event.countries.all())
626+
self.assertIn(self.country2, event.countries.all())
627+
self.assertIn(self.region, event.regions.all())
628+
629+
545630
class VisibilityTest(APITestCase):
546631
def test_country_snippet_visibility(self):
547632
country = models.Country.objects.create(name="1")

assets

0 commit comments

Comments
 (0)