diff --git a/src/backend/apps/event/enums.py b/src/backend/apps/event/enums.py index 164a530d..a85ff7ce 100644 --- a/src/backend/apps/event/enums.py +++ b/src/backend/apps/event/enums.py @@ -133,5 +133,14 @@ class EVENT_DISPLAY_CATEGORY: MINOR_DELAYS = 'minorEvents' FUTURE_DELAYS = 'futureEvents' ROAD_CONDITION = 'roadConditions' - HIGHWAY_CAMERAS = 'highwayCams' CHAIN_UP = 'chainUps' + + +EVENT_DISPLAY_CATEGORY_TITLE = { + EVENT_DISPLAY_CATEGORY.CLOSURE: "closures", + EVENT_DISPLAY_CATEGORY.MAJOR_DELAYS: "major delays", + EVENT_DISPLAY_CATEGORY.MINOR_DELAYS: "minor delays", + EVENT_DISPLAY_CATEGORY.FUTURE_DELAYS: "future delays", + EVENT_DISPLAY_CATEGORY.ROAD_CONDITION: "road conditions", + EVENT_DISPLAY_CATEGORY.CHAIN_UP: "chain ups" +} diff --git a/src/backend/apps/event/tasks.py b/src/backend/apps/event/tasks.py index f6be7c26..df079148 100644 --- a/src/backend/apps/event/tasks.py +++ b/src/backend/apps/event/tasks.py @@ -7,6 +7,7 @@ from apps.event.enums import ( EVENT_DIFF_FIELDS, EVENT_DISPLAY_CATEGORY, + EVENT_DISPLAY_CATEGORY_TITLE, EVENT_STATUS, EVENT_TYPE, EVENT_UPDATE_FIELDS, @@ -240,6 +241,52 @@ def send_event_notifications(updated_event_ids, dt=None): send_route_notifications(saved_route, updated_event_ids) +def generate_settings_message(route): + msg = 'Based on your settings, you are being notified for all new and updated ' + + # Add event types + if route.notification_types and len(route.notification_types) == 4: + msg += 'information ' + + else: + types = [EVENT_DISPLAY_CATEGORY_TITLE[t] for t in route.notification_types] + if len(types) > 2: + msg += f'{", ".join(types[:-1])}, and {types[-1]} ' + elif len(types) == 2: + msg += f'{types[0]} and {types[1]} ' + else: + msg += f'{types[0]} ' + + msg += 'that affects your route ' + + # Add date/time + # Immediately and all the time + if not route.notification_start_time: + msg += 'at any time.' + + else: + msg += (f'between {route.notification_start_time.strftime("%I:%M%p").lower()} ' + f'and {route.notification_end_time.strftime("%I:%M%p").lower()} ') + + # Specific date + if route.notification_end_date: + msg += (f'from {route.notification_start_date.strftime("%B %d")} ' + f'and {route.notification_end_date.strftime("%B %d")}.') + + # Date range + elif route.notification_start_date: + msg += f'on {route.notification_start_date.strftime("%B %d")}.' + + # Days of the week + elif len(route.notification_days) == 7: + msg += 'every day.' + + else: + msg += f'every {", ".join(route.notification_days)}.' + + return msg + + def send_route_notifications(saved_route, updated_event_ids): # Apply a 150m buffer to the route geometry saved_route.route.transform(3857) @@ -262,6 +309,7 @@ def send_route_notifications(saved_route, updated_event_ids): 'display_category': event.display_category, 'display_category_title': event.display_category_title, 'fe_base_url': settings.FRONTEND_BASE_URL, + 'footer_message': generate_settings_message(saved_route), } text = render_to_string('email/event_updated.txt', context) diff --git a/src/backend/apps/event/templates/email/event_updated.html b/src/backend/apps/event/templates/email/event_updated.html index 2ac5d03c..1a611378 100644 --- a/src/backend/apps/event/templates/email/event_updated.html +++ b/src/backend/apps/event/templates/email/event_updated.html @@ -169,7 +169,7 @@ - Based on your settings, you are being notified for new and updated advisories, closures, major delays, and road conditions from 9:00 am to 10:30 am, every Monday, Tuesday, Wednesday, Thursday, Friday. + {{ footer_message }} diff --git a/src/backend/apps/event/tests/test_event_notification.py b/src/backend/apps/event/tests/test_event_notification.py index 8f8e0a0f..80e7a041 100644 --- a/src/backend/apps/event/tests/test_event_notification.py +++ b/src/backend/apps/event/tests/test_event_notification.py @@ -11,7 +11,7 @@ EVENT_TYPE, ) from apps.event.models import Event -from apps.event.tasks import send_event_notifications +from apps.event.tasks import generate_settings_message, send_event_notifications from django.contrib.gis.geos import LineString, MultiLineString, Point from django.core import mail from django.test import TestCase @@ -22,7 +22,6 @@ EVENT_DISPLAY_CATEGORY.MINOR_DELAYS, EVENT_DISPLAY_CATEGORY.FUTURE_DELAYS, EVENT_DISPLAY_CATEGORY.ROAD_CONDITION, - EVENT_DISPLAY_CATEGORY.HIGHWAY_CAMERAS, EVENT_DISPLAY_CATEGORY.CHAIN_UP ] @@ -242,3 +241,81 @@ def test_restricted_types(self): assert 'Always Active Route' in mail.outbox[0].subject assert 'Always Active Minor Route' in mail.outbox[1].subject assert 'Intersecting Event Minor' in mail.outbox[1].body + + def test_generate_footer_message(self): + # All notification types + route = SavedRoutes(notification_types=[ + EVENT_DISPLAY_CATEGORY.CLOSURE, + EVENT_DISPLAY_CATEGORY.MAJOR_DELAYS, + EVENT_DISPLAY_CATEGORY.MINOR_DELAYS, + EVENT_DISPLAY_CATEGORY.ROAD_CONDITION + ]) + msg = generate_settings_message(route) + assert 'all new and updated information ' in msg + + # Two notification types + route = SavedRoutes(notification_types=[ + EVENT_DISPLAY_CATEGORY.CLOSURE, + EVENT_DISPLAY_CATEGORY.MAJOR_DELAYS + ]) + msg = generate_settings_message(route) + assert 'closures and major delays ' in msg + + # Three notification types + route = SavedRoutes(notification_types=[ + EVENT_DISPLAY_CATEGORY.CLOSURE, + EVENT_DISPLAY_CATEGORY.MAJOR_DELAYS, + EVENT_DISPLAY_CATEGORY.MINOR_DELAYS + ]) + msg = generate_settings_message(route) + assert 'closures, major delays, and minor delays ' in msg + + # Any time + route = SavedRoutes( + notification_types=[EVENT_DISPLAY_CATEGORY.MAJOR_DELAYS], + ) + msg = generate_settings_message(route) + assert 'at any time.' in msg + + # Specific date + route = SavedRoutes( + notification_types=[EVENT_DISPLAY_CATEGORY.MAJOR_DELAYS], + notification_start_time=datetime.time(8, 0), + notification_end_time=datetime.time(17, 0), + notification_start_date=datetime.date(2023, 1, 1) + ) + msg = generate_settings_message(route) + assert 'on January 01.' in msg + + # Date range + route = SavedRoutes( + notification_types=[EVENT_DISPLAY_CATEGORY.MAJOR_DELAYS], + notification_start_time=datetime.time(8, 0), + notification_end_time=datetime.time(17, 0), + notification_start_date=datetime.date(2023, 1, 1), + notification_end_date=datetime.date(2023, 1, 7) + ) + msg = generate_settings_message(route) + assert 'between 08:00am and 05:00pm ' in msg + assert 'from January 01 and January 07.' in msg + + # Days of the week + route = SavedRoutes( + notification_types=[EVENT_DISPLAY_CATEGORY.MAJOR_DELAYS], + notification_start_time=datetime.time(12, 0), + notification_end_time=datetime.time(23, 0), + notification_days=['Monday', 'Wednesday', 'Friday'] + ) + msg = generate_settings_message(route) + assert 'between 12:00pm and 11:00pm ' in msg + assert 'every Monday, Wednesday, Friday.' in msg + + # Every day + route = SavedRoutes( + notification_types=[EVENT_DISPLAY_CATEGORY.MAJOR_DELAYS], + notification_start_time=datetime.time(8, 0), + notification_end_time=datetime.time(17, 0), + notification_days=['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'] + ) + msg = generate_settings_message(route) + assert 'every day.' in msg diff --git a/src/frontend/src/Components/routing/forms/NotificationDateTime.js b/src/frontend/src/Components/routing/forms/NotificationDateTime.js index a8eb7989..affc09e2 100644 --- a/src/frontend/src/Components/routing/forms/NotificationDateTime.js +++ b/src/frontend/src/Components/routing/forms/NotificationDateTime.js @@ -24,8 +24,8 @@ const NotificationDateTime = forwardRef((props, ref) => { const [specificDateOption, setSpecificDateOption] = useState(defaultSpecificDateOption); // Time range - const defaultStartTime = route.notification_start_time.split(':').slice(0, 2).join(':'); - const defaultEndTime = route.notification_end_time.split(':').slice(0, 2).join(':'); + const defaultStartTime = route.notification_start_time ? route.notification_start_time.split(':').slice(0, 2).join(':') : null; + const defaultEndTime = route.notification_end_time ? route.notification_end_time.split(':').slice(0, 2).join(':') : null; const [startTime, setStartTime] = useState(defaultStartTime); const [endTime, setEndTime] = useState(defaultEndTime);