def email_reservation_reminders(request): # Exit early if the reservation reminder email template has not been customized for the organization yet. reservation_reminder_message = get_media_file_contents('reservation_reminder_email.html') reservation_warning_message = get_media_file_contents('reservation_warning_email.html') if not reservation_reminder_message or not reservation_warning_message: calendar_logger.error("Reservation reminder email couldn't be send because reservation_reminder_email.html is not defined") return HttpResponseNotFound('The reservation reminder email template has not been customized for your organization yet. Please visit the customization page to upload a template, then reservation reminder email notifications can be sent.') # Find all reservations that are two hours from now, plus or minus 5 minutes to allow for time skew. preparation_time = 120 tolerance = 5 earliest_start = timezone.now() + timedelta(minutes=preparation_time) - timedelta(minutes=tolerance) latest_start = timezone.now() + timedelta(minutes=preparation_time) + timedelta(minutes=tolerance) upcoming_reservations = Reservation.objects.filter(cancelled=False, start__gt=earliest_start, start__lt=latest_start) # Email a reminder to each user with an upcoming reservation. for reservation in upcoming_reservations: item = reservation.reservation_item item_type = reservation.reservation_item_type if item_type == ReservationItemType.TOOL and item.operational and not item.problematic() and item.all_resources_available()\ or item_type == ReservationItemType.AREA and not item.required_resource_is_unavailable(): subject = item.name + " reservation reminder" rendered_message = Template(reservation_reminder_message).render(Context({'reservation': reservation, 'template_color': bootstrap_primary_color('success')})) elif (item_type == ReservationItemType.TOOL and not item.operational) or item.required_resource_is_unavailable(): subject = item.name + " reservation problem" rendered_message = Template(reservation_warning_message).render(Context({'reservation': reservation, 'template_color': bootstrap_primary_color('danger'), 'fatal_error': True})) else: subject = item.name + " reservation warning" rendered_message = Template(reservation_warning_message).render(Context({'reservation': reservation, 'template_color': bootstrap_primary_color('warning'), 'fatal_error': False})) user_office_email = get_customization('user_office_email_address') reservation.user.email_user(subject, rendered_message, user_office_email) return HttpResponse()
def send_new_task_emails(request, task: Task, task_images: List[TaskImages]): message = get_media_file_contents('new_task_email.html') attachments = None if task_images: attachments = [create_email_attachment(task_image.image, task_image.image.name) for task_image in task_images] if message: dictionary = { 'template_color': bootstrap_primary_color('danger') if task.force_shutdown else bootstrap_primary_color('warning'), 'user': request.user, 'task': task, 'tool': task.tool, 'tool_control_absolute_url': request.build_absolute_uri(task.tool.get_absolute_url()) } # Send an email to the appropriate staff that a new task has been created: subject = ('SAFETY HAZARD: ' if task.safety_hazard else '') + task.tool.name + (' shutdown' if task.force_shutdown else ' problem') message = Template(message).render(Context(dictionary)) managers = [] if hasattr(settings, 'LAB_MANAGERS'): managers = settings.LAB_MANAGERS recipients = tuple([r for r in [task.tool.primary_owner.email, *task.tool.backup_owners.all().values_list('email', flat=True), task.tool.notification_email_address, *managers] if r]) send_mail(subject, message, request.user.email, recipients, attachments) # Send an email to any user (excluding staff) with a future reservation on the tool: user_office_email = get_customization('user_office_email_address') message = get_media_file_contents('new_task_email.html') if user_office_email and message: upcoming_reservations = Reservation.objects.filter(start__gt=timezone.now(), cancelled=False, tool=task.tool, user__is_staff=False) for reservation in upcoming_reservations: if not task.tool.operational: subject = reservation.tool.name + " reservation problem" rendered_message = Template(message).render(Context({'reservation': reservation, 'template_color': bootstrap_primary_color('danger'), 'fatal_error': True})) else: subject = reservation.tool.name + " reservation warning" rendered_message = Template(message).render(Context({'reservation': reservation, 'template_color': bootstrap_primary_color('warning'), 'fatal_error': False})) reservation.user.email_user(subject, rendered_message, user_office_email)
def nanofab_rules(request): if request.method == 'GET': tutorial = get_media_file_contents('nanofab_rules_tutorial.html') if tutorial: dictionary = { 'active_user_count': User.objects.filter(is_active=True).count(), 'active_project_count': Project.objects.filter(active=True).count(), } tutorial = Template(tutorial).render(RequestContext(request, dictionary)) return render(request, 'nanofab_rules.html', {'nanofab_rules_tutorial': tutorial}) elif request.method == 'POST': summary = request.POST.get('making_reservations_summary', '').strip()[:3000] dictionary = { 'user': request.user, 'making_reservations_rule_summary': summary, } abuse_email = get_customization('abuse_email_address') email_contents = get_media_file_contents('nanofab_rules_tutorial_email.html') if abuse_email and email_contents: message = Template(email_contents, dictionary).render(Context(dictionary)) send_mail('Laboratory rules tutorial', '', abuse_email, [abuse_email], html_message=message) dictionary = { 'title': 'Laboratory rules tutorial', 'heading': 'Tutorial complete!', 'content': 'Tool usage and reservation privileges have been enabled on your user account.', } request.user.training_required = False request.user.save() return render(request, 'acknowledgement.html', dictionary)
def send_broadcast_email(request): if not get_media_file_contents('generic_email.html'): return HttpResponseBadRequest('Generic email template not defined. Visit the NEMO customizable_key_values page to upload a template.') form = EmailBroadcastForm(request.POST) if not form.is_valid(): return render(request, 'email/compose_email.html', {'form': form}) dictionary = { 'title': form.cleaned_data['title'], 'greeting': form.cleaned_data['greeting'], 'contents': form.cleaned_data['contents'], 'template_color': form.cleaned_data['color'], } content = get_media_file_contents('generic_email.html') content = Template(content).render(Context(dictionary)) users = None audience = form.cleaned_data['audience'] selection = form.cleaned_data['selection'] active_choice = form.cleaned_data['only_active_users'] try: if audience == 'tool': users = User.objects.filter(qualifications__id=selection) elif audience == 'project': users = User.objects.filter(projects__id=selection) elif audience == 'account': users = User.objects.filter(projects__account__id=selection) elif audience == 'all': users = User.objects.all() if active_choice: users.filter(is_active=True) except: dictionary = {'error': 'Your email was not sent. There was a problem finding the users to send the email to.'} return render(request, 'email/compose_email.html', dictionary) if not users: dictionary = {'error': 'The audience you specified is empty. You must send the email to at least one person.'} return render(request, 'email/compose_email.html', dictionary) if audience == 'tool': t = Tool.objects.filter(id=selection) subject = t[0].name + ': ' + form.cleaned_data['subject'] else: subject = form.cleaned_data['subject'] users = [x.email for x in users] if form.cleaned_data['copy_me']: users += [request.user.email] try: email = EmailMultiAlternatives(subject, from_email=request.user.email, bcc=set(users)) email.attach_alternative(content, 'text/html') email.send() except SMTPException as e: dictionary = { 'title': 'Email not sent', 'heading': 'There was a problem sending your email', 'content': 'NEMO was unable to send the email through the email server. The error message that NEMO received is: ' + str(e), } return render(request, 'acknowledgement.html', dictionary) dictionary = { 'title': 'Email sent', 'heading': 'Your email was sent', } return render(request, 'acknowledgement.html', dictionary)
def facility_rules(request): if request.method == "GET" and (request.user.training_required or request.user.access_expiration < timezone.now().date() + timedelta(weeks=2) or request.user.is_staff): tutorial = get_media_file_contents("facility_rules_tutorial.html") if tutorial: dictionary = { "active_user_count": User.objects.filter(is_active=True).count(), "active_project_count": Project.objects.filter(active=True).count(), } tutorial = Template(tutorial).render( RequestContext(request, dictionary)) return render(request, "facility_rules.html", {"facility_rules_tutorial": tutorial}) elif request.method == "POST": facility_name = get_customization("facility_name") summary = request.POST.get("making_reservations_summary", "").strip()[:3000] dictionary = { "user": request.user, "making_reservations_rule_summary": summary } abuse_email = get_customization("abuse_email_address") email_contents = get_media_file_contents( "facility_rules_tutorial_email.html") if abuse_email and email_contents: message = Template(email_contents, dictionary).render(Context(dictionary)) send_mail( subject=f"{facility_name} rules tutorial", content=message, from_email=abuse_email, to=[abuse_email], email_category=EmailCategory.SYSTEM, ) dictionary = { "title": f"{facility_name} rules tutorial", "heading": "Tutorial complete!", "content": "Tool usage and reservation privileges have been enabled on your user account.", } request.user.training_required = False while request.user.access_expiration < timezone.now().date( ) + timedelta(weeks=2): try: request.user.access_expiration = request.user.access_expiration.replace( year=request.user.access_expiration.year + 1) except ValueError: request.user.access_expiration = request.user.access_expiration.replace( year=request.user.access_expiration.year + 1, day=request.user.access_expiration.day - 1) request.user.save() return render(request, "acknowledgement.html", dictionary)
def email_usage_reminders(request): projects_to_exclude = request.GET.getlist("projects_to_exclude[]") busy_users = AreaAccessRecord.objects.filter( end=None, staff_charge=None).exclude(project__id__in=projects_to_exclude) busy_tools = UsageEvent.objects.filter(end=None).exclude( project__id__in=projects_to_exclude) facility_name = get_customization('facility_name') if facility_name == '': facility_name = "Facility" # Make lists of all the things a user is logged in to. # We don't want to send 3 separate emails if a user is logged into three things. # Just send one email for all the things! aggregate = {} for access_record in busy_users: key = str(access_record.customer) aggregate[key] = { 'email': access_record.customer.email, 'first_name': access_record.customer.first_name, 'resources_in_use': [str(access_record.area)], } for usage_event in busy_tools: key = str(usage_event.operator) if key in aggregate: aggregate[key]['resources_in_use'].append(usage_event.tool.name) else: aggregate[key] = { 'email': usage_event.operator.email, 'first_name': usage_event.operator.first_name, 'resources_in_use': [usage_event.tool.name], } user_office_email = get_customization('user_office_email_address') message = get_media_file_contents('usage_reminder_email.html') if message: subject = f"{facility_name} usage" for user in aggregate.values(): rendered_message = Template(message).render(Context({'user': user})) send_mail(subject, '', user_office_email, [user['email']], html_message=rendered_message) message = get_media_file_contents('staff_charge_reminder_email.html') if message: busy_staff = StaffCharge.objects.filter(end=None) for staff_charge in busy_staff: subject = "Active staff charge since " + format_datetime( staff_charge.start) rendered_message = Template(message).render( Context({'staff_charge': staff_charge})) staff_charge.staff_member.email_user(subject, rendered_message, user_office_email) return HttpResponse()
def send_alert_emails(alert): user_office_email = get_customization('user_office_email_address') facility_name = get_customization('facility_name') if facility_name == '': facility_name = "Facility" generic_email = get_media_file_contents('generic_email.html') if user_office_email and generic_email: users = User.objects.filter(is_active=True).exclude(is_staff=True) subject = alert.title title = f"{facility_name} Alert" color = bootstrap_primary_color('danger') greeting = 'Labmembers,' message = alert.contents dictionary = { 'title': title, 'greeting': greeting, 'contents': message, 'template_color': color, } msg = Template(generic_email).render(Context(dictionary)) users = [x.email for x in users] email = EmailMultiAlternatives(subject, from_email=user_office_email, to=[user_office_email], bcc=set(users)) email.attach_alternative(msg, 'text/html') email.send()
def compose_email(request): audience = request.GET.get('audience') selection = request.GET.get('selection') try: if audience == 'tool': users = User.objects.filter( qualifications__id=selection).distinct() elif audience == 'project': users = User.objects.filter(projects__id=selection).distinct() elif audience == 'account': users = User.objects.filter( projects__account__id=selection).distinct() else: dictionary = {'error': 'You specified an invalid audience'} return render(request, 'email/email_broadcast.html', dictionary) except: dictionary = {'error': 'You specified an invalid audience parameter'} return render(request, 'email/email_broadcast.html', dictionary) generic_email_sample = get_media_file_contents('generic_email.html') dictionary = { 'audience': audience, 'selection': selection, 'users': users, } if generic_email_sample: generic_email_context = { 'title': 'TITLE', 'greeting': 'Greeting', 'contents': 'Contents', } dictionary['generic_email_sample'] = Template( generic_email_sample).render(Context(generic_email_context)) return render(request, 'email/compose_email.html', dictionary)
def login_user(request): if 'NEMO.views.authentication.RemoteUserAuthenticationBackend' in settings.AUTHENTICATION_BACKENDS or 'NEMO.views.authentication.NginxKerberosAuthorizationHeaderAuthenticationBackend' in settings.AUTHENTICATION_BACKENDS: if request.user.is_authenticated: return HttpResponseRedirect(reverse('landing')) else: return render(request, 'authorization_failed.html') dictionary = { 'login_banner': get_media_file_contents('login_banner.html'), 'user_name_or_password_incorrect': False, } if request.method == 'GET': return render(request, 'login.html', dictionary) username = request.POST.get('username', '') password = request.POST.get('password', '') user = authenticate(request, username=username, password=password) if user: login(request, user) try: next_page = request.GET[REDIRECT_FIELD_NAME] resolve(next_page) # Make sure the next page is a legitimate URL for NEMO except: next_page = reverse('landing') return HttpResponseRedirect(next_page) dictionary['user_name_or_password_incorrect'] = True return render(request, 'login.html', dictionary)
def safety(request): dictionary = {} if request.method == 'POST': form = SafetyIssueCreationForm(request.user, data=request.POST) if form.is_valid(): issue = form.save() send_safety_email_notification(request, issue) create_safety_notification(issue) dictionary = { 'title': 'Concern received', 'heading': 'Your safety concern was sent to NanoFab staff and will be addressed promptly', } if form.cleaned_data['post_alert']: now = timezone.now() alert_title = "Alert: Cleanroom may not be safe for entry" alert_preface = f'On {format_datetime(now)} {issue.reporter.get_full_name()} reported the following issue:\n' alert_contents = (alert_preface + issue.concern) safety_alert = Alert(title=alert_title, contents=alert_contents, creator=issue.reporter, debut_time=now) safety_alert.save() send_alert_emails(safety_alert) dictionary = { 'title': 'Concern received', 'heading': 'Your safety concern was sent to NanoFab staff and will be addressed promptly. An alert has been posted, and all labmembers have been emailed.', } return render(request, 'acknowledgement.html', dictionary) tickets = SafetyIssue.objects.filter(resolved=False).order_by('-creation_time') if not request.user.is_staff: tickets = tickets.filter(visible=True) dictionary['tickets'] = tickets dictionary['safety_introduction'] = get_media_file_contents('safety_introduction.html') dictionary['notifications'] = get_notifications(request.user, SafetyIssue) return render(request, 'safety/safety.html', dictionary)
def feedback(request): recipient = get_customization('feedback_email_address') email_contents = get_media_file_contents('feedback_email.html') if not recipient or not email_contents: return render(request, 'feedback.html', {'customization_required': True}) if request.method == 'GET': return render(request, 'feedback.html') contents = parse_parameter_string(request.POST, 'feedback', FEEDBACK_MAXIMUM_LENGTH) if contents == '': return render(request, 'feedback.html') dictionary = { 'contents': contents, 'user': request.user, } email = Template(email_contents).render(Context(dictionary)) send_mail('Feedback from ' + str(request.user), email, request.user.email, [recipient]) dictionary = { 'title': 'Feedback', 'heading': 'Thanks for your feedback!', 'content': 'Your feedback has been sent to the NanoFab staff. We will follow up with you as soon as we can.', } return render(request, 'acknowledgement.html', dictionary)
def login_user(request): # check to make sure we don't have a misconfiguration. pre-authentication backends need to use middleware response = check_pre_authentication_backends(request) if response: return response dictionary = { "login_banner": get_media_file_contents("login_banner.html"), "user_name_or_password_incorrect": False, } # if we are dealing with anything else than POST, send to login page if request.method != "POST": return render(request, "login.html", dictionary) # Otherwise try to log the user in else: username = request.POST.get("username", "") password = request.POST.get("password", "") try: user = authenticate(request, username=username, password=password) except (User.DoesNotExist, InactiveUserError): return authorization_failed(request) if user: login(request, user) try: next_page = request.GET[REDIRECT_FIELD_NAME] resolve( next_page ) # Make sure the next page is a legitimate URL for NEMO except: next_page = reverse("landing") return HttpResponseRedirect(next_page) dictionary["user_name_or_password_incorrect"] = True return render(request, "login.html", dictionary)
def send_reorder_supply_reminder_email(consumable: Consumable): user_office_email = get_customization('user_office_email_address') message = get_media_file_contents('reorder_supplies_reminder_email.html') if user_office_email and message: subject = f"Time to order more {consumable.name}" rendered_message = Template(message).render(Context({'item': consumable})) send_mail(subject=subject, content=rendered_message, from_email=user_office_email, to=[consumable.reminder_email], email_category=EmailCategory.SYSTEM)
def cancel_the_reservation(reservation: Reservation, user_cancelling_reservation: User, reason: Optional[str]): response = check_policy_to_cancel_reservation(reservation, user_cancelling_reservation) # Staff must provide a reason when cancelling a reservation they do not own. if reservation.user != user_cancelling_reservation and not reason: response = HttpResponseBadRequest("You must provide a reason when cancelling someone else's reservation.") if response.status_code == HTTPStatus.OK: # All policy checks passed, so cancel the reservation. reservation.cancelled = True reservation.cancellation_time = timezone.now() reservation.cancelled_by = user_cancelling_reservation if reason: ''' don't notify in this case since we are sending a specific email for the cancellation ''' reservation.save() dictionary = { 'staff_member': user_cancelling_reservation, 'reservation': reservation, 'reason': reason, 'template_color': bootstrap_primary_color('info') } email_contents = get_media_file_contents('cancellation_email.html') if email_contents: cancellation_email = Template(email_contents).render(Context(dictionary)) if getattr(reservation.user.preferences, 'attach_cancelled_reservation', False): attachment = create_ics_for_reservation(reservation, cancelled=True) reservation.user.email_user('Your reservation was cancelled', cancellation_email, user_cancelling_reservation.email, [attachment]) else: reservation.user.email_user('Your reservation was cancelled', cancellation_email, user_cancelling_reservation.email) else: ''' here the user cancelled his own reservation so notify him ''' reservation.save_and_notify() return response
def email_out_of_time_reservation_notification(request): """ Out of time reservation notification for areas is when a user is still logged in a area but his reservation expired. """ # Exit early if the missed reservation email template has not been customized for the organization yet. if not get_media_file_contents('out_of_time_reservation_email.html'): return HttpResponseNotFound('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 = [] # Find all logged users access_records:List[AreaAccessRecord] = AreaAccessRecord.objects.filter(end=None, staff_charge=None).prefetch_related('customer', 'area').only('customer', 'area') for access_record in access_records: # staff are exempt from out of time notification customer = access_record.customer area = access_record.area if customer.is_staff: 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. reservations = Reservation.objects.filter(cancelled=False, missed=False, shortened=False, area=area, user=customer, start__lte=timezone.now(), end=threshold) if reservations.exists(): out_of_time_user_area.append(reservations[0]) for reservation in out_of_time_user_area: send_out_of_time_reservation_notification(reservation) return HttpResponse()
def cancel_unused_reservations(request): # Exit early if the missed reservation email template has not been customized for the organization yet. if not get_media_file_contents('missed_reservation_email.html'): return HttpResponseNotFound('The missed reservation email template has not been customized for your organization yet. Please visit the NEMO customizable_key_values page to upload a template, then missed email notifications can be sent.') tools = Tool.objects.filter(visible=True, _operational=True, _missed_reservation_threshold__isnull=False) missed_reservations = [] for tool in tools: # If a tool is in use then there's no need to look for unused reservation time. if tool.in_use() or tool.required_resource_is_unavailable() or tool.scheduled_outage_in_progress(): continue # Calculate the timestamp of how long a user can be late for a reservation. threshold = (timezone.now() - timedelta(minutes=tool.missed_reservation_threshold)) threshold = datetime.replace(threshold, second=0, microsecond=0) # Round down to the nearest minute. # Find the reservations that began exactly at the threshold. reservation = Reservation.objects.filter(cancelled=False, missed=False, shortened=False, tool=tool, user__is_staff=False, start=threshold, end__gt=timezone.now()) for r in reservation: # Staff may abandon reservations. if r.user.is_staff: continue # If there was no tool enable or disable event since the threshold timestamp then we assume the reservation has been missed. if not (UsageEvent.objects.filter(tool_id__in=tool.get_family_tool_ids(), start__gte=threshold).exists() or UsageEvent.objects.filter(tool_id__in=tool.get_family_tool_ids(), end__gte=threshold).exists()): # Mark the reservation as missed and notify the user & NanoFab staff. r.missed = True r.save() missed_reservations.append(r) for r in missed_reservations: send_missed_reservation_notification(r) return HttpResponse()
def compose_email(request): try: audience = request.GET['audience'] selection = request.GET.getlist('selection') no_type = request.GET.get("no_type") == "on" users = get_users_for_email(audience, selection, no_type) except: dictionary = {'error': 'You specified an invalid audience parameter'} return render(request, 'email/email_broadcast.html', dictionary) generic_email_sample = get_media_file_contents('generic_email.html') dictionary = { 'audience': audience, 'selection': selection, 'no_type': no_type, 'users': users, } if generic_email_sample: generic_email_context = { 'title': 'TITLE', 'greeting': 'Greeting', 'contents': 'Contents', 'template_color': '#5bc0de', } dictionary['generic_email_sample'] = Template( generic_email_sample).render(Context(generic_email_context)) return render(request, 'email/compose_email.html', dictionary)
def send_missed_reservation_notification(reservation): subject = "Missed reservation for the " + str(reservation.tool) message = get_media_file_contents('missed_reservation_email.html') message = Template(message).render(Context({'reservation': reservation})) user_office_email = get_customization('user_office_email_address') abuse_email = get_customization('abuse_email_address') send_mail(subject, message, user_office_email, [reservation.user.email, abuse_email, user_office_email])
def consultation(request): recipient = get_customization('feedback_email_address') email_contents = get_media_file_contents('consultation_email.html') email_response_contents = get_media_file_contents( 'consultation_email_response.html') if not recipient or not email_contents or not email_response_contents: return render(request, 'feedback.html', {'customization_required': True}) if request.method == 'GET': return render(request, 'consultation.html') contents = parse_parameter_string(request.POST, 'consultation', FEEDBACK_MAXIMUM_LENGTH) if contents == '': return render(request, 'consultation.html') dictionary = { 'contents': contents, 'user': request.user, } email = Template(email_contents).render(Context(dictionary)) send_mail('Consultation Request from ' + str(request.user), '', request.user.email, [recipient], html_message=email) response_email = EmailMessage() email_body = Template(email_response_contents).render(Context(dictionary)) storage = get_storage_class()() response_email.subject = 'Design Consultation Request Follow Up' response_email.from_email = recipient response_email.to = [request.user.email] response_email.body = email_body response_email.content_subtype = "html" response_email.attach_file( storage.path('design_consultation_template.pptx')) response_email.send() dictionary = { 'title': 'Design Consultation Request', 'heading': 'Request Sent!', 'content': 'Your design consultation request has been sent to the staff. We will follow up with you as soon as we can.', } return render(request, 'acknowledgement.html', dictionary)
def send_user_cancelled_reservation_notification(reservation: Reservation): if getattr(reservation.user.preferences, 'attach_cancelled_reservation', False): subject = "[NEMO] Cancelled Reservation for the " + str(reservation.tool) message = get_media_file_contents('reservation_cancelled_user_email.html') message = Template(message).render(Context({'reservation': reservation})) user_office_email = get_customization('user_office_email_address') attachment = create_ics_for_reservation(reservation, cancelled=True) reservation.user.email_user(subject, message, user_office_email, [attachment])
def send_broadcast_email(request): content = get_media_file_contents('generic_email.html') if not content: return HttpResponseBadRequest('Generic email template not defined. Visit the customization page to upload a template.') form = EmailBroadcastForm(request.POST) if not form.is_valid(): return render(request, 'email/compose_email.html', {'form': form}) dictionary = { 'title': form.cleaned_data['title'], 'greeting': form.cleaned_data['greeting'], 'contents': form.cleaned_data['contents'], 'template_color': form.cleaned_data['color'], } content = Template(content).render(Context(dictionary)) users = None audience = form.cleaned_data['audience'] selection = form.cleaned_data['selection'] active_choice = form.cleaned_data['only_active_users'] try: if audience == 'tool': users = User.objects.filter(qualifications__id=selection) elif audience == 'project': users = User.objects.filter(projects__id=selection) elif audience == 'account': users = User.objects.filter(projects__account__id=selection) if active_choice: users = users.filter(is_active=True) except Exception as error: warning_message = 'Your email was not sent. There was a problem finding the users to send the email to.' dictionary = {'error': warning_message} logger.warning(warning_message + ' audience: {}, only_active: {}. The error message that was received is: {}'.format(audience, active_choice, str(error))) return render(request, 'email/compose_email.html', dictionary) if not users: dictionary = {'error': 'The audience you specified is empty. You must send the email to at least one person.'} return render(request, 'email/compose_email.html', dictionary) subject = form.cleaned_data['subject'] users = [x.email for x in users] if form.cleaned_data['copy_me']: users += [request.user.email] try: send_mail(subject=subject, content=content, from_email=request.user.email, bcc=set(users), email_category=EmailCategory.BROADCAST_EMAIL) except SMTPException as error: site_title = get_customization('site_title') error_message = f"{site_title} was unable to send the email through the email server. The error message that was received is: " + str(error) logger.exception(error_message) dictionary = { 'title': 'Email not sent', 'heading': 'There was a problem sending your email', 'content': error_message, } return render(request, 'acknowledgement.html', dictionary) dictionary = { 'title': 'Email sent', 'heading': 'Your email was sent', } return render(request, 'acknowledgement.html', dictionary)
def send_missed_reservation_notification(reservation): subject = "Missed reservation for the " + str(reservation.reservation_item) message = get_media_file_contents('missed_reservation_email.html') user_office_email = get_customization('user_office_email_address') abuse_email = get_customization('abuse_email_address') if message and user_office_email: message = Template(message).render(Context({'reservation': reservation})) send_mail(subject, message, user_office_email, [reservation.user.email, abuse_email, user_office_email]) else: calendar_logger.error("Missed reservation email couldn't be send because missed_reservation_email.html or user_office_email are not defined")
def forgot_password_process(request): email = request.POST.get('email') subject = "NEMO Password Reset" link = "" try: user = User.objects.get(email=email) message = get_media_file_contents('forgot_password_email.html') token = ForgotPasswordToken.create(email) token.save() link = request.build_absolute_uri(reverse('password_reset_token', args=[token.hash])) except User.DoesNotExist: user = None message = get_media_file_contents('forgot_password_email_no_user.html') dictionary = {"link": link} rendered_message = Template(message).render(Context(dictionary)) send_mail(subject, '', None, [email], html_message=rendered_message) return render(request, 'db_authentication/forgot_password_process.html', {'email': email})
def email_broadcast(request, audience=''): dictionary = { 'date_range': False, } if audience == 'tool': dictionary['search_base'] = Tool.objects.filter(visible=True) elif audience == 'project': dictionary['search_base'] = Project.objects.filter(active=True) elif audience == 'account': dictionary['search_base'] = Account.objects.filter(active=True) elif audience == 'user': dictionary['search_base'] = User.objects.filter( is_active=True).order_by('last_name', 'first_name') elif audience == 'group': dictionary['search_base'] = Group.objects.all() elif audience == 'tool_date': dictionary['search_base'] = Tool.objects.filter(visible=True) dictionary['date_range'] = True elif audience == 'project_date': dictionary['search_base'] = Project.objects.filter(active=True) dictionary['date_range'] = True elif audience == 'active_users_date': dictionary['search_base'] = User.objects.annotate( email_length=Length('email')).filter(is_active=True, email_length__gt=0).order_by( 'last_name', 'first_name') dictionary['date_range'] = True elif audience == 'active_users': dictionary['users'] = User.objects.annotate( email_length=Length('email')).filter(is_active=True, email_length__gt=0).order_by( 'last_name', 'first_name') dictionary['audience'] = audience dictionary['selection'] = None dictionary['cc_users'] = User.objects.filter(is_active=True).order_by( 'last_name', 'first_name') dictionary['date_range'] = False dictionary['start'] = None dictionary['end'] = None dictionary['current_time'] = timezone.now() generic_email_sample = get_media_file_contents('generic_email.html') if generic_email_sample: generic_email_context = { 'title': 'TITLE', 'greeting': 'Greeting', 'contents': 'Contents', } dictionary['generic_email_sample'] = Template( generic_email_sample).render(Context(generic_email_context)) return render(request, 'email/compose_email.html', dictionary) dictionary['audience'] = audience return render(request, 'email/email_broadcast.html', dictionary)
def send_out_of_time_reservation_notification(reservation:Reservation): subject = "Out of time in the " + str(reservation.area.name) message = get_media_file_contents('out_of_time_reservation_email.html') user_office_email = get_customization('user_office_email_address') if message and user_office_email: message = Template(message).render(Context({'reservation': reservation})) recipients = [reservation.user.email] recipients.extend(reservation.area.abuse_email_list()) send_mail(subject, message, user_office_email, recipients) else: calendar_logger.error("Out of time reservation email couldn't be send because out_of_time_reservation_email.html or user_office_email are not defined")
def send_tool_usage_counter_email(counter: ToolUsageCounter): user_office_email = get_customization('user_office_email_address') message = get_media_file_contents('counter_threshold_reached_email.html') if user_office_email and message: subject = f"Warning threshold reached for {counter.tool.name} {counter.name} counter" rendered_message = Template(message).render( Context({'counter': counter})) send_mail(subject=subject, content=rendered_message, from_email=user_office_email, to=counter.warning_email, email_category=EmailCategory.SYSTEM)
def set_task_status(request, task, status_name, user): if not user.is_staff and status_name: raise ValueError("Only staff can set task status") #If no status is given, assign to default status. This will make sure all tasks have a proper Task History if not status_name: status_name = "default" status = TaskStatus.objects.get_or_create(name=status_name) TaskHistory.objects.create(task=task, status=status_name, user=user, shutdown=task.force_shutdown) status_message = f'On {format_datetime(timezone.now())}, {user.get_full_name()} set the status of this task to "{status_name}".' task.progress_description = status_message if task.progress_description is None else task.progress_description + '\n\n' + status_message task.save() message = get_media_file_contents('task_status_notification.html') # Send an email to the appropriate staff that a task status has been updated: if message: dictionary = { 'template_color': bootstrap_primary_color('success'), 'title': f'{task.tool} task notification', 'status_message': status_message, 'notification_message': status.notification_message, 'task': task, 'tool_control_absolute_url': request.build_absolute_uri(task.tool.get_absolute_url()) } subject = f'{task.tool} task notification' message = Template(message).render(Context(dictionary)) recipients = [ task.tool.primary_tool_owner.email if status.notify_primary_tool_owner else None, task.tool.notification_email_address if status.notify_tool_notification_email else None, status.custom_notification_email_address ] if status.notify_backup_tool_owners: recipients += task.tool.backup_tool_owners.values_list('email') recipients = filter(None, recipients) send_mail(subject=subject, content=message, from_email=user.email, to=recipients, email_category=EmailCategory.TASKS)
def send_safety_email_notification(request, issue): recipient = get_customization('safety_email_address') message = get_media_file_contents('safety_issue_email.html') if recipient and message: subject = 'Safety issue' dictionary = { 'issue': issue, 'issue_absolute_url': request.build_absolute_uri(issue.get_absolute_url()), } rendered_message = Template(message).render(Context(dictionary)) from_email = issue.reporter.email if issue.reporter else recipient send_mail(subject=subject, content=rendered_message, from_email=from_email, to=[recipient], email_category=EmailCategory.SAFETY)
def compose_email(request): audience = request.GET.get('audience') selection = request.GET.get('selection') try: if audience == 'tool': users = User.objects.filter( qualifications__id=selection).distinct() elif request.user.is_staff: if audience == 'project': users = User.objects.filter(projects__id=selection).distinct() elif audience == 'account': users = User.objects.filter( projects__account__id=selection).distinct() elif audience == 'area': access_levels = Area.objects.get( pk=selection).get_physical_access_levels() user_filter = Q(physical_access_levels__in=access_levels) # if one of the access levels allows staff, add all staff if access_levels.filter(allow_staff_access=True).exists(): user_filter = user_filter | Q(is_staff=True) users = User.objects.filter(user_filter).distinct() elif audience == 'all': users = User.objects.all() else: dictionary = {'error': 'You specified an invalid audience'} return render(request, 'email/email_broadcast.html', dictionary) else: dictionary = { 'error': 'You may not broadcast email to this audience' } return render(request, 'email/email_broadcast.html', dictionary) except: dictionary = {'error': 'You specified an invalid audience parameter'} return render(request, 'email/email_broadcast.html', dictionary) generic_email_sample = get_media_file_contents('generic_email.html') dictionary = { 'audience': audience, 'selection': selection, 'users': users, } if generic_email_sample: generic_email_context = { 'title': 'TITLE', 'greeting': 'Greeting', 'contents': 'Contents', 'template_color': '#5bc0de', } dictionary['generic_email_sample'] = Template( generic_email_sample).render(Context(generic_email_context)) return render(request, 'email/compose_email.html', dictionary)
def send_safety_email_notification(request, issue): subject = 'Safety issue' dictionary = { 'issue': issue, 'issue_absolute_url': request.build_absolute_uri(issue.get_absolute_url()), } recipient = get_customization('safety_email_address') message = get_media_file_contents('safety_issue_email.html') if not recipient or not message: return rendered_message = Template(message).render(Context(dictionary)) from_email = issue.reporter.email if issue.reporter else recipient send_mail(subject, '', from_email, [recipient], html_message=rendered_message)