diff --git a/NEMO/views/timed_services.py b/NEMO/views/timed_services.py index 0ebef423..0dbcf3e1 100644 --- a/NEMO/views/timed_services.py +++ b/NEMO/views/timed_services.py @@ -385,7 +385,7 @@ def email_out_of_time_reservation_notification(request): def send_email_out_of_time_reservation_notification(request=None): """ - Out of time reservation notification for areas is when a user is still logged in an area but his reservation expired. + Out of time reservation notification for areas is when a user is still logged in an area but either their reservation expired or they are outside of their permitted access hours. """ # Exit early if the out of time reservation email template has not been customized for the organization yet. # This feature only sends emails, so if the template is not defined there nothing to do. @@ -394,7 +394,8 @@ def send_email_out_of_time_reservation_notification(request=None): "The out of time reservation email template has not been customized for your organization yet. Please visit the customization page to upload a template, then out of time email notifications can be sent." ) - out_of_time_user_area = [] + out_of_time_user_reservations = [] + trigger_time = timezone.now().replace(second=0, microsecond=0) # Round down to the nearest minute. # Find all logged users access_records: List[AreaAccessRecord] = ( @@ -409,31 +410,36 @@ def send_email_out_of_time_reservation_notification(request=None): if customer.is_staff or customer.is_service_personnel: continue - if area.requires_reservation: - # Calculate the timestamp of how late a user can be logged in after a reservation ended. - threshold = ( - timezone.now() - if not area.logout_grace_period - else timezone.now() - timedelta(minutes=area.logout_grace_period) - ) - threshold = datetime.replace(threshold, second=0, microsecond=0) # Round down to the nearest minute. - ending_reservations = Reservation.objects.filter( - cancelled=False, - missed=False, - shortened=False, - area=area, - user=customer, - start__lte=timezone.now(), - end=threshold, - ) - # find out if a reservation is starting right at the same time (in case of back to back reservations, in which case customer is good) - starting_reservations = Reservation.objects.filter( - cancelled=False, missed=False, shortened=False, area=area, user=customer, start=threshold - ) - if ending_reservations.exists() and not starting_reservations.exists(): - out_of_time_user_area.append(ending_reservations[0]) + threshold = ( + trigger_time if not area.logout_grace_period else trigger_time - timedelta(minutes=area.logout_grace_period) + ) + physical_access = PhysicalAccessLevel.objects.filter(user=customer, area=area).first() + # Check first if allowed schedule has just expired + if ( + physical_access + and physical_access.accessible_at(threshold - timedelta(minutes=1)) + and not physical_access.accessible_at(threshold) + ): + out_of_time_user_reservations.append(Reservation(user=customer, area=area, end=threshold)) + else: + if area.requires_reservation: + ending_reservations = Reservation.objects.filter( + cancelled=False, + missed=False, + shortened=False, + area=area, + user=customer, + start__lte=timezone.now(), + end=threshold, + ) + # find out if a reservation is starting right at the same time (in case of back to back reservations, in which case customer is good) + starting_reservations = Reservation.objects.filter( + cancelled=False, missed=False, shortened=False, area=area, user=customer, start=threshold + ) + if ending_reservations.exists() and not starting_reservations.exists(): + out_of_time_user_reservations.append(ending_reservations[0]) - for reservation in out_of_time_user_area: + for reservation in out_of_time_user_reservations: send_out_of_time_reservation_notification(reservation, request) return HttpResponse() @@ -443,7 +449,8 @@ def send_out_of_time_reservation_notification(reservation: Reservation, request= message = get_media_file_contents("out_of_time_reservation_email.html") user_office_email = EmailsCustomization.get("user_office_email_address") if message and user_office_email: - subject = "Out of time in the " + str(reservation.area.name) + name = str(reservation.area.name) + subject = "Out of time for the " + name if reservation.start else "Out of allowed schedule for the " + name message = render_email_template(message, {"reservation": reservation}, request) recipients = reservation.user.get_emails( reservation.user.get_preferences().email_send_reservation_ending_reminders diff --git a/resources/emails/out_of_time_reservation_email.html b/resources/emails/out_of_time_reservation_email.html index 55e9291e..bf208c00 100644 --- a/resources/emails/out_of_time_reservation_email.html +++ b/resources/emails/out_of_time_reservation_email.html @@ -11,18 +11,38 @@ font-family: 'Avenir Next', 'Helvetica Neue', 'Helvetica', 'Arial', 'sans-serif'"> -

OUT OF TIME IN THE {{ reservation.area }}

+

+ OUT OF + {% if reservation.start %} + TIME + {% else %} + ALLOWED SCHEDULE + {% endif %} + IN THE {{ reservation.area }} +

Dear {{ reservation.user.first_name }},

-

Your reservation of the {{ reservation.area }} in the facility ended at {{ reservation.end }}.

+

+ Your + {% if reservation.start %} + reservation of the + {% else %} + allowed schedule for the + {% endif %} + {{ reservation.area }} in the {{ facility_name }} ended at {{ reservation.end }}. +

However, our records show that you are still logged in the {{ reservation.area }}.

If you simply forgot to log out, please contact a staff member


- Please remember that the facility is a shared resource, and overstaying a reservation may inhibit the productivity of other facility users. + {% if reservation.start %} + Please remember that the {{ facility_name }} is a shared resource, and overstaying a reservation may inhibit the productivity of other facility users. + {% else %} + Please remember that users are not allowed to overstay in areas outside their allowed schedule. + {% endif %}