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
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[submodule "assets"]
path = assets
url = https://github.com/IFRCGo/go-api-artifacts
url = git@github.com:IFRCGo/go-api-artifacts.git
2 changes: 1 addition & 1 deletion assets
Submodule assets updated 1 files
+20 −0 openapi-schema.yaml
118 changes: 118 additions & 0 deletions eap/dev_views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
from django.http import HttpResponse
from django.template import loader
from rest_framework import permissions
from rest_framework.views import APIView


class EAPEmailPreview(APIView):
permission_classes = [permissions.IsAuthenticated]

def get(self, request):
type_param = request.GET.get("type")

template_map = {
"registration": "email/eap/registration.html",
"submission": "email/eap/submission.html",
"feedback_to_national_society": "email/eap/feedback_to_national_society.html",
"resubmission_of_revised_eap": "email/eap/re-submission.html",
"feedback_for_revised_eap": "email/eap/feedback_to_revised_eap.html",
"technically_validated_eap": "email/eap/technically_validated_eap.html",
"pending_pfa": "email/eap/pending_pfa.html",
"approved_eap": "email/eap/approved.html",
"reminder": "email/eap/reminder.html",
}

if type_param not in template_map:
valid_values = ", ".join(template_map.keys())
return HttpResponse(
f"Invalid 'type' parameter. Please use one of the following values: {valid_values}.",
)

context_map = {
"registration": {
"eap_type_display": "FULL EAP",
"country_name": "Test Country",
"national_society": "Test National Society",
"supporting_partners": [
{"society_name": "Partner 1"},
{"society_name": "Partner 2"},
],
"disaster_type": "Flood",
"ns_contact_name": "Test registration name",
"ns_contact_email": "[email protected]",
"ns_contact_phone": "1234567890",
},
"submission": {
"eap_type_display": "SIMPLIFIED EAP",
"country_name": "Test Country",
"national_society": "Test National Society",
"people_targated": 100,
"supporting_partners": [
{"society_name": "Partner NS 1"},
{"society_name": "Partner NS 2"},
],
"disaster_type": "Flood",
"total_budget": "250,000 CHF",
"ns_contact_name": "Test Ns Contact name",
"ns_contact_email": "[email protected]",
"ns_contact_phone": "+977-9800000000",
},
"feedback_to_national_society": {
"eap_type_display": "FULL EAP",
"country_name": "Test Country",
"national_society": "Test National Society",
},
"resubmission_of_revised_eap": {
"eap_type_display": "SIMPLIFIED EAP",
"country_name": "Test Country",
"national_society": "Test National Society",
"supporting_partners": [
{"society_name": "Partner NS 1"},
{"society_name": "Partner NS 2"},
],
"version": 2 or 3,
"people_targated": 100,
"disaster_type": "Flood",
"total_budget": "250,000 CHF",
"ns_contact_name": "Test Ns Contact name",
"ns_contact_email": "[email protected]",
"ns_contact_phone": "+977-9800000000",
},
"feedback_for_revised_eap": {
"eap_type_display": "FULL EAP",
"country_name": "Test Country",
"national_society": "Test National Society",
"version": 2,
},
"technically_validated_eap": {
"eap_type_display": "FULL EAP",
"country_name": "Test Country",
"national_society": "Test National Society",
"disaster_type": "Flood",
},
"pending_pfa": {
"eap_type_display": "FULL EAP",
"country_name": "Test Country",
"national_society": "Test National Society",
"disaster_type": "Flood",
},
"approved_eap": {
"eap_type_display": "FULL EAP",
"country_name": "Test Country",
"national_society": "Test National Society",
"disaster_type": "Flood",
},
"reminder": {
"eap_type_display": "FULL EAP",
"country_name": "Test Country",
"national_society": "Test National Society",
"disaster_type": "Flood",
},
}

context = context_map.get(type_param)
if context is None:
return HttpResponse("No context found for the email preview.")
template_file = template_map[type_param]
template = loader.get_template(template_file)
return HttpResponse(template.render(context, request))
12 changes: 12 additions & 0 deletions eap/serializers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import typing

from django.contrib.auth.models import User
from django.db import transaction
from django.utils import timezone
from django.utils.translation import gettext
from rest_framework import serializers
Expand Down Expand Up @@ -32,6 +33,7 @@
TimeFrame,
YearsTimeFrameChoices,
)
from eap.tasks import send_new_eap_registration_email
from eap.utils import (
has_country_permission,
is_user_ifrc_admin,
Expand Down Expand Up @@ -185,6 +187,16 @@ class Meta:
"latest_full_eap",
]

def create(self, validated_data: dict[str, typing.Any]):
instance = super().create(validated_data)

transaction.on_commit(
lambda: send_new_eap_registration_email.delay(
instance.id,
)
)
return instance

def update(self, instance: EAPRegistration, validated_data: dict[str, typing.Any]) -> dict[str, typing.Any]:
# NOTE: Cannot update once EAP application is being created.
if instance.has_eap_application:
Expand Down
58 changes: 58 additions & 0 deletions eap/tasks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import logging

from celery import shared_task
from django.conf import settings
from django.contrib.auth import get_user_model
from django.template.loader import render_to_string

from eap.models import EAPRegistration
from eap.utils import (
get_coordinator_emails_by_region,
get_eap_registration_email_context,
)
from notifications.notification import send_notification

User = get_user_model()

logger = logging.getLogger(__name__)


@shared_task
def send_new_eap_registration_email(eap_registration_id: int):

instance = EAPRegistration.objects.filter(id=eap_registration_id).first()
if not instance:
return None

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

recipients = [
settings.EMAIL_EAP_DREF_ANTICIPATORY_PILLAR,
instance.ifrc_contact_email,
]
cc_recipients = list(
set(
[
instance.national_society_contact_email,
*settings.EMAIL_EAP_DREF_AA_GLOBAL_TEAM,
*regional_coordinator_emails,
]
)
)
email_context = get_eap_registration_email_context(instance)
email_subject = (
f"[{instance.get_eap_type_display() if instance.get_eap_type_display() else 'EAP'} IN DEVELOPMENT] "
f"{instance.country} {instance.disaster_type}"
)
email_body = render_to_string("email/eap/registration.html", email_context)
email_type = "New EAP Registration"

send_notification(
subject=email_subject,
recipients=recipients,
html=email_body,
mailtype=email_type,
cc_recipients=cc_recipients,
)

return email_context
4 changes: 3 additions & 1 deletion eap/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,8 @@ def test_list_eap_registration(self):
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data["results"]), 5)

def test_create_eap_registration(self):
@mock.patch("eap.tasks.send_new_eap_registration_email")
def test_create_eap_registration(self, send_new_eap_registration_email):
url = "/api/v2/eap-registration/"
data = {
"eap_type": EAPType.FULL_EAP,
Expand Down Expand Up @@ -157,6 +158,7 @@ def test_create_eap_registration(self):
self.disaster_type.id,
},
)
self.assertTrue(send_new_eap_registration_email)

def test_retrieve_eap_registration(self):
eap_registration = EAPRegistrationFactory.create(
Expand Down
46 changes: 46 additions & 0 deletions eap/utils.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,56 @@
import os
from typing import Any, Dict, Set, TypeVar

from django.conf import settings
from django.contrib.auth.models import Permission, User
from django.core.exceptions import ValidationError
from django.db import models

from api.models import Region, RegionName

REGION_EMAIL_MAP: dict[RegionName, list[str]] = {
RegionName.AFRICA: settings.EMAIL_EAP_AFRICA_COORDINATORS,
RegionName.AMERICAS: settings.EMAIL_EAP_AMERICAS_COORDINATORS,
RegionName.ASIA_PACIFIC: settings.EMAIL_EAP_ASIA_PACIFIC_COORDINATORS,
RegionName.EUROPE: settings.EMAIL_EAP_EUROPE_COORDINATORS,
RegionName.MENA: settings.EMAIL_EAP_MENA_COORDINATORS,
}


def get_coordinator_emails_by_region(region: Region | None) -> list[str]:
"""
This function uses the REGION_EMAIL_MAP dictionary to map Region name to the corresponding list of email addresses.
Args:
region: Region instance for which the coordinator emails are needed.
Returns:
List of email addresses corresponding to the region coordinators.
Returns an empty list if the region is None or not found in the mapping.
"""
if not region:
return []

return REGION_EMAIL_MAP.get(region.name, [])


def get_eap_registration_email_context(instance):
from eap.serializers import EAPRegistrationSerializer

eap_registration_data = EAPRegistrationSerializer(instance).data

email_context = {
"registration_id": eap_registration_data["id"],
"eap_type_display": eap_registration_data["eap_type_display"],
"country_name": eap_registration_data["country_details"]["name"],
"national_society": eap_registration_data["national_society_details"]["society_name"],
"supporting_partners": eap_registration_data["partners_details"],
"disaster_type": eap_registration_data["disaster_type_details"]["name"],
"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"],
Copy link
Member

Choose a reason for hiding this comment

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

Do we need this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, we need disaster_type for this: image
and national society contacts for regards:
image

"frontend_url": settings.GO_WEB_URL,
}
return email_context


def has_country_permission(user: User, country_id: int) -> bool:
"""Checks if the user has country admin permission."""
Expand Down
18 changes: 18 additions & 0 deletions main/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@
EMAIL_USER=(str, None),
EMAIL_PASS=(str, None),
DEBUG_EMAIL=(bool, False), # This was 0/1 before
# EAP-EMAILS
EMAIL_EAP_DREF_ANTICIPATORY_PILLAR=(str, None),
EMAIL_EAP_DREF_AA_GLOBAL_TEAM=(list, None),
EMAIL_EAP_AFRICA_COORDINATORS=(list, None),
EMAIL_EAP_AMERICAS_COORDINATORS=(list, None),
EMAIL_EAP_ASIA_PACIFIC_COORDINATORS=(list, None),
EMAIL_EAP_EUROPE_COORDINATORS=(list, None),
EMAIL_EAP_MENA_COORDINATORS=(list, None),
# TEST_EMAILS=(list, ['[email protected]']), # maybe later
# Translation
# Translator Available:
Expand Down Expand Up @@ -198,6 +206,7 @@ def parse_domain(*env_keys: str) -> str:
ALLOWED_HOSTS = [
"localhost",
"0.0.0.0",
"127.0.0.1",
urlparse(GO_API_URL).hostname,
*env("DJANGO_ADDITIONAL_ALLOWED_HOSTS"),
]
Expand Down Expand Up @@ -581,6 +590,15 @@ def parse_domain(*env_keys: str) -> str:
DEBUG_EMAIL = env("DEBUG_EMAIL")
# TEST_EMAILS = env('TEST_EMAILS') # maybe later

# EAP-Email
EMAIL_EAP_DREF_ANTICIPATORY_PILLAR = env("EMAIL_EAP_DREF_ANTICIPATORY_PILLAR")
EMAIL_EAP_DREF_AA_GLOBAL_TEAM = env("EMAIL_EAP_DREF_AA_GLOBAL_TEAM")
EMAIL_EAP_AFRICA_COORDINATORS = env("EMAIL_EAP_AFRICA_COORDINATORS")
EMAIL_EAP_AMERICAS_COORDINATORS = env("EMAIL_EAP_AMERICAS_COORDINATORS")
EMAIL_EAP_ASIA_PACIFIC_COORDINATORS = env("EMAIL_EAP_ASIA_PACIFIC_COORDINATORS")
EMAIL_EAP_EUROPE_COORDINATORS = env("EMAIL_EAP_EUROPE_COORDINATORS")
EMAIL_EAP_MENA_COORDINATORS = env("EMAIL_EAP_MENA_COORDINATORS")

DATA_UPLOAD_MAX_MEMORY_SIZE = 104857600 # default 2621440, 2.5MB -> 100MB
# default 1000, was not enough for Mozambique Cyclone Idai data
# second 2000, was not enouch for Global COVID Emergency
Expand Down
2 changes: 2 additions & 0 deletions main/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
from deployments import drf_views as deployment_views
from dref import views as dref_views
from eap import views as eap_views
from eap.dev_views import EAPEmailPreview
from flash_update import views as flash_views
from lang import views as lang_views
from local_units import views as local_units_views
Expand Down Expand Up @@ -287,6 +288,7 @@
# For django versions before 2.0:
# url(r'^__debug__/', include(debug_toolbar.urls)),
url(r"^dev/email-preview/local-units/", LocalUnitsEmailPreview.as_view()),
url(r"^dev/email-preview/eap/", EAPEmailPreview.as_view()),
]
+ urlpatterns
+ static.static(
Expand Down
Loading
Loading