Skip to content

Commit 3e1c0e9

Browse files
committed
Update grant form texts for gender neutrality and consistency
Revised several Italian grant-related strings in `index.ts` to use more gender neutral language and improve consistency in tone. This helps ensure the form is welcoming and inclusive to all users, and maintains a coherent voice across the application.
1 parent 65f917e commit 3e1c0e9

File tree

13 files changed

+681
-308
lines changed

13 files changed

+681
-308
lines changed

backend/grants/admin.py

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -172,27 +172,13 @@ class Meta:
172172

173173

174174
def _check_amounts_are_not_empty(grant: Grant, request):
175-
if grant.total_amount is None:
175+
if grant.total_allocated_amount == 0:
176176
messages.error(
177177
request,
178178
f"Grant for {grant.name} is missing 'Total Amount'!",
179179
)
180180
return False
181181

182-
if grant.has_approved_accommodation() and grant.accommodation_amount is None:
183-
messages.error(
184-
request,
185-
f"Grant for {grant.name} is missing 'Accommodation Amount'!",
186-
)
187-
return False
188-
189-
if grant.has_approved_travel() and grant.travel_amount is None:
190-
messages.error(
191-
request,
192-
f"Grant for {grant.name} is missing 'Travel Amount'!",
193-
)
194-
return False
195-
196182
return True
197183

198184

@@ -216,10 +202,10 @@ def send_reply_emails(modeladmin, request, queryset):
216202

217203
for grant in queryset:
218204
if grant.status in (Grant.Status.approved,):
219-
if grant.approved_type is None:
205+
if not grant.reimbursements.exists():
220206
messages.error(
221207
request,
222-
f"Grant for {grant.name} is missing 'Grant Approved Type'!",
208+
f"Grant for {grant.name} is missing reimbursement categories!",
223209
)
224210
return
225211

backend/grants/summary.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def calculate(self, conference_id):
4444
filtered_grants, statuses
4545
)
4646
gender_stats = self._aggregate_data_by_gender(filtered_grants, statuses)
47-
financial_summary, total_amount = self._aggregate_financial_data_by_status_new(
47+
financial_summary, total_amount = self._aggregate_financial_data_by_status(
4848
filtered_grants, statuses
4949
)
5050
grant_type_summary = self._aggregate_data_by_grant_type(

backend/grants/tasks.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
1+
import logging
12
from datetime import timedelta
23
from urllib.parse import urljoin
34

45
from django.conf import settings
56
from django.utils import timezone
6-
from notifications.models import EmailTemplate, EmailTemplateIdentifier
77

8-
from users.models import User
98
from grants.models import Grant
109
from integrations import slack
11-
12-
import logging
13-
10+
from notifications.models import EmailTemplate, EmailTemplateIdentifier
1411
from pycon.celery import app
12+
from users.models import User
1513

1614
logger = logging.getLogger(__name__)
1715

@@ -32,7 +30,7 @@ def send_grant_reply_approved_email(*, grant_id, is_reminder):
3230
variables = {
3331
"reply_url": reply_url,
3432
"start_date": f"{grant.conference.start:%-d %B}",
35-
"end_date": f"{grant.conference.end+timedelta(days=1):%-d %B}",
33+
"end_date": f"{grant.conference.end + timedelta(days=1):%-d %B}",
3634
"deadline_date_time": f"{grant.applicant_reply_deadline:%-d %B %Y %H:%M %Z}",
3735
"deadline_date": f"{grant.applicant_reply_deadline:%-d %B %Y}",
3836
"visa_page_link": urljoin(settings.FRONTEND_URL, "/visa"),
@@ -42,12 +40,19 @@ def send_grant_reply_approved_email(*, grant_id, is_reminder):
4240
}
4341

4442
if grant.has_approved_travel():
45-
if not grant.travel_amount:
43+
from grants.models import GrantReimbursementCategory
44+
45+
travel_reimbursements = grant.reimbursements.filter(
46+
category__category=GrantReimbursementCategory.Category.TRAVEL
47+
)
48+
travel_amount = sum(r.granted_amount for r in travel_reimbursements)
49+
50+
if not travel_amount or travel_amount == 0:
4651
raise ValueError(
4752
"Grant travel amount is set to Zero, can't send the email!"
4853
)
4954

50-
variables["travel_amount"] = f"{grant.travel_amount:.0f}"
55+
variables["travel_amount"] = f"{travel_amount:.0f}"
5156

5257
_new_send_grant_email(
5358
template_identifier=EmailTemplateIdentifier.grant_approved,

backend/grants/tests/factories.py

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1+
import random
2+
from decimal import Decimal
3+
14
import factory.fuzzy
25
from factory.django import DjangoModelFactory
36

47
from conferences.tests.factories import ConferenceFactory
5-
from grants.models import Grant
6-
from helpers.constants import GENDERS
7-
from users.tests.factories import UserFactory
88
from countries import countries
9-
from participants.tests.factories import ParticipantFactory
9+
from grants.models import Grant, GrantReimbursement, GrantReimbursementCategory
10+
from helpers.constants import GENDERS
1011
from participants.models import Participant
11-
import random
12+
from participants.tests.factories import ParticipantFactory
13+
from users.tests.factories import UserFactory
1214

1315

1416
class GrantFactory(DjangoModelFactory):
@@ -57,3 +59,54 @@ def _create(self, model_class, *args, **kwargs):
5759
ParticipantFactory(user_id=grant.user.id, conference=grant.conference)
5860

5961
return grant
62+
63+
64+
class GrantReimbursementCategoryFactory(DjangoModelFactory):
65+
class Meta:
66+
model = GrantReimbursementCategory
67+
68+
conference = factory.SubFactory(ConferenceFactory)
69+
name = factory.LazyAttribute(
70+
lambda obj: GrantReimbursementCategory.Category(obj.category).label
71+
)
72+
description = factory.Faker("sentence", nb_words=6)
73+
max_amount = factory.fuzzy.FuzzyInteger(0, 1000)
74+
category = factory.fuzzy.FuzzyChoice(
75+
[choice[0] for choice in GrantReimbursementCategory.Category.choices]
76+
)
77+
included_by_default = False
78+
79+
class Params:
80+
ticket = factory.Trait(
81+
category=GrantReimbursementCategory.Category.TICKET,
82+
name="Ticket",
83+
max_amount=Decimal("100"),
84+
included_by_default=True,
85+
)
86+
travel = factory.Trait(
87+
category=GrantReimbursementCategory.Category.TRAVEL,
88+
name="Travel",
89+
max_amount=Decimal("500"),
90+
included_by_default=False,
91+
)
92+
accommodation = factory.Trait(
93+
category=GrantReimbursementCategory.Category.ACCOMMODATION,
94+
name="Accommodation",
95+
max_amount=Decimal("300"),
96+
included_by_default=False,
97+
)
98+
other = factory.Trait(
99+
category=GrantReimbursementCategory.Category.OTHER,
100+
name="Other",
101+
max_amount=Decimal("200"),
102+
included_by_default=False,
103+
)
104+
105+
106+
class GrantReimbursementFactory(DjangoModelFactory):
107+
class Meta:
108+
model = GrantReimbursement
109+
110+
grant = factory.SubFactory(GrantFactory)
111+
category = factory.SubFactory(GrantReimbursementCategoryFactory)
112+
granted_amount = factory.fuzzy.FuzzyInteger(0, 1000)

backend/grants/tests/test_admin.py

Lines changed: 50 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
from datetime import timedelta
2+
from decimal import Decimal
23
from unittest.mock import call
34

4-
from conferences.models.conference_voucher import ConferenceVoucher
5-
from conferences.tests.factories import ConferenceFactory, ConferenceVoucherFactory
6-
from grants.tests.factories import GrantFactory
75
import pytest
86
from django.utils import timezone
97

8+
from conferences.models.conference_voucher import ConferenceVoucher
9+
from conferences.tests.factories import ConferenceFactory, ConferenceVoucherFactory
1010
from grants.admin import (
1111
confirm_pending_status,
1212
create_grant_vouchers,
13+
mark_rejected_and_send_email,
1314
reset_pending_status_back_to_status,
1415
send_reply_emails,
15-
mark_rejected_and_send_email,
1616
)
17-
from grants.models import Grant
18-
17+
from grants.models import Grant, GrantReimbursement, GrantReimbursementCategory
18+
from grants.tests.factories import GrantFactory
1919

2020
pytestmark = pytest.mark.django_db
2121

@@ -60,9 +60,11 @@ def test_send_reply_emails_with_grants_from_multiple_conferences_fails(
6060
mock_send_rejected_email.assert_not_called()
6161

6262

63-
def test_send_reply_emails_approved_grant_missing_approved_type(rf, mocker, admin_user):
63+
def test_send_reply_emails_approved_grant_missing_reimbursements(
64+
rf, mocker, admin_user
65+
):
6466
mock_messages = mocker.patch("grants.admin.messages")
65-
grant = GrantFactory(status=Grant.Status.approved, approved_type=None)
67+
grant = GrantFactory(status=Grant.Status.approved)
6668
request = rf.get("/")
6769
request.user = admin_user
6870
mock_send_approved_email = mocker.patch(
@@ -73,20 +75,28 @@ def test_send_reply_emails_approved_grant_missing_approved_type(rf, mocker, admi
7375

7476
mock_messages.error.assert_called_once_with(
7577
request,
76-
f"Grant for {grant.name} is missing 'Grant Approved Type'!",
78+
f"Grant for {grant.name} is missing reimbursement categories!",
7779
)
7880
mock_send_approved_email.assert_not_called()
7981

8082

8183
def test_send_reply_emails_approved_missing_amount(rf, mocker, admin_user):
8284
mock_messages = mocker.patch("grants.admin.messages")
83-
grant = GrantFactory(
84-
status=Grant.Status.approved,
85-
approved_type=Grant.ApprovedType.ticket_accommodation,
86-
total_amount=None,
85+
grant = GrantFactory(status=Grant.Status.approved)
86+
# Create reimbursement categories and reimbursements with 0 amount
87+
ticket_category = GrantReimbursementCategory.objects.create(
88+
conference=grant.conference,
89+
category=GrantReimbursementCategory.Category.TICKET,
90+
name="Ticket",
91+
max_amount=Decimal("100"),
92+
included_by_default=True,
93+
)
94+
# Create reimbursement with 0 amount so total_allocated_amount is 0
95+
GrantReimbursement.objects.create(
96+
grant=grant,
97+
category=ticket_category,
98+
granted_amount=Decimal("0"),
8799
)
88-
grant.total_amount = None
89-
grant.save()
90100
request = rf.get("/")
91101
request.user = admin_user
92102
mock_send_approved_email = mocker.patch(
@@ -106,10 +116,31 @@ def test_send_reply_emails_approved_set_deadline_in_fourteen_days(
106116
rf, mocker, admin_user
107117
):
108118
mock_messages = mocker.patch("grants.admin.messages")
109-
grant = GrantFactory(
110-
status=Grant.Status.approved,
111-
approved_type=Grant.ApprovedType.ticket_accommodation,
112-
total_amount=800,
119+
grant = GrantFactory(status=Grant.Status.approved)
120+
# Create reimbursement categories and reimbursements
121+
ticket_category = GrantReimbursementCategory.objects.create(
122+
conference=grant.conference,
123+
category=GrantReimbursementCategory.Category.TICKET,
124+
name="Ticket",
125+
max_amount=Decimal("100"),
126+
included_by_default=True,
127+
)
128+
accommodation_category = GrantReimbursementCategory.objects.create(
129+
conference=grant.conference,
130+
category=GrantReimbursementCategory.Category.ACCOMMODATION,
131+
name="Accommodation",
132+
max_amount=Decimal("700"),
133+
included_by_default=False,
134+
)
135+
GrantReimbursement.objects.create(
136+
grant=grant,
137+
category=ticket_category,
138+
granted_amount=Decimal("100"),
139+
)
140+
GrantReimbursement.objects.create(
141+
grant=grant,
142+
category=accommodation_category,
143+
granted_amount=Decimal("700"),
113144
)
114145
request = rf.get("/")
115146
request.user = admin_user

0 commit comments

Comments
 (0)