class EmailBroadcastForm(Form): subject = CharField(required=False) color = ChoiceField(choices=((bootstrap_primary_color('info'), 'info'), (bootstrap_primary_color('success'), 'success'), (bootstrap_primary_color('warning'), 'warning'), (bootstrap_primary_color('danger'), 'danger'))) title = CharField(required=False) greeting = CharField(required=False) contents = CharField(required=False) copy_me = BooleanField(initial=True) audience = ChoiceField(choices=[('tool', 'tool'), ('project', 'project'), ('account', 'account'), ('tool_date', 'tool_date'), ('project_date', 'project_date'), ('user', 'user'), ('group', 'group')], label="Audience") selection = IntegerField() recipient = CharField(required=False) only_active_users = BooleanField(initial=True) def clean_title(self): return self.cleaned_data['title'].upper()
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)
class EmailBroadcastForm(Form): subject = CharField(required=False) color = ChoiceField( choices=( (bootstrap_primary_color("info"), "info"), (bootstrap_primary_color("success"), "success"), (bootstrap_primary_color("warning"), "warning"), (bootstrap_primary_color("danger"), "danger"), ) ) title = CharField(required=False) greeting = CharField(required=False) contents = CharField(required=False) copy_me = BooleanField(initial=True) audience = ChoiceField(choices=[("tool", "tool"), ("project", "project"), ("account", "account"), ("area", "area"), ("user", "user")]) selection = CharField(required=False) no_type = BooleanField(initial=False, required=False) only_active_users = BooleanField(initial=True) def clean_title(self): return self.cleaned_data["title"].upper() def clean_selection(self): return self.data.getlist('selection')
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: return HttpResponseNotFound( 'The reservation reminder email template has not been customized for your organization yet. Please visit the NEMO customizable_key_values 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: tool = reservation.tool if tool.operational and not tool.problematic( ) and tool.all_resources_available(): subject = reservation.tool.name + " reservation reminder" rendered_message = Template(reservation_reminder_message).render( Context({ 'reservation': reservation, 'template_color': bootstrap_primary_color('success') })) elif not tool.operational or tool.required_resource_is_unavailable(): subject = reservation.tool.name + " reservation problem" rendered_message = Template(reservation_warning_message).render( Context({ 'reservation': reservation, 'template_color': bootstrap_primary_color('danger'), 'fatal_error': True })) else: subject = reservation.tool.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_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 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 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)
class EmailBroadcastForm(Form): subject = CharField(required=False) color = ChoiceField(choices=( (bootstrap_primary_color("info"), "info"), (bootstrap_primary_color("success"), "success"), (bootstrap_primary_color("warning"), "warning"), (bootstrap_primary_color("danger"), "danger"), )) title = CharField(required=False) greeting = CharField(required=False) contents = CharField(required=False) copy_me = BooleanField(initial=True) audience = ChoiceField( choices=[('tool', 'tool'), ('project', 'project'), ('account', 'account'), ('all', 'all')]) selection = IntegerField() only_active_users = BooleanField(initial=True) def clean_title(self): return self.cleaned_data["title"].upper()
def set_task_status(request, task, status_name, user): if not status_name: return if not user.is_staff: raise ValueError("Only staff can set task status") status = TaskStatus.objects.get(name=status_name) TaskHistory.objects.create(task=task, status=status_name, user=user) 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') if not message: return 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()) } # Send an email to the appropriate NanoFab staff that a new task has been created: 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, '', user.email, recipients, html_message=message)
def cancel_reservation(request, reservation_id): """ Cancel a reservation for a user. """ reservation = get_object_or_404(Reservation, id=reservation_id) response = check_policy_to_cancel_reservation(reservation, request.user) # Staff must provide a reason when cancelling a reservation they do not own. reason = parse_parameter_string(request.POST, 'reason') if reservation.user != request.user 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 = request.user reservation.save() if reason: dictionary = { 'staff_member': request.user, '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)) reservation.user.email_user('Your reservation was cancelled', cancellation_email, request.user.email) if request.device == 'desktop': return response if request.device == 'mobile': if response.status_code == HTTPStatus.OK: return render(request, 'mobile/cancellation_result.html', { 'event_type': 'Reservation', 'tool': reservation.tool }) else: return render(request, 'mobile/error.html', {'message': response.content})
def email_reservation_reminders(request): # Exit early if the reservation reminder email template has not been customized for the organization yet. good_message = get_media_file_contents('reservation_reminder_email.html') problem_message = get_media_file_contents('reservation_warning_email.html') if not good_message or not problem_message: return HttpResponseNotFound( 'The reservation reminder email template has not been customized for your organization yet. Please visit the NEMO customizable_key_values page to upload a template, then reservation reminder email notifications can be sent.' ) # Find all reservations in the next day #preparation_time = 120 These were sending an email for each reservation 2 hrs in advance. I'm not using them #tolerance = 5 earliest_start = timezone.now() latest_start = timezone.now() + timedelta(hours=24) 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. goodAggregate = {} problemAggregate = {} for reservation in upcoming_reservations: key = str(reservation.user) if reservation.tool.operational and not reservation.tool.problematic( ) and reservation.tool.all_resources_available(): if key in goodAggregate: goodAggregate[key]['Tools'].append( reservation.tool.name + " starting " + format_datetime(reservation.start)) else: goodAggregate[key] = { 'email': reservation.user.email, 'first_name': reservation.user.first_name, 'Tools': [ reservation.tool.name + " starting " + format_datetime(reservation.start) ], } else: if key in problemAggregate: problemAggregate[key]['Tools'].append( reservation.tool.name + " starting " + format_datetime(reservation.start)) else: problemAggregate[key] = { 'email': reservation.user.email, 'first_name': reservation.user.first_name, 'Tools': [ reservation.tool.name + " starting " + format_datetime(reservation.start) ], } user_office_email = get_customization('user_office_email_address') if good_message: subject = "Upcoming PRISM Cleanroom Reservations" for user in goodAggregate.values(): rendered_message = Template(good_message).render( Context({ 'user': user, 'template_color': bootstrap_primary_color('success') })) send_mail(subject, '', user_office_email, [user['email']], html_message=rendered_message) if problem_message: subject = "Problem With Upcoming PRISM Cleanroom Reservations" for user in problemAggregate.values(): rendered_message = Template(problem_message).render( Context({ 'user': user, 'template_color': bootstrap_primary_color('danger') })) send_mail(subject, '', user_office_email, [user['email']], html_message=rendered_message) return HttpResponse()
def send_new_task_emails(request, task): message = get_media_file_contents('new_task_email.html') 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 NanoFab 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)) recipients = tuple([ r for r in [ task.tool.primary_owner.email, task.tool.secondary_owner.email, task.tool.notification_email_address ] if r ]) send_mail(subject, '', request.user.email, recipients, html_message=message) # 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 send_new_task_emails(request, task): message = get_media_file_contents('new_task_email.html') 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 NanoFab 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)) 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 ] if r ]) send_mail(subject, '', request.user.email, recipients, html_message=message) # Send an email to all users (excluding staff) qualified 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: users = User.objects.filter(qualifications__id=task.tool.id, is_staff=False) 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()), 'labmember': True } subject = task.tool.name + (' shutdown' if task.force_shutdown else ' problem') users = [x.email for x in users] rendered_message = Template(message).render(Context(dictionary)) try: email = EmailMultiAlternatives(subject, from_email=user_office_email, bcc=set(users)) email.attach_alternative(rendered_message, '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), }
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 ] # Send an email to the appropriate staff that a new task has been created: 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()) } 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=subject, content=message, from_email=request.user.email, to=recipients, attachments=attachments, email_category=EmailCategory.TASKS) # Send an email to all users (excluding staff) qualified 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: users = User.objects.filter(qualifications__id=task.tool.id, is_staff=False, is_active=True) 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()), 'labmember': True } subject = task.tool.name + (' shutdown' if task.force_shutdown else ' problem') users = [x.email for x in users] rendered_message = Template(message).render(Context(dictionary)) #try: email = EmailMultiAlternatives(subject, from_email=user_office_email, bcc=set(users)) email.attach_alternative(rendered_message, 'text/html') email.send()
def send_task_update_emails(request, task): message = get_media_file_contents('generic_email.html') user_office_email = get_customization('user_office_email_address') if message and user_office_email: task.refresh_from_db() if not task.tool.problematic(): tool_status = 'up' elif task.tool.operational: tool_status = 'in problem status' else: tool_status = 'down' if task.resolved: subject = f'{task.tool} Issue Resolved' task_user = task.resolver elif task.cancelled: subject = f'{task.tool} Issue Cancelled' task_user = task.resolver else: subject = f'{task.tool} Update' task_user = task.last_updated_by messagebody = f""" The status of the {task.tool} was just modified by {task_user}. You can see the full status description here: {request.build_absolute_uri(task.tool.get_absolute_url())}. {task.tool} is currently {tool_status}. """ if task.resolved: messagebody += f""" Resolution description: {task.resolution_description} """ dictionary = { 'template_color': bootstrap_primary_color('success') if tool_status == 'up' else bootstrap_primary_color('warning'), 'title': subject, 'greeting': f'{task.tool} Users,', 'contents': messagebody } users = User.objects.filter(qualifications__id=task.tool.id, is_active=True) users = [x.email for x in users] rendered_message = Template(message).render(Context(dictionary)) try: email = EmailMultiAlternatives(subject, from_email=user_office_email, bcc=set(users)) email.attach_alternative(rendered_message, '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), }