Skip to content
Merged
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
2 changes: 1 addition & 1 deletion assets
Submodule assets updated 1 files
+188 −48 openapi-schema.yaml
16 changes: 14 additions & 2 deletions eap/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
@admin.register(EAPFile)
class EAPFileAdmin(admin.ModelAdmin):
search_fields = ("caption",)
list_select_related = True
autocomplete_fields = (
"created_by",
"modified_by",
)


@admin.register(EAPRegistration)
Expand Down Expand Up @@ -85,12 +90,13 @@ class SimplifiedEAPAdmin(admin.ModelAdmin):
"eap_registration__country__name",
"eap_registration__disaster_type__name",
)
list_display = ("simplifed_eap_application", "version", "is_locked")
list_display = ("simplifed_eap_application", "eap_registration", "version", "is_locked")
autocomplete_fields = (
"eap_registration",
"created_by",
"modified_by",
"admin2",
"partners",
)
readonly_fields = (
"cover_image",
Expand Down Expand Up @@ -159,6 +165,7 @@ def get_queryset(self, request):
)
.prefetch_related(
"admin2",
"partners",
"partner_contacts",
)
)
Expand All @@ -176,12 +183,13 @@ class FullEAPAdmin(admin.ModelAdmin):
"eap_registration__country__name",
"eap_registration__disaster_type__name",
)
list_display = ("eap_registration",)
list_display = ("full_eap_application", "eap_registration", "version", "is_locked")
autocomplete_fields = (
"eap_registration",
"created_by",
"modified_by",
"admin2",
"partners",
)
readonly_fields = (
"partner_contacts",
Expand Down Expand Up @@ -244,6 +252,9 @@ def regenerate_diff_pdf_file(self, request, queryset):

regenerate_diff_pdf_file.short_description = "Regenerate EAP diff PDF files for selected Full EAPs"

def full_eap_application(self, obj):
return f"{obj.eap_registration.national_society.society_name} - {obj.eap_registration.disaster_type.name}"

def get_queryset(self, request):
return (
super()
Expand All @@ -258,6 +269,7 @@ def get_queryset(self, request):
)
.prefetch_related(
"admin2",
"partners",
"partner_contacts",
"key_actors",
"risk_analysis_source_of_information",
Expand Down
2 changes: 0 additions & 2 deletions eap/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,6 @@ class Meta:

approach = fuzzy.FuzzyChoice(EnablingApproach.Approach)
budget_per_approach = fuzzy.FuzzyInteger(1000, 1000000)
ap_code = fuzzy.FuzzyInteger(100, 999)

@factory.post_generation
def readiness_activities(self, create, extracted, **kwargs):
Expand Down Expand Up @@ -175,7 +174,6 @@ class Meta:
sector = fuzzy.FuzzyChoice(PlannedOperation.Sector)
people_targeted = fuzzy.FuzzyInteger(100, 100000)
budget_per_sector = fuzzy.FuzzyInteger(1000, 1000000)
ap_code = fuzzy.FuzzyInteger(100, 999)

@factory.post_generation
def readiness_activities(self, create, extracted, **kwargs):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Generated by Django 4.2.29 on 2026-03-16 09:00

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('eap', '0006_fulleap_meal_source_of_information_and_more'),
]

operations = [
migrations.RemoveField(
model_name='eapregistration',
name='activated_at',
),
migrations.AddField(
model_name='fulleap',
name='lead_timeframe_unit',
field=models.IntegerField(blank=True, choices=[(10, 'Years'), (20, 'Months'), (30, 'Days'), (40, 'Hours')], null=True, verbose_name='Lead Timeframe Unit'),
),
migrations.AlterField(
model_name='eapregistration',
name='status',
field=models.IntegerField(choices=[(10, 'Under Development'), (20, 'Under Review'), (30, 'NS Addressing Comments'), (40, 'Technically Validated'), (50, 'Approved(Pending PFA)'), (60, 'Approved')], default=10, help_text='Select the current status of the EAP development process.', verbose_name='EAP Status'),
),
migrations.AlterField(
model_name='enablingapproach',
name='ap_code',
field=models.IntegerField(default=1, verbose_name='AP Code'),
preserve_default=False,
),
]
37 changes: 37 additions & 0 deletions eap/migrations/0008_remove_enablingapproach_ap_code_and_more.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Generated by Django 4.2.28 on 2026-04-03 09:16

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('api', '0228_merge_20260123_0806'),
('eap', '0007_remove_eapregistration_activated_at_and_more'),
]

operations = [
migrations.RemoveField(
model_name='enablingapproach',
name='ap_code',
),
migrations.RemoveField(
model_name='plannedoperation',
name='ap_code',
),
migrations.AddField(
model_name='fulleap',
name='partners',
field=models.ManyToManyField(blank=True, help_text='Select any partner NS involved in the EAP development.', related_name='+', to='api.country', verbose_name='Partners'),
),
migrations.AddField(
model_name='simplifiedeap',
name='partners',
field=models.ManyToManyField(blank=True, help_text='Select any partner NS involved in the EAP development.', related_name='+', to='api.country', verbose_name='Partners'),
),
migrations.AlterField(
model_name='plannedoperation',
name='sector',
field=models.IntegerField(choices=[(101, 'Shelter, Settlement and Housing'), (102, 'Livelihoods'), (103, 'Protection, Gender and Inclusion'), (104, 'Health and Care'), (105, 'Risk Reduction, Climate Adaptation and Recovery'), (106, 'Multipurpose Cash'), (107, 'Water, Sanitation And Hygiene'), (109, 'Education'), (110, 'Migration'), (111, 'Environment Sustainability'), (112, 'Community Engagement And Accountability')], verbose_name='sector'),
),
]
70 changes: 54 additions & 16 deletions eap/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -386,16 +386,30 @@ class Sector(models.IntegerChoices):
RISK_REDUCTION_CLIMATE_ADAPTATION_AND_RECOVERY = 105, _("Risk Reduction, Climate Adaptation and Recovery")
MULTIPURPOSE_CASH = 106, _("Multipurpose Cash")
WATER_SANITATION_AND_HYGIENE = 107, _("Water, Sanitation And Hygiene")
WASH = 108, _("WASH")
EDUCATION = 109, _("Education")
MIGRATION = 110, _("Migration")
ENVIRONMENT_SUSTAINABILITY = 111, _("Environment Sustainability")
COMMUNITY_ENGAGEMENT_AND_ACCOUNTABILITY = 112, _("Community Engagement And Accountability")

@classmethod
def get_sector_ap_codes(cls) -> dict[int, list[str]]:
return {
cls.SHELTER_SETTLEMENT_AND_HOUSING: ["AP101", "AP103", "AP104"],
cls.LIVELIHOODS: ["AP007"],
cls.PROTECTION_GENDER_AND_INCLUSION: ["AP114", "AP116", "AP117"],
cls.HEALTH_AND_CARE: ["AP107", "AP108", "AP109"],
cls.RISK_REDUCTION_CLIMATE_ADAPTATION_AND_RECOVERY: ["AP101", "AP103", "AP104", "AP105", "AP106"],
cls.MULTIPURPOSE_CASH: ["AP081"],
cls.WATER_SANITATION_AND_HYGIENE: ["AP110", "AP111"],
cls.EDUCATION: ["AP115"],
cls.MIGRATION: ["AP112", "AP113"],
cls.ENVIRONMENT_SUSTAINABILITY: ["AP102"],
cls.COMMUNITY_ENGAGEMENT_AND_ACCOUNTABILITY: ["AP129"],
}

sector = models.IntegerField(choices=Sector.choices, verbose_name=_("sector"))
people_targeted = models.IntegerField(verbose_name=_("People Targeted"))
budget_per_sector = models.IntegerField(verbose_name=_("Budget per sector (CHF)"))
ap_code = models.IntegerField(verbose_name=_("AP Code"))
previous_id = models.PositiveIntegerField(verbose_name=_("Previous ID"), null=True, blank=True)

indicators = models.ManyToManyField(
Expand Down Expand Up @@ -439,9 +453,24 @@ class Approach(models.IntegerChoices):
NATIONAL_SOCIETY_STRENGTHENING = 20, _("National Society Strengthening")
PARTNERSHIP_AND_COORDINATION = 30, _("Partnership And Coordination")

@classmethod
def get_approach_ap_codes(cls) -> dict[int, list[str]]:
return {
cls.SECRETARIAT_SERVICES: ["AP122"],
cls.NATIONAL_SOCIETY_STRENGTHENING: ["AP124", "AP125", "AP126"],
cls.PARTNERSHIP_AND_COORDINATION: [
"AP049",
"AP118",
"AP119",
"AP120",
"AP121",
"AP127",
"AP128",
],
}

approach = models.IntegerField(choices=Approach.choices, verbose_name=_("Approach"))
budget_per_approach = models.IntegerField(verbose_name=_("Budget per approach (CHF)"))
ap_code = models.IntegerField(verbose_name=_("AP Code"), null=True, blank=True)
previous_id = models.PositiveIntegerField(verbose_name=_("Previous ID"), null=True, blank=True)

indicators = models.ManyToManyField(
Expand Down Expand Up @@ -553,7 +582,7 @@ class EAPStatus(models.IntegerChoices):
IFRC can change status to NS_ADDRESSING_COMMENTS or PENDING_PFA.
"""

PENDING_PFA = 50, _("Pending PFA")
PENDING_PFA = 50, _("Approved(Pending PFA)")
"""EAP is in the process of signing the PFA between IFRC and NS.
"""

Expand All @@ -562,9 +591,6 @@ class EAPStatus(models.IntegerChoices):
Cannot be changed back to previous statuses.
"""

ACTIVATED = 70, _("Activated")
"""EAP has been activated"""


# BASE MODEL FOR EAP
class EAPRegistration(EAPBaseModel):
Expand Down Expand Up @@ -722,12 +748,6 @@ class EAPRegistration(EAPBaseModel):
verbose_name=_("pending pfa at"),
help_text=_("Timestamp when the EAP was marked as pending PFA."),
)
activated_at = models.DateTimeField(
null=True,
blank=True,
verbose_name=_("activated at"),
help_text=_("Timestamp when the EAP was activated."),
)

# EAP submission deadline
deadline = models.DateField(
Expand Down Expand Up @@ -808,6 +828,14 @@ class CommonEAPFields(models.Model):
related_name="+",
)

partners = models.ManyToManyField(
Country,
verbose_name=_("Partners"),
help_text=_("Select any partner NS involved in the EAP development."),
related_name="+",
blank=True,
)

people_targeted = models.IntegerField(
verbose_name=_("People Targeted."),
blank=True,
Expand Down Expand Up @@ -1247,7 +1275,7 @@ class Meta:
]

def __str__(self):
return f"Simplified EAP for {self.eap_registration}- version:{self.version}"
return f"{self.eap_registration} (VERSION {self.version})"

def generate_snapshot(self):
"""
Expand All @@ -1263,6 +1291,7 @@ def generate_snapshot(self):
overrides={
"parent_id": self.id,
"version": self.version + 1,
"is_locked": False,
"created_by_id": self.created_by_id,
"modified_by_id": self.modified_by_id,
"review_checklist_file": None,
Expand All @@ -1272,6 +1301,7 @@ def generate_snapshot(self):
},
exclude_clone_m2m_fields={
"admin2",
"partners",
"cover_image",
"hazard_impact_images",
"risk_selected_protocols_images",
Expand Down Expand Up @@ -1449,13 +1479,19 @@ class FullEAP(EAPBaseModel, CommonEAPFields):
)

# NOTE: In days
# TODO(susilnem): add unit for lead time
lead_time = models.IntegerField(
verbose_name=_("Lead Time"),
null=True,
blank=True,
)

lead_timeframe_unit = models.IntegerField(
choices=TimeFrame.choices,
verbose_name=_("Lead Timeframe Unit"),
null=True,
blank=True,
)

trigger_statement_source_of_information = models.ManyToManyField(
SourceInformation,
verbose_name=_("Trigger Statement Source of Forecast"),
Expand Down Expand Up @@ -1788,7 +1824,7 @@ class Meta:
]

def __str__(self):
return f"Full EAP for {self.eap_registration}- version:{self.version}"
return f"{self.eap_registration} (VERSION {self.version})"

def generate_snapshot(self):
"""
Expand All @@ -1803,6 +1839,7 @@ def generate_snapshot(self):
overrides={
"parent_id": self.id,
"version": self.version + 1,
"is_locked": False,
"created_by_id": self.created_by_id,
"modified_by_id": self.modified_by_id,
"review_checklist_file": None,
Expand All @@ -1812,6 +1849,7 @@ def generate_snapshot(self):
},
exclude_clone_m2m_fields={
"admin2",
"partners",
"cover_image",
# Files
"hazard_selection_images",
Expand Down
Loading
Loading