Ejemplo n.º 1
0
    def add_columns(self):
        """Adds bhp-specific olumns in the listing
        """
        bhp_columns = {
            # Participant ID
            "getParticipantID": {
                "title": _("Participant ID"),
                "index": "getParticipantID",
                "sortable": True,
                "toggle": True,
                "after": "getClientSampleID"
            },
            # Visit Number
            "getVisit": {
                "title": _("Visit Code"),
                "attr": "getVisit",
                "sortable": False,
                "toggle": True,
                "after": "getParticipantID"
            }
        }

        # Add the columns
        rv_keys = map(lambda r: r["id"], self.listing.review_states)
        for column_id, column_values in bhp_columns.items():
            utils.add_column(listing=self.listing,
                             column_id=column_id,
                             column_values=column_values,
                             after=column_values["after"],
                             review_states=rv_keys)
Ejemplo n.º 2
0
    def fiddle(self, schema):
        # Add panic alert range columns
        validator = AnalysisSpecificationsValidator()
        schema['ResultsRange'].subfields += ('minpanic', 'maxpanic',
                                             'calculation')
        schema['ResultsRange'].subfield_validators['minpanic'] = validator
        schema['ResultsRange'].subfield_validators['maxpanic'] = validator
        schema['ResultsRange'].subfield_labels['minpanic'] = _('Min panic')
        schema['ResultsRange'].subfield_labels['maxpanic'] = _('Max panic')
        schema['ResultsRange'].subfield_labels['calculation'] = _(
            "Specification calculation")

        # Add grade ranges columns
        schema["ResultsRange"].subfields += GRADES_KEYS
        for grade in GRADES_KEYS:
            grade_label = grade.replace("_", " ")
            schema["ResultsRange"].subfield_labels[grade] = _(grade_label)

        srcwidget = schema['ResultsRange'].widget
        schema['ResultsRange'].widget = AnalysisSpecificationWidget(
            checkbox_bound=srcwidget.checkbox_bound,
            label=srcwidget.label,
            description=srcwidget.description,
        )
        return schema
Ejemplo n.º 3
0
def send_panic_email(view):
    ar = view.context
    if not IAnalysisRequest.providedBy(ar):
        return False

    if not ar.has_analyses_in_panic():
        addMessage(view, _("No results exceed the panic levels"), 'warning')
        return False

    # Send an alert email
    laboratory = view.context.bika_setup.laboratory
    subject = view.request.get('subject')
    to = view.request.get('to')
    body = view.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 = getToolByName(view.context, 'MailHost')
        host.send(mime_msg.as_string(), immediate=True)
    except Exception, msg:
        ar = view.context.id
        logger.error("Panic level email %s: %s" % (ar, str(msg)))
        message = _('Unable to send an email to alert client '
                    'that some results exceeded the panic levels') \
                  + (": %s" % str(msg))
        addMessage(view, message, 'warning')
        return False
Ejemplo n.º 4
0
    def __call__(self):
        form = self.request.form
        form_submitted = form.get("submitted", False)
        form_send = form.get("send", False)
        form_cancel = form.get("cancel", False)
        objs = self.get_objects()

        if not objs:
            return self.redirect(message=_("No items selected"))

        # Handle form submit
        if form_submitted and form_send:
            logger.info("*** SEND TO LAB ***")
            courier_uid = form.get("courier")
            courier = self.get_object_by_uid(courier_uid)

            if courier is None:
                return self.redirect(message=_("No courier selected"))

            sent_objects = []
            sent_object_ids = []
            for obj in objs:
                logger.info("*** SENDING {} TO LAB ***".format(obj.getId()))
                if self.send_to_lab(obj, courier):
                    sent_objects.append(obj)

            sent_object_ids = map(lambda obj: obj.getId(), sent_objects)
            message = None
            if sent_object_ids:
                message = _("Sent {} to Lab".format(
                    ", ".join(sent_object_ids)))

                # TODO Redirect the user to back_url while downloading pdf

                # Generate the delivery report
                pdf = generate_delivery_pdf(self.context, sent_objects)
                return self.response_pdf(pdf)

            else:
                message = _("All items have been already shipped".format(
                    ", ".join(sent_object_ids)))
            return self.redirect(message=message, level="info")

        # Handle form cancel
        if form_submitted and form_cancel:
            logger.info("*** CANCEL ***")
            return self.redirect(message=_("Delivery canceled"))

        # render the template
        return self.template()
Ejemplo n.º 5
0
def setup_laboratory(portal):
    """Setup Laboratory
    """
    logger.info("*** Setup Laboratory ***")
    lab = portal.bika_setup.laboratory
    lab.edit(title=_('BHP'))
    lab.reindexObject()

    # Set autoprinting of stickers on register
    portal.bika_setup.setAutoPrintStickers('register')
Ejemplo n.º 6
0
    def __init__(self, context, request, fieldvalue=[], allow_edit=True):
        BaseView.__init__(self,
                          context,
                          request,
                          fieldvalue=fieldvalue,
                          allow_edit=allow_edit)

        # Add "min panic" and "max panic" fields.
        min_panic = {"title": _("Min panic"), "sortable": False}
        self.add_column('minpanic', min_panic, before='warn_min')

        max_panic = {"title": _("Max panic"), "sortable": False}
        self.add_column('maxpanic', max_panic, after='warn_max')

        calculation = {
            "title": _("Specification Calculation"),
            "sortable": False,
            "type": "choices"
        }
        self.add_column("calculation", calculation, after='hidemax')
Ejemplo n.º 7
0
 def get_panic_analyses_list_message(self, ar):
     translate = self.context.translate
     analyses = ar.getAnalyses(full_objects=True, retracted=False)
     messages = list()
     for analysis in analyses:
         if not is_out_of_range(analysis)[1]:
             continue
         messages.append("- {0}, {1}: {2} {3}".format(
             analysis.Title(), translate(_("Result")),
             analysis.getFormattedResult(), analysis.getUnit()).strip())
     return "\n".join(messages)
 def get_body_message(self, ar):
     laboratory = self.context.bika_setup.laboratory
     lab_address = "\n".join(laboratory.getPrintAddress())
     return self.context.translate(
         _("Some results from the Analysis Request ${ar} "
           "exceeded the panic levels that may indicate an "
           "imminent life-threatening condition."
           "\n\n${lab_address}",
           mapping={'ar': ar.getId(),
                    'arlist': self.get_panic_analyses_list_message(ar),
                    'lab_address': lab_address})
     )
Ejemplo n.º 9
0
    def __init__(self, context, request, fieldvalue=[], allow_edit=True):
        BaseView.__init__(self, context, request, fieldvalue=fieldvalue,
                          allow_edit=allow_edit)

        # Add "min panic" and "max panic" fields.
        min_panic = {"title": _("Min panic"), "sortable": False}
        self.add_column('minpanic', min_panic, before='warn_min')

        max_panic = {"title": _("Max panic"), "sortable": False}
        self.add_column('maxpanic', max_panic, after='warn_max')

        # Add Grade ranges fields
        for grade in GRADES_KEYS:
            grade_label = grade.replace("_", " ")
            grade_item = {"title": _(grade_label), "sortable": False}
            self.add_column(grade, grade_item, before="hidemax")

        # Add spec calculation column
        calculation = {"title": _("Specification Calculation"),
                       "sortable": False, "type": "choices"}
        self.add_column("calculation", calculation, after='hidemax')
Ejemplo n.º 10
0
def setup_laboratory(portal):
    """Setup Laboratory
    """
    logger.info("Setting up Laboratory ...")
    lab = portal.bika_setup.laboratory
    lab.edit(title=_('BHP'))
    lab.reindexObject()

    # Set autoprinting of stickers on register
    portal.bika_setup.setAutoPrintStickers('register')

    # Unselect ShowPartitions from setup to hide partitions to Clients
    # https://github.com/senaite/senaite.core/pull/1392
    portal.bika_setup.setShowPartitions(False)

    logger.info("Setting up Laboratory [DONE]")
Ejemplo n.º 11
0
    def __call__(self):
        form = self.request.form
        form_submitted = form.get("submitted", False)
        form_send = form.get("send", False)
        form_cancel = form.get("cancel", False)
        objs = self.get_objects()

        if not objs:
            return self.redirect(message=_("No items selected"))

        # Handle form submit
        if form_submitted and form_send:
            logger.info("*** SEND TO POINT OF TESTING ***")
            lab_contact_uid = form.get("lab_contact")
            lab_contact = self.get_object_by_uid(lab_contact_uid)
            lab_department_uid = form.get("lab_department")
            lab_department = self.get_object_by_uid(lab_department_uid)

            if lab_contact is None:
                return self.redirect(message=_("No lab contact selected"))

            if lab_department is None:
                return self.redirect(message=_("No department selected"))

            sent_objects = []
            sent_object_ids = []
            for obj in objs:
                logger.info("*** SENDING {} TO POINT OF TESTING ***".format(obj.getId()))
                if self.send_to_pot(obj, lab_contact):
                    sent_objects.append(obj)

            sent_object_ids = map(lambda obj: obj.getId(), sent_objects)
            message = None
            if sent_object_ids:
                message = _("Sent {} to point of testing".format(
                    ", ".join(sent_object_ids)))

                # TODO Redirect the user to back_url while downloading pdf

                # Generate the delivery report
                pdf = generate_internal_delivery_pdf(self.context, sent_objects, lab_department)
                return self.response_pdf(pdf)

            else:
                message = _("All items have been already sent to point of testing".format(
                    ", ".join(sent_object_ids)))
            return self.redirect(message=message, level="info")

        # Handle form cancel
        if form_submitted and form_cancel:
            logger.info("*** CANCEL ***")
            return self.redirect(message=_("Delivery canceled"))

        # render the template
        return self.template()
Ejemplo n.º 12
0
    def __call__(self):
        plone.protect.CheckAuthenticator(self.request)
        ar = api.get_object_by_uid(self.request.get('uid', None), None) or \
             self.context
        if not ar or not IAnalysisRequest.providedBy(ar):
            return self.template()

        # Set the default recipients for the email
        self.recipients = self.get_recipients(ar)
        # Set the subject
        self.subject = self.context.translate(
            _("Some results from ${ar} exceeded panic range",
              mapping={"ar": ar.getId()}))
        # Set the body of the message
        self.body = self.get_body_message(ar)

        return self.template()
Ejemplo n.º 13
0
    def add_columns(self):
        """Adds bhp-specific columns in the listing
        """
        bhp_columns = {
            # Referral Laboratory
            "ReferralLab": {
                "title": _("Referral Lab"),
                "index": "getReferralLabUID",
                "sortable": False,
                "toggle": True,
                "ajax": True,
                "after": "retested"
            },
        }

        # Add the columns
        rv_keys = map(lambda r: r["id"], self.listing.review_states)
        for column_id, column_values in bhp_columns.items():
            utils.add_column(listing=self.listing,
                             column_id=column_id,
                             column_values=column_values,
                             after=column_values["after"],
                             review_states=rv_keys)
Ejemplo n.º 14
0
    def validate_service(self, request, uid):
        """Validates the specs values from request for the service uid. Returns
        a non-translated message if the validation failed.
        """
        logger.info("Validating......")
        message = BaseValidator.validate_service(self, request, uid)
        if message:
            # Somehow, failed a validation for one or more of the default
            # range fields (min, max, error, warn_min, warn_max)
            return message

        spec_min = get_record_value(request, uid, "min")
        spec_max = get_record_value(request, uid, "max")
        min_panic = get_record_value(request, uid, "minpanic")
        max_panic = get_record_value(request, uid, "maxpanic")

        # minpanic must be below min and below maxpanic
        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 _b("'{}' value must be numeric or empty").format(
                    _("Min panic"))
            if api.to_float(min_panic) > api.to_float(spec_min):
                return _b("'{}' value must be below '{}").format(
                    _("Min panic"), _("Min"))

        if max_panic:
            if not api.is_floatable(min_panic):
                return _b("'{}' value must be numeric or empty").format(
                    _("Max panic"))
            if api.to_float(min_panic) > api.to_float(spec_max):
                return _b("'{}' value must be above '{}").format(
                    _("Max panic"), _("Max"))

        return None
Ejemplo n.º 15
0
def init_listing(listing):
    primary = {"title": _("Primary Sample"), "sortable": True, "toggle": True}
    add_column(listing, 'primary', primary, before='getSample')
    ShipmentListingDecorator().render(listing)
Ejemplo n.º 16
0
    def __call__(self):
        form = self.request.form
        form_submitted = form.get("submitted", False)
        form_print = form.get("print", False)
        form_cancel = form.get("cancel", False)
        objs = self.get_objects()

        # No ARs selected
        if not objs:
            return self.redirect(message=_("No items selected"),
                                 level="warning")

        # Handle form submit
        if form_submitted and form_print:
            logger.info("*** PRINT ***")
            printer_uid = form.get("barcode_printer", "")
            printer = self.get_object_by_uid(printer_uid)

            # No printer selected/available
            if not printer:
                return self.redirect(message=_("No printer selected"),
                                     level="warning")

            # Proceed with selected printer
            printer_name = printer.Title()
            filepath = printer.getPrinterPath()

            # remember succeeded and failed printings
            obj_succeed = []
            obj_failed = []

            for obj in objs:
                logger.info("*** SENDING {} TO THE PRINTER ***"
                            .format(obj.getId()))
                # format the barcode template
                barcode = self.format_template_for(obj, printer.getTemplate())
                # format the filename template
                filename = self.format_template_for(obj, printer.getFileName())
                self.write(barcode, filename, filepath)
                success = self.send_to_printer(
                    printer_name, filename, filepath)
                if success:
                    obj_succeed.append(obj)
                else:
                    obj_failed.append(obj)

            # process a message for successful printings
            if obj_succeed:
                message = _("Barcodes sent to printer {} for {}"
                            .format(printer_name,
                                    "; ".join(map(api.get_title, objs))))
                self.add_status_message(message, level="info")

            # process a message for failed printings
            if obj_failed:
                message = _("Barcodes failed for printer {} for {}"
                            .format(printer_name,
                                    "; ".join(map(api.get_title, objs))))
                self.add_status_message(message, level="error")

            return self.redirect()

        # Handle form cancel
        if form_submitted and form_cancel:
            logger.info("*** CANCEL ***")
            self.redirect(message=_("Barcode print cancelled"), level="info")

        # render the template
        return self.template()
Ejemplo n.º 17
0
 def __call__(self, value, *args, **kwargs):
     if not os.path.exists(value):
         return _("Path not found")
     return True
Ejemplo n.º 18
0
    def __init__(self, context, request):
        super(ReferralLabsListing, self).__init__(context, request)

        self.catalog = "bika_setup_catalog"
        self.contentFilter = {
            "portal_type": "ReferralLab",
            "sort_on": "sortable_title",
            "sort_order": "ascending",
        }

        self.context_actions = {
            _("Add"): {
                "url": "createObject?type_name=ReferralLab",
                "permission": "Add portal content",
                "icon": "++resource++bika.lims.images/add.png"
            }
        }

        self.title = self.context.translate(_("Referral Laboratories"))
        self.icon = "{}/{}".format(
            self.portal_url,
            "++resource++bhp.lims.static/images/referrallabs_big.png")
        self.show_sort_column = False
        self.show_select_row = False
        self.show_select_column = True
        self.pagesize = 25

        self.columns = collections.OrderedDict((
            ("Title", {
                "title": _("Name"),
                "index": "sortable_title"
            }),
            ("Description", {
                "title": _("Description"),
                "index": "Description",
                "toggle": True
            }),
        ))

        self.review_states = [{
            "id": "default",
            "title": _("Active"),
            "contentFilter": {
                "is_active": True
            },
            "transitions": [],
            "columns": self.columns.keys(),
        }, {
            "id": "inactive",
            "title": _("Dormant"),
            "contentFilter": {
                "is_active": False
            },
            "transitions": [],
            "columns": self.columns.keys(),
        }, {
            "id": "all",
            "title": _("All"),
            "contentFilter": {},
            "columns": self.columns.keys(),
        }]
Ejemplo n.º 19
0
from bhp.lims.config import PRODUCT_NAME
from bhp.lims.interfaces import IBarcodePrinter
from bika.lims.content.bikaschema import BikaSchema
from Products.Archetypes import atapi
from Products.Archetypes.public import BaseContent
from zope.interface import implements
from bhp.lims import bhpMessageFactory as _

schema = BikaSchema.copy() + atapi.Schema((
    atapi.StringField(
        "FileName",
        default="SENAITE-${id}.lbl",
        required=True,
        # validators=("os_path_exists",),
        widget=atapi.StringWidget(
            label=_("The name of the generated label file. "
                    "You can also reference SuperModel attributes."),
            description=_(""),
        ),
    ),
    atapi.StringField(
        "PrinterPath",
        required=True,
        validators=("os_path_exists", ),
        widget=atapi.StringWidget(
            size=60,
            label=_("Printer Spool Path"),
            description=_(""),
        ),
    ),
    atapi.TextField("Template",
                    required=True,
Ejemplo n.º 20
0
    def add_review_states(self):
        """Adds bhp-specific review states (filter buttons) in the listing
        """
        # Get the columns and custom actions set by default for sample_due
        sample_due = filter(lambda o: o["id"] == "sample_due",
                            self.listing.review_states)[0]
        default_columns = sample_due["columns"]
        default_actions = sample_due.get("custom_transitions", [])

        bhp_review_states = [
            # Sample ordered
            {
                "id": "sample_ordered",
                "title": _("Ordered"),
                "contentFilter": {
                    "review_state": ("sample_ordered", ),
                    "sort_on": "created",
                    "sort_order": "descending"
                },
                "transitions": [],
                "custom_transitions": default_actions,
                "columns": default_columns,
            },

            # Sample shipped
            {
                "id": "sample_shipped",
                "title": _("Shipped"),
                "contentFilter": {
                    "review_state": ("sample_shipped", ),
                    "sort_on": "created",
                    "sort_order": "descending"
                },
                "transitions": [],
                "custom_transitions": default_actions,
                "columns": default_columns,
            },

            # Sample at reception
            {
                "id": "sample_at_reception",
                "title": _("At reception"),
                "contentFilter": {
                    "review_state": ("sample_at_reception", ),
                    "sort_on": "created",
                    "sort_order": "descending"
                },
                "transitions": [],
                "custom_transitions": default_actions,
                "columns": default_columns,
            }
        ]

        # Add the review states
        for bhp_review_state in bhp_review_states:
            utils.add_review_state(listing=self.listing,
                                   review_state=bhp_review_state,
                                   before="sample_due")

        # Add bhp custom states to "active" filter
        bhp_state_ids = map(lambda r: r["id"], bhp_review_states)
        for rv_filter in self.listing.review_states:
            if rv_filter["id"] == "default":
                review_states = rv_filter["contentFilter"]["review_state"]
                review_states = list(review_states) + bhp_state_ids
                review_states = tuple(set(review_states))
                rv_filter["contentFilter"]["review_state"] = review_states
                break
Ejemplo n.º 21
0
    def __call__(self):
        form = self.request.form

        # Form submit toggle
        form_submitted = form.get("submitted", False)

        # Buttons
        form_preview = form.get("button_preview", False)
        form_create = form.get("button_create", False)
        form_cancel = form.get("button_cancel", False)

        objs = self.get_objects()

        # No ARs selected
        if not objs:
            return self.redirect(message=_("No items selected"),
                                 level="warning")

        # Handle preview
        if form_submitted and form_preview:
            logger.info("*** PREVIEW ***")

        # Handle create
        if form_submitted and form_create:
            logger.info("*** CREATE PARTITIONS ***")

            partitions = []

            # create the partitions
            for partition in form.get("partitions", []):
                primary_uid = partition.get("primary_uid")
                sampletype_uid = partition.get("sampletype_uid")
                analyses_uids = partition.get("analyses")
                if not analyses_uids or not primary_uid:
                    # Cannot create a partition w/o analyses!
                    continue

                partition = self.create_partition(
                    primary_uid, sampletype_uid, analyses_uids)
                partitions.append(partition)
                logger.info("Successfully created partition: {}".format(
                    api.get_path(partition)))

                # Force the reception of the partition
                force_receive(partition)

            if not partitions:
                # If no partitions were created, show a warning message
                return self.redirect(message=_("No partitions were created"))

            message = _("Created {} partitions: {}".format(
                len(partitions), ", ".join(map(api.get_title, partitions))))

            # Redirect to the printing view
            uids = ','.join(map(lambda part: api.get_uid(part), partitions))
            url = "{}{}".format(self.context.absolute_url(),
                                '/print_view?uids='+uids)
            return self.request.response.redirect(url)

        # Handle cancel
        if form_submitted and form_cancel:
            logger.info("*** CANCEL ***")
            return self.redirect(message=_("Partitioning canceled"))

        return self.template()