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
+25 −0 openapi-schema.yaml
2 changes: 2 additions & 0 deletions deploy/helm/ifrcgo-helm/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,8 @@ cronjobs:
# https://github.com/jazzband/django-oauth-toolkit/blob/master/docs/management_commands.rst#cleartokens
- command: 'oauth_cleartokens'
schedule: '0 1 * * *'
- command: 'eap_submission_reminder'
schedule: '0 0 * * *'


elasticsearch:
Expand Down
34 changes: 34 additions & 0 deletions eap/management/commands/eap_submission_reminder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from datetime import timedelta

from django.core.management.base import BaseCommand
from django.utils import timezone
from sentry_sdk.crons import monitor

from eap.models import EAPRegistration
from eap.tasks import send_deadline_reminder_email
from main.sentry import SentryMonitor


class Command(BaseCommand):
help = "Send EAP submission reminder emails 1 week before deadline"

@monitor(monitor_slug=SentryMonitor.EAP_SUBMISSION_REMINDER)
def handle(self, *args, **options):
"""
Finds EAP-registrations whose submission deadline is exactly 1 week from today
and sends reminder emails for each matching registration.
"""
target_date = timezone.now().date() + timedelta(weeks=1)
queryset = EAPRegistration.objects.filter(
deadline=target_date,
)

if not queryset.exists():
self.stdout.write(self.style.NOTICE("No EAP registrations found for deadline reminder."))
return

for instance in queryset.iterator():
self.stdout.write(self.style.NOTICE(f"Sending deadline reminder email for EAPRegistration ID={instance.id}"))
send_deadline_reminder_email(instance.id)

self.stdout.write(self.style.SUCCESS("Successfully sent all deadline reminder emails."))
23 changes: 23 additions & 0 deletions eap/migrations/0014_eapregistration_deadline_and_more.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 4.2.26 on 2025-12-24 05:46

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('eap', '0013_alter_eapregistration_national_society_contact_email_and_more'),
]

operations = [
migrations.AddField(
model_name='eapregistration',
name='deadline',
field=models.DateField(blank=True, help_text='Date by which the EAP submission must be completed.', null=True, verbose_name='deadline'),
),
migrations.AddField(
model_name='eapregistration',
name='deadline_remainder_sent_at',
field=models.DateTimeField(blank=True, help_text='Timestamp when the deadline reminder email was sent.', null=True, verbose_name='deadline reminder email sent at'),
),
]
15 changes: 15 additions & 0 deletions eap/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,21 @@ class EAPRegistration(EAPBaseModel):
help_text=_("Timestamp when the EAP was activated."),
)

# EAP submission deadline
deadline = models.DateField(
null=True,
blank=True,
verbose_name=_("deadline"),
help_text=_("Date by which the EAP submission must be completed."),
)

deadline_remainder_sent_at = models.DateTimeField(
null=True,
blank=True,
verbose_name=_("deadline reminder email sent at"),
help_text=_("Timestamp when the deadline reminder email was sent."),
)

# TYPING
id: int
national_society_id: int
Expand Down
25 changes: 25 additions & 0 deletions eap/serializers.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import typing
from datetime import timedelta

from django.contrib.auth.models import User
from django.db import transaction
Expand Down Expand Up @@ -193,6 +194,7 @@ class Meta:
"modified_by",
"latest_simplified_eap",
"latest_full_eap",
"deadline",
]

def create(self, validated_data: dict[str, typing.Any]):
Expand Down Expand Up @@ -960,11 +962,28 @@ def update(self, instance: EAPRegistration, validated_data: dict[str, typing.Any
Therefore:
- version == 2 always corresponds to the first IFRC feedback cycle
- Any later versions (>= 3) correspond to resubmitted cycles

Deadline update rules:
- First IFRC feedback cycle: deadline is set to 90 days from the current date.
- Subsequent feedback or resubmission cycles: deadline is set to 30 days from the current date.
"""

if eap_count == 2:
updated_instance.deadline = timezone.now().date() + timedelta(days=90)
updated_instance.save(
update_fields=[
"deadline",
]
)
transaction.on_commit(lambda: send_feedback_email.delay(eap_registration_id))

elif eap_count > 2:
updated_instance.deadline = timezone.now().date() + timedelta(days=30)
updated_instance.save(
update_fields=[
"deadline",
]
)
transaction.on_commit(lambda: send_feedback_email_for_resubmitted_eap.delay(eap_registration_id))

elif (old_status, new_status) == (
Expand All @@ -976,6 +995,12 @@ def update(self, instance: EAPRegistration, validated_data: dict[str, typing.Any
EAPRegistration.Status.TECHNICALLY_VALIDATED,
EAPRegistration.Status.NS_ADDRESSING_COMMENTS,
):
updated_instance.deadline = timezone.now().date() + timedelta(days=30)
updated_instance.save(
update_fields=[
"deadline",
]
)
transaction.on_commit(lambda: send_feedback_email_for_resubmitted_eap.delay(eap_registration_id))

elif (old_status, new_status) == (
Expand Down
18 changes: 11 additions & 7 deletions eap/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from django.conf import settings
from django.contrib.auth import get_user_model
from django.template.loader import render_to_string
from django.utils import timezone

from eap.models import EAPRegistration, EAPType, FullEAP, SimplifiedEAP
from eap.utils import get_coordinator_emails_by_region, get_eap_email_context
Expand Down Expand Up @@ -114,7 +115,7 @@ def send_feedback_email(eap_registration_id: int):
ifrc_delegation_focal_point_email = latest_simplified_eap.ifrc_delegation_focal_point_email
else:
latest_full_eap = instance.latest_full_eap
partner_ns_email = latest_full_eap.partner_ns_name
partner_ns_email = latest_full_eap.partner_ns_email
ifrc_delegation_focal_point_email = latest_full_eap.ifrc_delegation_focal_point_email

regional_coordinator_emails: list[str] = get_coordinator_emails_by_region(instance.country.region)
Expand Down Expand Up @@ -165,7 +166,7 @@ def send_eap_resubmission_email(eap_registration_id: int):
latest_version = latest_simplified_eap.version
else:
latest_full_eap = instance.latest_full_eap
partner_ns_email = latest_full_eap.partner_ns_name
partner_ns_email = latest_full_eap.partner_ns_email
latest_version = latest_full_eap.version

regional_coordinator_emails: list[str] = get_coordinator_emails_by_region(instance.country.region)
Expand Down Expand Up @@ -268,7 +269,7 @@ def send_technical_validation_email(eap_registration_id: int):
partner_ns_email = latest_simplified_eap.partner_ns_email
else:
latest_full_eap = instance.latest_full_eap
partner_ns_email = latest_full_eap.partner_ns_name
partner_ns_email = latest_full_eap.partner_ns_email

regional_coordinator_emails: list[str] = get_coordinator_emails_by_region(instance.country.region)

Expand Down Expand Up @@ -312,7 +313,7 @@ def send_pending_pfa_email(eap_registration_id: int):
partner_ns_email = latest_simplified_eap.partner_ns_email
else:
latest_full_eap = instance.latest_full_eap
partner_ns_email = latest_full_eap.partner_ns_name
partner_ns_email = latest_full_eap.partner_ns_email

regional_coordinator_emails: list[str] = get_coordinator_emails_by_region(instance.country.region)

Expand Down Expand Up @@ -357,7 +358,7 @@ def send_approved_email(eap_registration_id: int):
email_context = "Simplified EAP"
else:
latest_full_eap = instance.latest_full_eap
partner_ns_email = latest_full_eap.partner_ns_name
partner_ns_email = latest_full_eap.partner_ns_email
email_context = "Full EAP"

regional_coordinator_emails: list[str] = get_coordinator_emails_by_region(instance.country.region)
Expand Down Expand Up @@ -402,7 +403,7 @@ def send_deadline_reminder_email(eap_registration_id: int):
partner_ns_email = latest_simplified_eap.partner_ns_email
else:
latest_full_eap = instance.latest_full_eap
partner_ns_email = latest_full_eap.partner_ns_name
partner_ns_email = latest_full_eap.partner_ns_email

regional_coordinator_emails: list[str] = get_coordinator_emails_by_region(instance.country.region)

Expand All @@ -422,7 +423,7 @@ def send_deadline_reminder_email(eap_registration_id: int):
)
email_context = get_eap_email_context(instance)
email_subject = f"[DREF {instance.get_eap_type_display()} SUBMISSION REMINDER] {instance.country} {instance.disaster_type}"
email_body = render_to_string("email/eap/approved.html", email_context)
email_body = render_to_string("email/eap/reminder.html", email_context)
email_type = "Approved EAP"
send_notification(
subject=email_subject,
Expand All @@ -431,4 +432,7 @@ def send_deadline_reminder_email(eap_registration_id: int):
mailtype=email_type,
cc_recipients=cc_recipients,
)
instance.deadline_remainder_sent_at = timezone.now()
instance.save(update_fields=["deadline_remainder_sent_at"])

return email_context
2 changes: 1 addition & 1 deletion eap/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ def get_coordinator_emails_by_region(region: Region | None) -> list[str]:


# TODO @sudip-khanal: Add files to email context after implementing file sending in email notification
# also include the deadline field once it added to the model.


def get_eap_email_context(instance):
Expand All @@ -52,6 +51,7 @@ def get_eap_email_context(instance):
"ns_contact_name": eap_registration_data["national_society_contact_name"],
"ns_contact_email": eap_registration_data["national_society_contact_email"],
"ns_contact_phone": eap_registration_data["national_society_contact_phone_number"],
"deadline": eap_registration_data["deadline"],
"frontend_url": settings.GO_WEB_URL,
# "review_checklist_file":eap_registration_data["review_checklist_file"],
# "validated_budget_file":eap_registration_data["validated_budget_file"],
Expand Down
1 change: 1 addition & 0 deletions main/sentry.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ class SentryMonitor(models.TextChoices):
INGEST_ICRC = "ingest_icrc", "0 3 * * 0"
# NOTIFY_VALIDATORS = "notify_validators", "0 0 * * *" # NOTE: Disable local unit email notification for now
OAUTH_CLEARTOKENS = "oauth_cleartokens", "0 1 * * *"
EAP_SUBMISSION_REMINDER = "eap_submission_reminder", "0 0 * * *"

@staticmethod
def load_cron_data() -> typing.List[typing.Tuple[str, str]]:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
<!-- Deadline paragraph -->
<p>
The NS has 3 months to address these comments, which means that we expect to receive the new version
of the EAP no later than <span style="font-weight: bold;">Deadline</span> (3 months).
of the EAP no later than <span style="font-weight: bold;">{{ deadline }}</span> (3 months).
In case the NS has any questions about the feedback provided, we are available to organize a feedback call.
Do not hesitate to contact us should you have any further questions at <a href="mailto:DREF.anticipatorypillar@ifrc.org">DREF.anticipatorypillar@ifrc.org</a>.
</p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
<!-- Deadline paragraph -->
<p>
The NS has 1 month to address these comments, which means that we expect to receive the new version of the EAP no later than
<span style="font-weight: bold;">Deadline</span> (1 month).
<span style="font-weight: bold;">{{ deadline }}</span> (1 month).
In case the NS has any questions about the feedback provided, we are available to organize a feedback call.
Do not hesitate to contact us should you have any further questions at
<a href="mailto:DREF.anticipatorypillar@ifrc.org">DREF.anticipatorypillar@ifrc.org</a>.
Expand Down
2 changes: 1 addition & 1 deletion notifications/templates/email/eap/reminder.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
</p>

<p>
This is a reminder that the next version of the {{ country_name }} {{ disaster_type }} should be submitted before Deadline.
This is a reminder that the next version of the {{ country_name }} {{ disaster_type }} should be submitted before {{ deadline }}.
</p>

<p>
Expand Down
Loading