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)
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
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
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()
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')
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')
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}) )
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')
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]")
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()
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()
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)
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
def init_listing(listing): primary = {"title": _("Primary Sample"), "sortable": True, "toggle": True} add_column(listing, 'primary', primary, before='getSample') ShipmentListingDecorator().render(listing)
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()
def __call__(self, value, *args, **kwargs): if not os.path.exists(value): return _("Path not found") return True
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(), }]
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,
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
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()