Skip to content
Draft
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
12 changes: 11 additions & 1 deletion api/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,8 @@ def changeform_view(self, request, object_id=None, form_url="", extra_context=No
"auto_generated_source",
"created_at",
"updated_at",
"auto_generated_external_source",
"auto_generated_external_source_id",
)

# Set severity level from GET parameter
Expand Down Expand Up @@ -634,7 +636,15 @@ def assist(self, obj):
"districts",
)

readonly_fields = ("report_date", "created_at", "updated_at", "summary", "fr_num")
readonly_fields = (
"report_date",
"created_at",
"updated_at",
"summary",
"fr_num",
"external_source",
"external_source_id",
)
list_filter = [MembershipFilter, "ns_request_assistance"]
actions = [
"create_events",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Generated by Django 5.2.14 on 2026-06-10 04:46

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('api', '0231_alter_export_export_type'),
]

operations = [
migrations.AddField(
model_name='event',
name='auto_generated_external_source',
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'),
),
migrations.AddField(
model_name='event',
name='auto_generated_external_source_id',
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'),
),
migrations.AddField(
model_name='fieldreport',
name='external_source',
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'),
),
migrations.AddField(
model_name='fieldreport',
name='external_source_id',
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'),
),
]
34 changes: 32 additions & 2 deletions api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -828,7 +828,22 @@ class Event(models.Model):
auto_generated_source = models.CharField(
verbose_name=_("auto generated source"), max_length=50, null=True, blank=True, editable=False
)

# External system or source from which the event data originated
auto_generated_external_source = models.CharField(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we even need these fields here in the event table? Do we need filtering for this features?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we may need these for filtering in the future. @frozenhelium , can you also confirm this?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's correct!

verbose_name=_("external source"),
max_length=100,
null=True,
blank=True,
help_text=_("External source or system from which the original event data originated."),
)
# Identifier of the originating event record in the external system
auto_generated_external_source_id = models.CharField(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason for using CharField here?

This looks like it should be a UUID field.

If this value is always expected to be a UUID, would it be possible to use a UUIDField instead? That would provide built-in validation.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the MRCS site, we are not using UUIDs for the id field. If it’s coming externally as a UUID, we can switch to a UUIDField. @frozenhelium can you also confirm this?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct!

verbose_name=_("external source ID"),
max_length=100,
null=True,
blank=True,
help_text=_("Unique identifier of the event record in the external source system."),
)
# Meant to give the organization a way of highlighting certain, important events.
is_featured = models.BooleanField(default=False, verbose_name=_("is featured on home page"))

Expand Down Expand Up @@ -2473,7 +2488,22 @@ class RecentAffected(models.IntegerChoices):
default=0,
help_text='<a target="_blank" href="/api/v2/recentaffected">Key/value pairs</a>',
)

# External system from which this Field Report created.
external_source = models.CharField(
verbose_name=_("external source"),
max_length=100,
null=True,
blank=True,
help_text=_("External source system from which this field report created."),
)
# Identifier of the report in the external system.
external_source_id = models.CharField(
verbose_name=_("external source ID"),
max_length=100,
null=True,
blank=True,
help_text=_("Unique identifier of this field report in the external source system."),
)
# start_date is now what the user explicitly sets while filling the Field Report form.
start_date = models.DateTimeField(verbose_name=_("start date"), blank=True, null=True)

Expand Down
2 changes: 2 additions & 0 deletions api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2219,6 +2219,8 @@ def create_event(self, report):
disaster_start_date=report.start_date,
auto_generated=True,
auto_generated_source=SOURCES["new_report"],
auto_generated_external_source=report.external_source or None,
auto_generated_external_source_id=report.external_source_id or None,
visibility=report.visibility,
**{TRANSLATOR_ORIGINAL_LANGUAGE_FIELD_NAME: django_get_language()},
)
Expand Down
85 changes: 85 additions & 0 deletions api/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
from django.utils import timezone

import api.models as models
from api.factories.country import CountryFactory
from api.factories.disaster_type import DisasterTypeFactory
from api.factories.event import (
AppealFactory,
AppealType,
Expand All @@ -20,6 +22,7 @@
EventLinkFactory,
)
from api.factories.field_report import FieldReportFactory
from api.factories.region import RegionFactory
from api.models import Profile, VisibilityChoices
from deployments.factories.user import UserFactory
from dref.models import DrefFile
Expand Down Expand Up @@ -542,6 +545,88 @@ def test_create_and_update(self):
# )


class FieldReportEventCreationTest(APITestCase):

def setUp(self):
self.user = UserFactory()
self.region = RegionFactory.create(name=1)
self.country1 = CountryFactory(name="test abc", region=self.region)
self.country2 = CountryFactory(name="test xyz", region=self.region)
self.disaster_type = DisasterTypeFactory.create(name="disaster 1")
self.url = "/api/v2/field-report/"

# Test 1 without external source
def test_event_creation(self):
data = {
"countries": [self.country1.id, self.country2.id],
"dtype": self.disaster_type.id,
"summary": "test",
"description": "this is a test description",
"num_assisted": 100,
"visibility": 2,
}
self.client.force_authenticate(self.user)
response = self.client.post(
self.url,
data=data,
format="json",
)
self.assert_201(response)
field_report_id = response.data["id"]

field_report = models.FieldReport.objects.get(id=field_report_id)
self.assertIsNotNone(field_report.event)

event = field_report.event
self.assertEqual(event.name, field_report.summary)
self.assertEqual(event.dtype, field_report.dtype)
self.assertTrue(event.auto_generated)

self.assertIn(self.country1, event.countries.all())
self.assertIn(self.country2, event.countries.all())
self.assertIn(self.region, event.regions.all())

self.assertIsNone(event.auto_generated_external_source)
self.assertIsNone(event.auto_generated_external_source_id)

# Test with external source
def test_event_creation_with_external_source(self):

external_source_id = str(uuid.uuid4())
data = {
"countries": [self.country1.id, self.country2.id],
"dtype": self.disaster_type.id,
"summary": "test",
"description": "this is a test description",
"num_assisted": 100,
"visibility": 2,
"external_source": "mrcs",
"external_source_id": external_source_id,
}
self.client.force_authenticate(self.user)
response = self.client.post(
self.url,
data=data,
format="json",
)
self.assert_201(response)
field_report_id = response.data["id"]
field_report = models.FieldReport.objects.get(id=field_report_id)

self.assertIsNotNone(field_report.event)

event = field_report.event
self.assertEqual(event.name, field_report.summary)
self.assertTrue(event.auto_generated)

self.assertEqual(event.auto_generated_external_source, "mrcs")
self.assertEqual(event.auto_generated_external_source_id, external_source_id)

self.assertIn(self.country1, event.countries.all())
self.assertIn(self.country2, event.countries.all())
self.assertIn(self.region, event.regions.all())


class VisibilityTest(APITestCase):
def test_country_snippet_visibility(self):
country = models.Country.objects.create(name="1")
Expand Down
2 changes: 1 addition & 1 deletion assets
Submodule assets updated 1 files
+22 −0 openapi-schema.yaml
Loading