diff --git a/api/filter_set.py b/api/filter_set.py index 05b696afb..5e85e1fca 100644 --- a/api/filter_set.py +++ b/api/filter_set.py @@ -365,6 +365,7 @@ class FieldReportFilter(filters.FilterSet): id = filters.NumberFilter(field_name="id", lookup_expr="exact") is_covid_report = filters.BooleanFilter(field_name="is_covid_report") summary = filters.CharFilter(field_name="summary", lookup_expr="icontains") + event = filters.NumberFilter(field_name="event", lookup_expr="exact") class Meta: model = FieldReport diff --git a/api/serializers.py b/api/serializers.py index 8a6b08e91..bbeea9c4a 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -1078,6 +1078,7 @@ class ListEventSerializer(ModelSerializer): dtype = DisasterTypeSerializer(required=False) ifrc_severity_level_display = serializers.CharField(source="get_ifrc_severity_level_display", read_only=True) active_deployments = serializers.IntegerField(read_only=True) + visibility_display = serializers.CharField(source="get_visibility_display", read_only=True) class Meta: model = Event @@ -1107,6 +1108,8 @@ class Meta: "tab_three_title", "emergency_response_contact_email", "active_deployments", + "visibility", + "visibility_display", ) @@ -2179,7 +2182,7 @@ class FieldReportSerializer( countries_details = MiniCountrySerializer(source="countries", many=True, read_only=True) districts_details = MiniDistrictSerializer(source="districts", many=True, read_only=True) regions_details = RegionSerializer(source="regions", many=True, read_only=True) - event_details = MiniEventSerializer(source="event", read_only=True) + event_details = ListEventSerializer(source="event", read_only=True) dtype_details = DisasterTypeSerializer(source="dtype", read_only=True) external_partners_details = ExternalPartnerSerializer(source="external_partners", many=True, read_only=True) supported_activities_details = SupportedActivitySerializer(source="supported_activities", many=True, read_only=True) diff --git a/assets b/assets index 6614f72f5..6bc9eeafc 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 6614f72f5174333c67f43ca533513972c2581e9c +Subproject commit 6bc9eeafc135de9c0345ae10ed1e0234f9e5847a diff --git a/dref/filter_set.py b/dref/filter_set.py index 2e27ebf18..a1ad9ee03 100644 --- a/dref/filter_set.py +++ b/dref/filter_set.py @@ -39,6 +39,7 @@ class BaseDrefFilterSet(filters.FilterSet): field_name="disaster_type", queryset=DisasterType.objects.all(), ) + event = filters.NumberFilter(field_name="event", lookup_expr="exact") appeal_code = filters.CharFilter(field_name="appeal_code", lookup_expr="icontains") diff --git a/dref/management/commands/migrate_event.py b/dref/management/commands/migrate_event.py new file mode 100644 index 000000000..41a80d022 --- /dev/null +++ b/dref/management/commands/migrate_event.py @@ -0,0 +1,28 @@ +from django.core.management.base import BaseCommand + +from api.models import Appeal, AppealType +from dref.models import Dref + + +class Command(BaseCommand): + help = "Migrate related Event to Dref" + + def handle(self, *args, **options): + self.stdout.write(self.style.NOTICE("Starting migration of events to Dref...")) + + appeal_qs = Appeal.objects.filter(atype=AppealType.DREF).exclude(code__isnull=True) + + appeal_map = {appeal.code: appeal.event for appeal in appeal_qs if appeal.event} + + drefs = Dref.objects.exclude(appeal_code__isnull=True) + self.stdout.write(self.style.NOTICE(f"Total Dref records with appeal code:{drefs.count()}")) + count = 0 + + for dref in drefs: + event_id = appeal_map.get(dref.appeal_code) + if event_id: + dref.event = event_id + count += 1 + + Dref.objects.bulk_update(drefs, ["event"]) + self.stdout.write(self.style.SUCCESS(f"Updated {count} Dref records with related Event")) diff --git a/dref/migrations/0089_dref_event.py b/dref/migrations/0089_dref_event.py new file mode 100644 index 000000000..3b68d9a52 --- /dev/null +++ b/dref/migrations/0089_dref_event.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.29 on 2026-04-06 08:02 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0230_alter_districtgeoms_district'), + ('dref', '0088_remove_identifiedneed_title_ar_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='dref', + name='event', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='dref_event', to='api.event', verbose_name='event'), + ), + ] diff --git a/dref/models.py b/dref/models.py index dc8ae4228..4bb4fa811 100644 --- a/dref/models.py +++ b/dref/models.py @@ -11,7 +11,7 @@ from django.utils.translation import gettext_lazy as _ from pdf2image import convert_from_bytes -from api.models import Country, DisasterType, District, FieldReport +from api.models import Country, DisasterType, District, Event, FieldReport from deployments.models import Sector from main.fields import SecureFileField @@ -295,6 +295,15 @@ class Status(models.IntegerChoices): related_name="modified_by_dref", ) users = models.ManyToManyField(settings.AUTH_USER_MODEL, verbose_name=_("users"), blank=True, related_name="user_dref") + + event = models.ForeignKey[Event]( + Event, + verbose_name=_("event"), + on_delete=models.SET_NULL, + null=True, + blank=True, + related_name="dref_event", + ) field_report = models.ForeignKey( FieldReport, verbose_name=_("field report"), diff --git a/dref/serializers.py b/dref/serializers.py index f3801fa33..f796da5f6 100644 --- a/dref/serializers.py +++ b/dref/serializers.py @@ -11,9 +11,10 @@ from drf_spectacular.utils import extend_schema_field from rest_framework import serializers -from api.models import Appeal +from api.models import Appeal, Event from api.serializers import ( DisasterTypeSerializer, + ListEventSerializer, MiniCountrySerializer, MiniDistrictSerializer, UserNameSerializer, @@ -217,6 +218,7 @@ class MiniDrefSerializer(serializers.ModelSerializer): operational_update_details = serializers.SerializerMethodField() final_report_details = serializers.SerializerMethodField() starting_language = serializers.CharField(read_only=True) + event_detail = ListEventSerializer(source="event", read_only=True) class Meta: model = Dref @@ -248,6 +250,7 @@ class Meta: "status_display", "date_of_approval", "starting_language", + "event_detail", ] @extend_schema_field(MiniOperationalUpdateActiveSerializer(many=True)) @@ -431,6 +434,7 @@ class DrefSerializer(NestedUpdateMixin, NestedCreateMixin, ModelSerializer): source="contingency_plans_supporting_document", read_only=True, required=False, allow_null=True ) proposed_action = ProposedActionSerializer(many=True, required=False) + event = serializers.PrimaryKeyRelatedField(queryset=Event.objects.all(), required=False) class Meta: model = Dref @@ -1598,6 +1602,7 @@ class CompletedDrefOperationsSerializer(serializers.ModelSerializer): application_type = serializers.SerializerMethodField() application_type_display = serializers.SerializerMethodField() starting_language = serializers.CharField(read_only=True) + event_detail = ListEventSerializer(source="dref__event", read_only=True) class Meta: model = DrefFinalReport @@ -1616,6 +1621,7 @@ class Meta: "status", "status_display", "starting_language", + "event_detail", ) def get_application_type(self, obj) -> str: diff --git a/dref/test_views.py b/dref/test_views.py index 3c4a221d0..1b7b5558d 100644 --- a/dref/test_views.py +++ b/dref/test_views.py @@ -10,6 +10,7 @@ from django.core import management from rest_framework import status +from api.factories.event import EventFactory from api.models import Country, DisasterType, District, Region, RegionName from api.utils import get_model_name from deployments.factories.project import SectorFactory @@ -119,6 +120,7 @@ def test_post_dref_creation(self, send_notification): old_count = Dref.objects.count() national_society = Country.objects.create(name="xzz") disaster_type = DisasterType.objects.create(name="abc") + event = EventFactory.create(name="Test event") data = { "title": "Dref test title", "type_of_onset": Dref.OnsetType.SLOW.value, @@ -182,6 +184,7 @@ def test_post_dref_creation(self, send_notification): "originator_email": "test@gmail.com", "national_society": national_society.id, "disaster_type": disaster_type.id, + "event": event.id, # NOTE: Test Many to Many fields "risk_security": [ {"risk": "Test Risk 1", "mitigation_measure": "Test Mitigation Measure"}, @@ -246,6 +249,7 @@ def test_post_dref_creation(self, send_notification): response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, 201) self.assertEqual(Dref.objects.count(), old_count + 1) + self.assertEqual(response.data["event"], event.id) instance = Dref.objects.get(id=response.data["id"]) instance.users.add(self.user.id) instance_user_email = [user.email for user in instance.users.all()] @@ -1928,10 +1932,14 @@ def test_completed_dref_operations(self): def test_filter_active_dref(self): country_1 = Country.objects.create(name="country1") country_2 = Country.objects.create(name="country2") - + event = EventFactory.create(name="Test Event") # create some dref dref_1 = DrefFactory.create( - is_active=True, type_of_dref=Dref.DrefType.ASSESSMENT, country=country_1, created_by=self.root_user + is_active=True, + type_of_dref=Dref.DrefType.ASSESSMENT, + country=country_1, + created_by=self.root_user, + event=event, ) dref_2 = DrefFactory.create(is_active=True, type_of_dref=Dref.DrefType.LOAN, country=country_2, created_by=self.root_user) # some dref final report @@ -1968,6 +1976,12 @@ def test_filter_active_dref(self): self.assertEqual(len(response.data["results"]), 1) self.assertEqual(response.data["results"][0]["final_report_details"]["id"], dref_final_report.id) + # filter by event + url = f"/api/v2/active-dref/?event={event.id}" + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(response.data["results"]), 1) + def test_dref_share_users(self): user1 = UserFactory.create( username="user1@test.com",