Exemple #1
0
    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
Exemple #2
0
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
Exemple #3
0
    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,
    )
Exemple #5
0
    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
Exemple #6
0
    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)
Exemple #7
0
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")