Skip to content

Commit 5491784

Browse files
authored
Merge pull request #2594 from IFRCGo/feature/review-checklist
Feature/EAP Snapshots and Review checklist
2 parents 83be9df + 7a02896 commit 5491784

9 files changed

Lines changed: 578 additions & 40 deletions

eap/admin.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class SimplifiedEAPAdmin(admin.ModelAdmin):
5555
"eap_registration__country__name",
5656
"eap_registration__disaster_type__name",
5757
)
58-
list_display = ("eap_registration",)
58+
list_display = ("simplifed_eap_application", "version", "is_locked")
5959
autocomplete_fields = (
6060
"eap_registration",
6161
"created_by",
@@ -69,8 +69,16 @@ class SimplifiedEAPAdmin(admin.ModelAdmin):
6969
"selected_early_actions_file",
7070
"planned_operations",
7171
"enable_approaches",
72+
"parent",
73+
"is_locked",
74+
"version",
7275
)
7376

77+
def simplifed_eap_application(self, obj):
78+
return f"{obj.eap_registration.national_society.society_name} - {obj.eap_registration.disaster_type.name}"
79+
80+
simplifed_eap_application.short_description = "Simplified EAP Application"
81+
7482
def get_queryset(self, request):
7583
return (
7684
super()
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Generated by Django 4.2.19 on 2025-11-14 10:27
2+
3+
from django.db import migrations
4+
import main.fields
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
('eap', '0005_eapregistration_activated_at_and_more'),
11+
]
12+
13+
operations = [
14+
migrations.AddField(
15+
model_name='eapregistration',
16+
name='review_checklist_file',
17+
field=main.fields.SecureFileField(blank=True, null=True, upload_to='eap/files/', verbose_name='Review Checklist File'),
18+
),
19+
migrations.AddField(
20+
model_name='simplifiedeap',
21+
name='updated_checklist_file',
22+
field=main.fields.SecureFileField(blank=True, null=True, upload_to='eap/files/', verbose_name='Updated Checklist File'),
23+
),
24+
]
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Generated by Django 4.2.19 on 2025-11-20 07:30
2+
3+
from django.db import migrations, models
4+
import django.db.models.deletion
5+
6+
7+
class Migration(migrations.Migration):
8+
dependencies = [
9+
("eap", "0006_eapregistration_review_checklist_file_and_more"),
10+
]
11+
12+
operations = [
13+
migrations.AlterModelOptions(
14+
name="eapfile",
15+
options={
16+
"ordering": ["-id"],
17+
"verbose_name": "eap file",
18+
"verbose_name_plural": "eap files",
19+
},
20+
),
21+
migrations.AlterModelOptions(
22+
name="eapregistration",
23+
options={
24+
"ordering": ["-id"],
25+
"verbose_name": "Development Registration EAP",
26+
"verbose_name_plural": "Development Registration EAPs",
27+
},
28+
),
29+
migrations.AlterModelOptions(
30+
name="simplifiedeap",
31+
options={
32+
"ordering": ["-id"],
33+
"verbose_name": "Simplified EAP",
34+
"verbose_name_plural": "Simplified EAPs",
35+
},
36+
),
37+
migrations.AddField(
38+
model_name="simplifiedeap",
39+
name="is_locked",
40+
field=models.BooleanField(
41+
default=False,
42+
help_text="Indicates whether the Simplified EAP is locked for editing.",
43+
verbose_name="Is Locked?",
44+
),
45+
),
46+
migrations.AddField(
47+
model_name="simplifiedeap",
48+
name="parent",
49+
field=models.ForeignKey(
50+
blank=True,
51+
help_text="Reference to the parent Simplified EAP if this is a snapshot.",
52+
null=True,
53+
on_delete=django.db.models.deletion.SET_NULL,
54+
related_name="snapshots",
55+
to="eap.simplifiedeap",
56+
verbose_name="Parent Simplified EAP",
57+
),
58+
),
59+
migrations.AddField(
60+
model_name="simplifiedeap",
61+
name="version",
62+
field=models.IntegerField(
63+
default=1,
64+
help_text="Version identifier for the Simplified EAP.",
65+
verbose_name="Version",
66+
),
67+
),
68+
migrations.AlterField(
69+
model_name="simplifiedeap",
70+
name="eap_registration",
71+
field=models.ForeignKey(
72+
on_delete=django.db.models.deletion.CASCADE,
73+
related_name="simplified_eap",
74+
to="eap.eapregistration",
75+
verbose_name="EAP Development Registration",
76+
),
77+
),
78+
]

eap/models.py

Lines changed: 80 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from django.conf import settings
22
from django.contrib.postgres.fields import ArrayField
3-
from django.db import models
3+
from django.db import models, transaction
44
from django.utils.translation import gettext_lazy as _
55

66
from api.models import Admin2, Country, DisasterType, District
@@ -206,9 +206,14 @@ class EAPBaseModel(models.Model):
206206
related_name="%(class)s_modified_by",
207207
)
208208

209+
# TYPING
210+
id: int
211+
created_by_id: int
212+
modified_by_id: int
213+
209214
class Meta:
210215
abstract = True
211-
ordering = ["-created_at"]
216+
ordering = ["-id"]
212217

213218

214219
class EAPFile(EAPBaseModel):
@@ -224,6 +229,7 @@ class EAPFile(EAPBaseModel):
224229
class Meta:
225230
verbose_name = _("eap file")
226231
verbose_name_plural = _("eap files")
232+
ordering = ["-id"]
227233

228234

229235
class OperationActivity(models.Model):
@@ -377,7 +383,7 @@ class EAPStatus(models.IntegerChoices):
377383
"""Initial status when an EAP is being created."""
378384

379385
UNDER_REVIEW = 20, _("Under Review")
380-
""" EAP has been submitted by NS. It is under review by IFRC and/or technical partners."""
386+
"""EAP has been submitted by NS. It is under review by IFRC and/or technical partners."""
381387

382388
NS_ADDRESSING_COMMENTS = 30, _("NS Addressing Comments")
383389
"""NS is addressing comments provided during the review process.
@@ -471,6 +477,14 @@ class EAPRegistration(EAPBaseModel):
471477
help_text=_("Upload the validated budget file once the EAP is technically validated."),
472478
)
473479

480+
# Review checklist
481+
review_checklist_file = SecureFileField(
482+
verbose_name=_("Review Checklist File"),
483+
upload_to="eap/files/",
484+
null=True,
485+
blank=True,
486+
)
487+
474488
# Contacts
475489
# National Society
476490
national_society_contact_name = models.CharField(
@@ -537,6 +551,7 @@ class EAPRegistration(EAPBaseModel):
537551
class Meta:
538552
verbose_name = _("Development Registration EAP")
539553
verbose_name_plural = _("Development Registration EAPs")
554+
ordering = ["-id"]
540555

541556
def __str__(self):
542557
# NOTE: Use select_related in admin get_queryset for national_society field to avoid extra queries
@@ -546,7 +561,9 @@ def __str__(self):
546561
def has_eap_application(self) -> bool:
547562
"""Check if the EAP Registration has an associated EAP application."""
548563
# TODO(susilnem): Add FULL EAP check, when model is created.
549-
return hasattr(self, "simplified_eap")
564+
if hasattr(self, "simplified_eap") and self.simplified_eap.exists():
565+
return True
566+
return False
550567

551568
@property
552569
def get_status_enum(self) -> EAPStatus:
@@ -574,7 +591,7 @@ def update_eap_type(self, eap_type: EAPType, commit: bool = True):
574591
class SimplifiedEAP(EAPBaseModel):
575592
"""Model representing a Simplified EAP."""
576593

577-
eap_registration = models.OneToOneField[EAPRegistration, EAPRegistration](
594+
eap_registration = models.ForeignKey[EAPRegistration, EAPRegistration](
578595
EAPRegistration,
579596
on_delete=models.CASCADE,
580597
verbose_name=_("EAP Development Registration"),
@@ -865,13 +882,70 @@ class SimplifiedEAP(EAPBaseModel):
865882
blank=True,
866883
)
867884

885+
# Review Checklist
886+
updated_checklist_file = SecureFileField(
887+
verbose_name=_("Updated Checklist File"),
888+
upload_to="eap/files/",
889+
null=True,
890+
blank=True,
891+
)
892+
893+
# NOTE: Snapshot fields
894+
version = models.IntegerField(
895+
verbose_name=_("Version"),
896+
help_text=_("Version identifier for the Simplified EAP."),
897+
default=1,
898+
)
899+
is_locked = models.BooleanField(
900+
verbose_name=_("Is Locked?"),
901+
help_text=_("Indicates whether the Simplified EAP is locked for editing."),
902+
default=False,
903+
)
904+
parent = models.ForeignKey(
905+
"self",
906+
on_delete=models.SET_NULL,
907+
verbose_name=_("Parent Simplified EAP"),
908+
help_text=_("Reference to the parent Simplified EAP if this is a snapshot."),
909+
null=True,
910+
blank=True,
911+
related_name="snapshots",
912+
)
913+
868914
# TYPING
869915
eap_registration_id: int
916+
parent_id: int
870917
id = int
871918

872919
class Meta:
873920
verbose_name = _("Simplified EAP")
874921
verbose_name_plural = _("Simplified EAPs")
922+
ordering = ["-id"]
875923

876924
def __str__(self):
877-
return f"Simplified EAP for {self.eap_registration}"
925+
return f"Simplified EAP for {self.eap_registration}- version:{self.version}"
926+
927+
def generate_snapshot(self):
928+
"""
929+
Generate a snapshot of the given Simplified EAP.
930+
"""
931+
932+
from eap.utils import copy_model_instance
933+
934+
with transaction.atomic():
935+
copy_model_instance(
936+
self,
937+
overrides={
938+
"parent_id": self.id,
939+
"version": self.version + 1,
940+
"created_by_id": self.created_by_id,
941+
"modified_by_id": self.modified_by_id,
942+
"updated_checklist_file": None,
943+
},
944+
exclude_clone_m2m_fields=[
945+
"admin2",
946+
],
947+
)
948+
949+
# Setting Parent as locked
950+
self.is_locked = True
951+
self.save(update_fields=["is_locked"])

0 commit comments

Comments
 (0)