Skip to content

Commit 7bd1a33

Browse files
knikollaQuanMPhm
andauthored
Extend alerting email to managers (#281)
* Extend alerting email to managers - Introduces a new get_managers function that gets the list of managers with enabled notifications. - Switches from Django's send_mail to ColdFront's send_email because of CC support and better error handling. - Removes EMAIL_ENABLED since that is built into send_email now. - Fixes some bugs with parameter passing that slipped through review due to missing unit tests. - Added unit test. Co-authored-by: Quan Pham <qmpham2019@gmail.com>
1 parent 806916d commit 7bd1a33

File tree

2 files changed

+72
-14
lines changed

2 files changed

+72
-14
lines changed

src/coldfront_plugin_cloud/management/commands/fetch_daily_billable_usage.py

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,9 @@
4848

4949
CENTER_BASE_URL = import_from_settings("CENTER_BASE_URL")
5050
EMAIL_SENDER = import_from_settings("EMAIL_SENDER")
51-
EMAIL_ENABLED = import_from_settings("EMAIL_ENABLED")
5251
EMAIL_TEMPLATE = """Dear New England Research Cloud user,
5352
54-
Your {resource.name} {resource.type} Allocation in project {allocation.project.title} has reached your preset Alert value.
53+
Your {resource.name} {resource.resource_type} Allocation in project {allocation.project.title} has reached your preset Alert value.
5554
5655
- As of midnight last night, your Allocation reached or exceeded your preset Alert value of {alert_value}.
5756
- To view your Allocation information visit {url}/allocation/{allocation.id}
@@ -259,11 +258,11 @@ def handle_alerting(
259258
f"{allocation.id} of {allocation.project.title} exceeded"
260259
f"alerting value of {allocation_alerting_value}."
261260
)
262-
if not already_alerted and EMAIL_ENABLED:
261+
if not already_alerted:
263262
try:
264263
cls.send_alert_email(
265264
allocation,
266-
allocation.resources.first().name,
265+
allocation.get_parent_resource,
267266
allocation_alerting_value,
268267
)
269268
logger.info(
@@ -276,15 +275,28 @@ def handle_alerting(
276275
)
277276

278277
@staticmethod
279-
def send_alert_email(allocation: Allocation, resource: Resource, alert_value):
280-
mail.send_mail(
278+
def get_managers(allocation: Allocation):
279+
"""Returns list of managers with enabled notifications."""
280+
managers_query = allocation.project.projectuser_set.filter(
281+
role__name="Manager", status__name="Active", enable_notifications=True
282+
)
283+
return [manager.user.email for manager in managers_query]
284+
285+
@classmethod
286+
def send_alert_email(cls, allocation: Allocation, resource: Resource, alert_value):
287+
mail.send_email(
281288
subject="Allocation Usage Alert",
282-
message=EMAIL_TEMPLATE.format(
289+
body=EMAIL_TEMPLATE.format(
283290
allocation=allocation,
284291
resource=resource,
285292
alert_value=alert_value,
286293
url=CENTER_BASE_URL,
287294
),
288-
from_email=EMAIL_SENDER,
289-
recipient_list=[allocation.project.pi.email],
295+
sender=EMAIL_SENDER,
296+
receiver_list=[allocation.project.pi.email],
297+
cc=[
298+
x
299+
for x in cls.get_managers(allocation)
300+
if x != allocation.project.pi.email
301+
],
290302
)

src/coldfront_plugin_cloud/tests/unit/test_fetch_daily_billable_usage.py

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import io
2+
from unittest import mock
23

34
from unittest.mock import Mock, patch
45

@@ -21,6 +22,18 @@
2122
test-allocation-2,OpenStack CPU,0.25
2223
"""
2324

25+
OUTPUT_EMAIL_TEMPLATE = """Dear New England Research Cloud user,
26+
27+
Your FakeProd OpenStack Allocation in project FakeProject has reached your preset Alert value.
28+
29+
- As of midnight last night, your Allocation reached or exceeded your preset Alert value of 100.
30+
- To view your Allocation information visit http://localhost/allocation/{allocation_id}
31+
32+
Thank you,
33+
New England Research Cloud (NERC)
34+
https://nerc.mghpcc.org/
35+
"""
36+
2437

2538
class TestFetchDailyBillableUsage(base.TestBase):
2639
def test_get_daily_location_for_prefix(self):
@@ -107,10 +120,6 @@ def test_get_allocations_for_daily_billing(self):
107120
self.assertNotIn(prod_allocation_3, returned_allocation_ids)
108121
self.assertNotIn(dev_allocation_1, returned_allocation_ids)
109122

110-
@patch(
111-
"coldfront_plugin_cloud.management.commands.fetch_daily_billable_usage.EMAIL_ENABLED",
112-
True,
113-
)
114123
@patch(
115124
"coldfront_plugin_cloud.management.commands.fetch_daily_billable_usage.RESOURCES_DAILY_ENABLED",
116125
["FakeProd"],
@@ -179,7 +188,7 @@ def test_call_command(self, mock_get_allocation_usage):
179188
"coldfront_plugin_cloud.management.commands.fetch_daily_billable_usage.Command.send_alert_email"
180189
) as mock:
181190
call_command("fetch_daily_billable_usage", date="2025-11-16")
182-
mock.assert_called_once_with(allocation_1, fakeprod.name, 200)
191+
mock.assert_called_once_with(allocation_1, fakeprod, 200)
183192
self.assertEqual(
184193
allocation_1.get_attribute(attributes.ALLOCATION_CUMULATIVE_CHARGES),
185194
"2025-11-16: 225.00 USD",
@@ -192,3 +201,40 @@ def test_call_command(self, mock_get_allocation_usage):
192201
allocation_1.get_attribute(attributes.ALLOCATION_CUMULATIVE_CHARGES),
193202
"2025-11-16: 225.00 USD",
194203
)
204+
205+
@patch(
206+
"coldfront_plugin_cloud.management.commands.fetch_daily_billable_usage.CENTER_BASE_URL",
207+
"http://localhost",
208+
)
209+
@patch(
210+
"coldfront_plugin_cloud.management.commands.fetch_daily_billable_usage.EMAIL_SENDER",
211+
"test@example.com",
212+
)
213+
def test_send_alert_email(self):
214+
fakeprod = self.new_openstack_resource(
215+
name="FakeProd", internal_name="FakeProd"
216+
)
217+
prod_project = self.new_project(title="FakeProject")
218+
allocation_1 = self.new_allocation(
219+
project=prod_project, resource=fakeprod, quantity=1, status="Active"
220+
)
221+
222+
manager = self.new_user()
223+
self.new_project_user(manager, prod_project, role="Manager")
224+
225+
normal_user = self.new_user()
226+
self.new_project_user(normal_user, prod_project, role="User")
227+
228+
with mock.patch("coldfront.core.utils.mail.send_email") as mock_send_email:
229+
Command.send_alert_email(
230+
allocation=allocation_1, resource=fakeprod, alert_value=100
231+
)
232+
mock_send_email.assert_called_once_with(
233+
subject="Allocation Usage Alert",
234+
body=OUTPUT_EMAIL_TEMPLATE.format(
235+
allocation_id=allocation_1.id,
236+
),
237+
sender="test@example.com",
238+
receiver_list=[allocation_1.project.pi.email],
239+
cc=[manager.email],
240+
)

0 commit comments

Comments
 (0)