Skip to content

Commit

Permalink
Merge branch 'develop' into 215787_drop_cash_assist
Browse files Browse the repository at this point in the history
  • Loading branch information
MarekBiczysko authored Nov 7, 2024
2 parents 88ab205 + 83204a2 commit bd77c0f
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import { hasPermissions, PERMISSIONS } from '../../../config/permissions';
import { UniversalActivityLogTable } from '../../tables/UniversalActivityLogTable';
import { UniversalErrorBoundary } from '@components/core/UniversalErrorBoundary';
import { ReactElement } from 'react';
import { IndividualDeliveryMechanisms } from '@components/population/IndividualDeliveryMechanisms';

const Container = styled.div`
padding: 20px 20px 00px 20px;
Expand Down Expand Up @@ -164,6 +165,9 @@ export const PeopleDetailsPage = (): ReactElement => {
choicesData={choicesData}
grievancesChoices={grievancesChoices}
/>
<IndividualDeliveryMechanisms
individual={individual as IndividualNode}
/>
<IndividualAdditionalRegistrationInformation
flexFieldsData={flexFieldsData}
individual={individual as IndividualNode}
Expand Down
10 changes: 4 additions & 6 deletions src/hct_mis_api/api/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from django.contrib.admin.models import LogEntry
from django.contrib.admin.templatetags.admin_urls import admin_urlname
from django.core.exceptions import ValidationError
from django.core.mail import send_mail
from django.db.models import QuerySet
from django.db.transaction import atomic
from django.forms import Form
Expand Down Expand Up @@ -134,11 +133,10 @@ def _get_email_context(self, request: HttpRequest, obj: Any) -> Dict[str, Any]:

def _send_token_email(self, request: HttpRequest, obj: Any, template: str) -> None:
try:
send_mail(
f"HOPE API Token {obj} infos",
template.format(**self._get_email_context(request, obj)),
None,
recipient_list=[obj.user.email],
user = obj.user
user.email_user(
subject=f"HOPE API Token {obj} infos",
text_body=template.format(**self._get_email_context(request, obj)),
)
self.message_user(request, f"Email sent to {obj.user.email}", messages.SUCCESS)
except OSError:
Expand Down
4 changes: 4 additions & 0 deletions src/hct_mis_api/api/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ class AreaFilter(UpdatedAtFilter):
country_iso_code2 = CharFilter(field_name="area_type__country__iso_code2")
country_iso_code3 = CharFilter(field_name="area_type__country__iso_code3")
area_type_area_level = NumberFilter(field_name="area_type__area_level")
parent_id = CharFilter(field_name="parent__id")
parent_p_code = CharFilter(field_name="parent__p_code")

class Meta:
model = Area
Expand All @@ -28,6 +30,8 @@ class Meta:
"area_type_area_level",
"valid_from",
"valid_until",
"parent_id",
"parent_p_code",
)


Expand Down
37 changes: 21 additions & 16 deletions src/hct_mis_api/apps/payment/notifications.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import logging
from typing import Any, Dict, List
from typing import Any, Dict

from django.conf import settings
from django.db.models import Q, QuerySet
Expand Down Expand Up @@ -37,18 +37,22 @@ class PaymentNotification:
ACTION_SEND_FOR_APPROVAL: {
"action_name": "sent for approval",
"subject": "Payment pending for Approval",
"recipient_title": "Approver",
},
ACTION_APPROVE: {
"action_name": "approved",
"subject": "Payment pending for Authorization",
"recipient_title": "Authorizer",
},
ACTION_AUTHORIZE: {
"action_name": "authorized",
"subject": "Payment pending for Release",
"recipient_title": "Reviewer",
},
ACTION_REVIEW: {
"action_name": "released",
"subject": "Payment is Released",
"recipient_title": "Reviewer",
},
}

Expand All @@ -61,8 +65,9 @@ def __init__(self, payment_plan: PaymentPlan, action: str, action_user: User, ac
self.payment_plan_creation_date = self.payment_plan.created_at
self.email_subject = self.ACTION_PREPARE_EMAIL_BODIES_MAP[self.action]["subject"]
self.action_name = self.ACTION_PREPARE_EMAIL_BODIES_MAP[self.action]["action_name"]
self.recipient_title = self.ACTION_PREPARE_EMAIL_BODIES_MAP[self.action]["recipient_title"]
self.user_recipients = self._prepare_user_recipients()
self.emails = self._prepare_emails()
self.email = self._prepare_email()
self.enable_email_notification = self.payment_plan.business_area.enable_email_notification

def _prepare_user_recipients(self) -> QuerySet[User]:
Expand Down Expand Up @@ -93,22 +98,23 @@ def _prepare_user_recipients(self) -> QuerySet[User]:
if permission in DEFAULT_PERMISSIONS_LIST_FOR_IS_UNICEF_PARTNER
else Q()
)
users = User.objects.filter(
(Q(user_roles__in=user_roles) & program_access_q) | Q(partner_role_q & program_access_q) | unicef_q
).distinct()
users = (
User.objects.filter(
(Q(user_roles__in=user_roles) & program_access_q) | Q(partner_role_q & program_access_q) | unicef_q
)
.exclude(id=self.action_user.id)
.distinct()
)
if settings.ENV == "prod":
users = users.exclude(is_superuser=True)
return users

def _prepare_emails(self) -> List[MailjetClient]:
return [self._prepare_email(user) for user in self.user_recipients.exclude(id=self.action_user.id)]

def _prepare_email(self, user_recipient: User) -> MailjetClient:
body_variables = self._prepare_body_variables(user_recipient)
def _prepare_email(self) -> MailjetClient:
body_variables = self._prepare_body_variables()
email = MailjetClient(
mailjet_template_id=config.MAILJET_TEMPLATE_PAYMENT_PLAN_NOTIFICATION,
subject=self.email_subject,
recipients=[user_recipient.email],
recipients=[user_recipient.email for user_recipient in self.user_recipients],
ccs=[self.action_user.email],
variables=body_variables,
)
Expand All @@ -117,16 +123,15 @@ def _prepare_email(self, user_recipient: User) -> MailjetClient:
def send_email_notification(self) -> None:
if config.SEND_PAYMENT_PLANS_NOTIFICATION and self.enable_email_notification:
try:
for email in self.emails:
email.send_email()
self.email.send_email()
except Exception as e: # pragma: no cover
logger.exception(e)

def _prepare_body_variables(self, user_recipient: User) -> Dict[str, Any]:
def _prepare_body_variables(self) -> Dict[str, Any]:
protocol = "https" if settings.SOCIAL_AUTH_REDIRECT_IS_HTTPS else "http"
variables = {
"first_name": user_recipient.first_name,
"last_name": user_recipient.last_name,
"first_name": "Payment Plan",
"last_name": self.recipient_title,
"action_name": self.action_name,
"payment_plan_url": (
f"{protocol}://{settings.FRONTEND_HOST}/{self.payment_plan.business_area.slug}/programs/"
Expand Down
1 change: 0 additions & 1 deletion src/hct_mis_api/apps/utils/mailjet.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ def send_email(self) -> bool:

email_body = self._get_email_body()
attachments = {"Attachments": self.attachments} if self.attachments else {}

data = {
"Messages": [
{
Expand Down
73 changes: 73 additions & 0 deletions tests/unit/api/test_api_token.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import json
from datetime import datetime
from typing import Any
from unittest.mock import patch

from django.conf import settings
from django.http import HttpRequest
from django.test import TestCase, override_settings

from constance.test import override_config

from hct_mis_api.api.admin import TOKEN_INFO_EMAIL, APITokenAdmin
from hct_mis_api.api.models import Grant
from hct_mis_api.apps.account.fixtures import UserFactory
from hct_mis_api.apps.core.fixtures import create_afghanistan
from tests.unit.api.factories import APITokenFactory


class TestApiToken(TestCase):
@classmethod
def setUpClass(cls) -> None:
super().setUpClass()
cls.afg = create_afghanistan()
cls.user = UserFactory(
email="[email protected]",
)
cls.token = APITokenFactory(
user=cls.user,
grants=[
Grant.API_READ_ONLY.name,
Grant.API_RDI_UPLOAD.name,
Grant.API_PROGRAM_CREATE.name,
],
valid_to=datetime(2050, 1, 1),
)
cls.token.valid_for.set([cls.afg])

@patch("hct_mis_api.apps.utils.mailjet.requests.post")
@patch.object(APITokenAdmin, "message_user", return_value=None)
@patch.object(APITokenAdmin, "__init__", return_value=None)
@override_settings(EMAIL_SUBJECT_PREFIX="test")
@override_config(ENABLE_MAILJET=True)
def test_send_api_token(
self, mocked_requests_init: Any, mocked_requests_user: Any, mocked_requests_post: Any
) -> None:
request = HttpRequest()

APITokenAdmin()._send_token_email(request, self.token, TOKEN_INFO_EMAIL)

mocked_requests_post.assert_called_once()

expected_data = json.dumps(
{
"Messages": [
{
"From": {"Email": settings.DEFAULT_EMAIL, "Name": settings.DEFAULT_EMAIL_DISPLAY},
"Subject": f"[test] HOPE API Token {self.token} infos",
"To": [
{
"Email": "[email protected]",
},
],
"Cc": [],
"TextPart": f"\nDear {self.user.first_name},\n\nplease find below API token infos\n\nName: {self.token}\nKey: {self.token.key}\nGrants: {self.token.grants}\nExpires: {self.token.valid_to}\nBusiness Areas: {', '.join(self.token.valid_for.values_list('name', flat=True))}\n\nRegards\n\nThe HOPE Team\n",
}
]
}
)
mocked_requests_post.assert_called_with(
"https://api.mailjet.com/v3.1/send",
auth=(settings.MAILJET_API_KEY, settings.MAILJET_SECRET_KEY),
data=expected_data,
)
7 changes: 6 additions & 1 deletion tests/unit/api/test_lookups.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ def setUpTestData(cls) -> None:
cls.area2 = AreaFactory(
name="area2",
area_type=cls.area_type2,
parent=cls.area1,
)
cls.area2.valid_from = datetime(2020, 1, 1, tzinfo=pytz.UTC)
cls.area2.valid_until = datetime(2020, 12, 31, tzinfo=pytz.UTC)
Expand All @@ -162,7 +163,7 @@ def get_result(self, area: Area) -> dict:
"rght": area.rght,
"tree_id": area.tree_id,
"level": area.level,
"parent": area.parent,
"parent": str(area.parent.id) if area.parent else None,
"area_type": str(area.area_type.id),
}

Expand All @@ -189,6 +190,10 @@ def test_get_area_list_filter(self) -> None:
({"valid_until_after": "2021-01-01"}, []),
({"area_type_area_level": 1}, [self.area1]),
({"area_type_area_level": 2}, [self.area2]),
({"parent_id": str(self.area1.id)}, [self.area2]),
({"parent_p_code": self.area1.p_code}, [self.area2]),
({"parent_id": str(self.area2.id)}, []),
({"parent_p_code": self.area2.p_code}, []),
):
with token_grant_permission(self.token, Grant.API_READ_ONLY):
response = self.client.get(self.url, filter_data) # type: ignore
Expand Down
68 changes: 37 additions & 31 deletions tests/unit/apps/payment/test_payment_notification.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ def test_send_email_notification(self, mock_send: Any) -> None:
payment_notification.send_email_notification()
self.assertEqual(
mock_send.call_count,
3,
1,
)

@mock.patch("hct_mis_api.apps.payment.notifications.MailjetClient.send_email")
Expand All @@ -278,8 +278,7 @@ def test_send_email_notification_subject_test_env(self, mock_send: Any) -> None:
self.user_action_user,
f"{timezone.now():%-d %B %Y}",
)
for mailjet_client in payment_notification.emails:
self.assertEqual(mailjet_client.subject, "[test] Payment pending for Approval")
self.assertEqual(payment_notification.email.subject, "[test] Payment pending for Approval")

@mock.patch("hct_mis_api.apps.payment.notifications.MailjetClient.send_email")
@override_config(SEND_PAYMENT_PLANS_NOTIFICATION=True)
Expand All @@ -291,8 +290,7 @@ def test_send_email_notification_subject_prod_env(self, mock_send: Any) -> None:
self.user_action_user,
f"{timezone.now():%-d %B %Y}",
)
for mailjet_client in payment_notification.emails:
self.assertEqual(mailjet_client.subject, "Payment pending for Approval")
self.assertEqual(payment_notification.email.subject, "Payment pending for Approval")

@mock.patch("hct_mis_api.apps.utils.mailjet.requests.post")
@override_config(
Expand All @@ -307,14 +305,18 @@ def test_send_email_notification_catch_all_email(self, mock_post: Any) -> None:
f"{timezone.now():%-d %B %Y}",
)
payment_notification.send_email_notification()
for mailjet_client in payment_notification.emails:
self.assertEqual(
mailjet_client.recipients,
["[email protected]", "[email protected]"],
)
self.assertEqual(len(payment_notification.email.recipients), 2)
self.assertIn(
"[email protected]",
payment_notification.email.recipients,
)
self.assertIn(
"[email protected]",
payment_notification.email.recipients,
)
self.assertEqual(
mock_post.call_count,
3,
1,
)

@mock.patch("hct_mis_api.apps.utils.mailjet.requests.post")
Expand All @@ -329,18 +331,22 @@ def test_send_email_notification_without_catch_all_email(self, mock_post: Any) -
f"{timezone.now():%-d %B %Y}",
)
payment_notification.send_email_notification()
for mailjet_client in payment_notification.emails:
self.assertIn(
mailjet_client.recipients[0],
[
self.user_with_approval_permission_partner_unicef.email,
self.user_with_approval_permission_partner_with_program_access.email,
self.user_with_partner_action_permissions_and_program_access.email,
],
)
self.assertEqual(len(payment_notification.email.recipients), 3)
self.assertIn(
self.user_with_approval_permission_partner_unicef.email,
payment_notification.email.recipients,
)
self.assertIn(
self.user_with_approval_permission_partner_with_program_access.email,
payment_notification.email.recipients,
)
self.assertIn(
self.user_with_partner_action_permissions_and_program_access.email,
payment_notification.email.recipients,
)
self.assertEqual(
mock_post.call_count,
3,
1,
)

@mock.patch("hct_mis_api.apps.utils.mailjet.requests.post")
Expand All @@ -358,16 +364,16 @@ def test_send_email_notification_exclude_superuser(self, mock_post: Any) -> None
f"{timezone.now():%-d %B %Y}",
)
payment_notification.send_email_notification()
for mailjet_client in payment_notification.emails:
self.assertIn(
mailjet_client.recipients[0],
[
self.user_with_approval_permission_partner_with_program_access.email,
self.user_with_partner_action_permissions_and_program_access.email,
],
)
self.assertNotIn(self.user_with_approval_permission_partner_unicef.email, payment_notification.emails)
self.assertEqual(len(payment_notification.email.recipients), 2)
self.assertIn(
self.user_with_approval_permission_partner_with_program_access.email,
payment_notification.email.recipients,
)
self.assertIn(
self.user_with_partner_action_permissions_and_program_access.email,
payment_notification.email.recipients,
)
self.assertEqual(
mock_post.call_count,
2,
1,
)

0 comments on commit bd77c0f

Please sign in to comment.