def validate_service(self, request, uid): """Validates the specs values from request for the service uid. Returns a message if the validation failed """ spec_min = get_record_value(request, uid, "min") spec_max = get_record_value(request, uid, "max") min_panic = get_record_value(request, uid, "min_panic") max_panic = get_record_value(request, uid, "max_panic") if not min_panic and not max_panic: # Neither min_panic nor max_panic values are set, dismiss return None if min_panic: if not api.is_floatable(min_panic): return _("'{}' value must be numeric or empty").format( _("Min panic")) if api.to_float(min_panic) > api.to_float(spec_min): return _("'{}' value must be below '{}' or empty").format( _("Min panic"), _("Min")) if max_panic: if not api.is_floatable(max_panic): return _("'{}' value must be numeric or empty").format( _("Max panic")) if api.to_float(max_panic) < api.to_float(spec_max): return _("'{}' value must be above '{}' or empty").format( _("Max panic"), _("Max")) return None
def fiddle_panic_subfields(schema): # Add panic alert sub fields and labels labels = collections.OrderedDict(( ("min_panic", _("Min panic")), ("max_panic", _("Max panic")), )) for label in labels.keys(): if label not in schema["ResultsRange"].subfields: schema["ResultsRange"].subfields += (label, ) schema["ResultsRange"].subfield_labels.update(labels) return schema
def send_panic_email(self): # Send an alert email setup = api.get_setup() laboratory = setup.laboratory subject = self.request.get('subject') to = self.request.get('to') body = self.request.get('email_body') body = "<br/>".join(body.split("\r\n")) mime_msg = MIMEMultipart('related') mime_msg['Subject'] = subject mime_msg['From'] = formataddr( (encode_header(laboratory.getName()), laboratory.getEmailAddress())) mime_msg['To'] = to msg_txt = MIMEText(safe_unicode(body).encode('utf-8'), _subtype='html') mime_msg.preamble = 'This is a multi-part MIME message.' mime_msg.attach(msg_txt) try: host = api.get_tool("MailHost") host.send(mime_msg.as_string(), immediate=True) except Exception, msg: sample_id = api.get_id(self.sample) logger.error("Panic level email %s: %s" % (sample_id, str(msg))) message = _("Unable to send an email to alert client " "that some results exceeded the panic levels") message = "{}: {}".format(message, str(msg)) return self.redirect(self.back_url, message, "warning")
class IPanicControlPanel(Interface): """Control panel settings """ email_subject = schema.TextLine( title=_(u"Email alert subject"), description=_( "Template text for the panic alert email's subject. The accepted " "wildcards are: ${sample_id}, {client_sample_id} and ${client_id}" ), default=_( "Some results from Sample ${sample_id} exceeded panic range"), required=True, ) email_body = schema.Text( title=_(u"Email alert body template"), description=_( "Template text for the panic alert email's body. The accepted " "wildcards are: ${analyses}, ${lab_address}, ${sample_id}, " "{client_sample_id}, ${client_id} and ${sample_url}"), default=_( "Some results from the Sample ${sample_id} exceeded the panic " "levels:\n\n${analyses}\n\n--\n${lab_address}"), required=True, )
def folder_item(self, obj, item, index): # Don't do anything if senaite.panic is not installed # This is necessary for subscribers if not is_installed(): return item obj = api.get_object(obj) if utils.is_in_panic(obj): # Place a severe warning icon next to the result img = utils.get_image("panic.png", title=_("Panic result")) self.listing._append_html_element(item, element="Result", html=img) return item
def before_render(self): # Don't do anything if senaite.panic is not installed # This is necessary for subscribers if not is_installed(): return # Add the columns new_columns = collections.OrderedDict(( ("min_panic", { "title": _("Panic < Min"), "sortable": False }), ("max_panic", { "title": _("Panic > Max"), "sortable": False }), )) self.listing.columns.update(new_columns) # Apply the columns to all review_states keys = self.listing.columns.keys() map(lambda rv: rv.update({"columns": keys}), self.listing.review_states)
class EmailPopupView(BrowserView): implements(IViewView) template = ViewPageTemplateFile("templates/panic_alert_email.pt") _marker = object() _sample = _marker def __call__(self): # If sample is not valid, do not render if self.sample is None: return "Context is not a sample or no sample uid specified" # If the email for panic levels has been submitted, send the email if "email_popup_submit" in self.request: return self.send_panic_email() # Return the template return self.template() @property def back_url(self): return api.get_url(self.sample) @property def sample(self): if self._sample == self._marker: # Maybe current context is a Sample? if IAnalysisRequest.providedBy(self.context): self._sample = self.context return self._sample # Try with the uid uid = self.request.get("uid") sample = self.get_object_by_uid(uid) if sample and IAnalysisRequest.providedBy(sample): self._sample = sample else: self._sample = None return self._sample @view.memoize def get_object_by_uid(self, uid): if uid and uid != "0" and api.is_uid(uid): return api.get_object_by_uid(uid) return None @property def recipients(self): """Returns the recipients the notification email has to be sent to """ contacts = self.get_client_contacts(self.sample) contacts.extend(self.get_other_contacts(self.sample)) contacts = list(set(contacts)) # Generate the recipients recipients = map(lambda con: self.get_recipient(con), contacts) recipients = filter(None, recipients) # Include the client in the recipients list client = self.sample.getClient() client_email = client.getEmailAddress() if client_email: recipients.append({ "uid": api.get_uid(client), "name": api.get_title(client), "email": client_email, }) return recipients @property def formatted_recipients(self): out = list() for recipient in self.recipients: name = recipient["name"] email = recipient["email"] out.append("%s <%s>" % (name, email)) return ', '.join(out) @property def subject(self): """Returns the subject of the email """ email_subject = api.get_registry_record("senaite.panic.email_subject") client = self.sample.getClient() return self.context.translate( email_subject, mapping={ "sample_id": api.get_id(self.sample), "client_id": client.getClientID(), "client_sample_id": self.sample.getClientSampleID(), }) @property def body(self): """Returns the body message of the email """ setup = api.get_setup() laboratory = setup.laboratory lab_address = "\n".join(laboratory.getPrintAddress()) analyses = map(self.to_str, self.get_analyses_in_panic(self.sample)) analyses = "\n-".join(analyses) # TODO more mappings here (custom body)! email_body = api.get_registry_record("senaite.panic.email_body") client = self.sample.getClient() return self.context.translate( email_body, mapping={ "sample_id": api.get_id(self.sample), "analyses": analyses, "lab_address": lab_address, "client_id": client.getClientID(), "client_sample_id": self.sample.getClientSampleID(), "sample_url": api.get_url(self.sample), }) def get_client_contacts(self, sample): """Returns a list with the primary contacts from the client side """ contacts = [sample.getContact(), sample.getCCContact()] return filter(None, contacts) def get_other_contacts(self, sample): """Returns a list with additional contacts the alert has to be sent to """ contacts = set() contacts.add(api.get_user_contact(api_user.get_user())) # Get the responsibles of departments in_panic = self.get_analyses_in_panic(sample) departments = map(lambda an: an.getDepartment(), in_panic) managers = map(lambda dept: dept.getManager(), departments) for manager in managers: if manager: contacts.add(manager) return list(contacts) def get_analyses_in_panic(self, sample): """Returns the analyses found in panic for the given sample """ for analysis in sample.getAnalyses(full_objects=True): if utils.is_in_panic(analysis): yield analysis def get_recipient(self, contact): """Returns a dict representing an email recipient """ if not contact: return None contact_obj = api.get_object(contact) email = contact_obj.getEmailAddress() if not email: return None return {'uid': api.get_uid(contact_obj), 'name': contact_obj.Title(), 'email': email} def to_str(self, analysis): """Returns a string representation of the analysis """ return "{} ({}) {}".format( api.get_title(analysis), analysis.getKeyword(), utils.get_formatted_panic(analysis)).strip() def send_panic_email(self): # Send an alert email setup = api.get_setup() laboratory = setup.laboratory subject = self.request.get('subject') to = self.request.get('to') body = self.request.get('email_body') body = "<br/>".join(body.split("\r\n")) mime_msg = MIMEMultipart('related') mime_msg['Subject'] = subject mime_msg['From'] = formataddr( (encode_header(laboratory.getName()), laboratory.getEmailAddress())) mime_msg['To'] = to msg_txt = MIMEText(safe_unicode(body).encode('utf-8'), _subtype='html') mime_msg.preamble = 'This is a multi-part MIME message.' mime_msg.attach(msg_txt) try: host = api.get_tool("MailHost") host.send(mime_msg.as_string(), immediate=True) except Exception, msg: sample_id = api.get_id(self.sample) logger.error("Panic level email %s: %s" % (sample_id, str(msg))) message = _("Unable to send an email to alert client " "that some results exceeded the panic levels") message = "{}: {}".format(message, str(msg)) return self.redirect(self.back_url, message, "warning") # Store this fact in the Sample object self.sample.getField("PanicEmailAlertSent").set(self.sample, True) message = _("Panic notification email sent") return self.redirect(self.back_url, message)
class PanicControlPanelForm(RegistryEditForm): schema = IPanicControlPanel schema_prefix = "senaite.panic" label = _("SENAITE PANIC Settings")