def workflow_action_retract_ar(self): # AR should be retracted # Can't transition inactive ARs if not api.is_active(self.context): message = _('Item is inactive.') self.context.plone_utils.addPortalMessage(message, 'info') self.request.response.redirect(self.context.absolute_url()) return # 1. Copies the AR linking the original one and viceversa ar = self.context newar = self.cloneAR(ar) # 2. The old AR gets a status of 'invalid' api.do_transition_for(ar, 'retract_ar') # 3. The new AR copy opens in status 'to be verified' changeWorkflowState(newar, 'bika_ar_workflow', 'to_be_verified') # 4. The system immediately alerts the client contacts who ordered # the results, per email and SMS, that a possible mistake has been # picked up and is under investigation. # A much possible information is provided in the email, linking # to the AR online. bika_setup = api.get_bika_setup() if bika_setup.getNotifyOnARRetract(): self.notify_ar_retract(ar, newar) message = _('${items} invalidated.', mapping={'items': ar.getId()}) self.context.plone_utils.addPortalMessage(message, 'warning') self.request.response.redirect(newar.absolute_url())
def workflow_action_retract_ar(self): # AR should be retracted # Can't transition inactive ARs if not api.is_active(self.context): message = _('Item is inactive.') self.context.plone_utils.addPortalMessage(message, 'info') self.request.response.redirect(self.context.absolute_url()) return # 1. Copies the AR linking the original one and viceversa ar = self.context newar = self.cloneAR(ar) # 2. The old AR gets a status of 'invalid' api.do_transition_for(ar, 'retract_ar') # 3. The new AR copy opens in status 'to be verified' changeWorkflowState(newar, 'bika_ar_workflow', 'to_be_verified') # 4. The system immediately alerts the client contacts who ordered # the results, per email and SMS, that a possible mistake has been # picked up and is under investigation. # A much possible information is provided in the email, linking # to the AR online. bika_setup = api.get_bika_setup() if bika_setup.getNotifyOnARRetract(): self.notify_ar_retract(ar, newar) message = _('${items} invalidated.', mapping={'items': ar.getRequestID()}) self.context.plone_utils.addPortalMessage(message, 'warning') self.request.response.redirect(newar.absolute_url())
def idserver_generate_id(context, prefix, batch_size=None): """ Generate a new id using external ID server. """ plone = context.portal_url.getPortalObject() url = api.get_bika_setup().getIDServerURL() try: if batch_size: # GET f = urllib.urlopen('%s/%s/%s?%s' % (url, plone.getId(), prefix, urllib.urlencode({'batch_size': batch_size}))) else: f = urllib.urlopen('%s/%s/%s' % (url, plone.getId(), prefix)) new_id = f.read() f.close() except: from sys import exc_info info = exc_info() zLOG.LOG( 'INFO', 0, '', 'generate_id raised exception: %s, %s \n ID server URL: %s' % (info[0], info[1], url)) raise IDServerUnavailable(_('ID Server unavailable')) return new_id
def idserver_generate_id(context, prefix, batch_size=None): """ Generate a new id using external ID server. """ plone = context.portal_url.getPortalObject() url = api.get_bika_setup().getIDServerURL() try: if batch_size: # GET f = urllib.urlopen('%s/%s/%s?%s' % ( url, plone.getId(), prefix, urllib.urlencode({'batch_size': batch_size})) ) else: f = urllib.urlopen('%s/%s/%s' % (url, plone.getId(), prefix)) new_id = f.read() f.close() except: from sys import exc_info info = exc_info() msg = 'generate_id raised exception: {}, {} \n ID server URL: {}' msg = msg.format(info[0], info[1], url) zLOG.LOG('INFO', 0, '', msg) raise IDServerUnavailable(_('ID Server unavailable')) return new_id
def workflow_action_invalidate(self): # AR should be retracted # Can't transition inactive ARs if not api.is_active(self.context): message = _('Item is inactive.') self.context.plone_utils.addPortalMessage(message, 'info') self.request.response.redirect(self.context.absolute_url()) return # Retract the AR and get the retest api.do_transition_for(self.context, 'invalidate') retest = self.context.getRetest() # 4. The system immediately alerts the client contacts who ordered # the results, per email and SMS, that a possible mistake has been # picked up and is under investigation. # A much possible information is provided in the email, linking # to the AR online. bika_setup = api.get_bika_setup() if bika_setup.getNotifyOnARRetract(): self.notify_ar_retract(self.context, retest) message = _('${items} invalidated.', mapping={'items': self.context.getId()}) self.context.plone_utils.addPortalMessage(message, 'warning') self.request.response.redirect(retest.absolute_url())
def __init__(self, context, request): BaseView.__init__(self, context, request) self.context = context self.request = request self.analyst = None self.instrument = None self.contentFilter = { 'getWorksheetUID': context.UID(), 'sort_on': 'sortable_title' } self.sort_on = 'sortable_title' self.icon = self.portal_url + "/++resource++bika.lims.images/worksheet_big.png" self.allow_edit = True self.show_categories = False self.expand_all_categories = False self.columns = { 'Pos': {'title': _('Position')}, 'DueDate': {'title': _('Due Date')}, 'Service': {'title': _('Analysis')}, 'Method': {'title': _('Method')}, 'DetectionLimit': { 'title': _('DL'), 'sortable': False, 'toggle': False}, 'Result': {'title': _('Result'), 'input_width': '6', 'input_class': 'ajax_calculate numeric', 'sortable': False}, 'Uncertainty': {'title': _('+-')}, 'retested': {'title': "<img src='++resource++bika.lims.images/retested.png' title='%s'/>" % _('Retested'), 'type':'boolean'}, 'Attachments': {'title': _('Attachments')}, 'Instrument': {'title': _('Instrument')}, 'state_title': {'title': _('State')}, } self.review_states = [ {'id':'default', 'title': _('All'), 'contentFilter':{}, 'transitions': [{'id':'submit'}, {'id':'verify'}, {'id':'retract'}, {'id':'unassign'}], 'columns':['Pos', 'Service', 'Method', 'Instrument', 'DetectionLimit', 'Result', 'Uncertainty', 'DueDate', 'state_title', 'Attachments'] }, ] self.bika_setup = api.get_bika_setup() self.uids_strpositions = self.get_uids_strpositions() self.items_rowspans = dict()
def rename_bika_setup(): """ Rename Bika Setup to just Setup to avoid naming confusions for new users """ logger.info("Renaming Bika Setup...") bika_setup = api.get_bika_setup() bika_setup.setTitle("Setup") bika_setup.reindexObject() setup = api.get_portal().portal_setup setup.runImportStepFromProfile('profile-bika.lims:default', 'controlpanel')
def setup_control_panels(portal): """Setup Plone control and Senaite management panels """ logger.info("*** Setup Control Panels ***") # get the bika_setup object bika_setup = api.get_bika_setup() cp = api.get_tool("portal_controlpanel") def get_action_index(action_id): if action_id == "*": action = cp.listActions()[-1] action_id = action.getId() for n, action in enumerate(cp.listActions()): if action.getId() == action_id: return n return -1 for item in CONTROL_PANELS: id = item.get("id") type = item.get("type") title = item.get("title") description = item.get("description") panel = bika_setup.get(id, None) if panel is None: logger.info("Creating Setup Folder '{}' in Setup.".format(id)) # allow content creation in setup temporary portal_types = api.get_tool("portal_types") fti = portal_types.getTypeInfo(bika_setup) fti.filter_content_types = False myfti = portal_types.getTypeInfo(type) global_allow = myfti.global_allow myfti.global_allow = True folder_id = bika_setup.invokeFactory(type, id, title=title) panel = bika_setup[folder_id] myfti.global_allow = global_allow fti.filter_content_types = True else: # set some meta data panel.setTitle(title) panel.setDescription(description) # Move configlet action to the right index action_index = get_action_index(id) ref_index = get_action_index(item["insert-after"]) if (action_index != -1) and (ref_index != -1): actions = cp._cloneActions() action = actions.pop(action_index) actions.insert(ref_index + 1, action) cp._actions = tuple(actions) cp._p_changed = 1 # reindex the object to render it properly in the navigation portlet panel.reindexObject()
def get_email_body(self, sample): """Returns the email body text """ retest = sample.getRetest() lab_address = api.get_bika_setup().laboratory.getPrintAddress() setup = api.get_setup() body = Template(setup.getEmailBodySampleInvalidation())\ .safe_substitute( dict(sample_link=self.get_html_link(sample), retest_link=self.get_html_link(retest), sample_id=api.get_id(sample), retest_id=api.get_id(retest), lab_address="<br/>".join(lab_address))) return body
def setUp(self): super(test_FormattedResult, self).setUp() portal = get_portal() bika_setup = get_bika_setup() login(self.portal, TEST_USER_NAME) self.client = self.addthing( portal.clients, 'Client', title='Happy Hills', ClientID='HH') self.contact = self.addthing( self.client, 'Contact', Firstname='Rita', Lastname='Mohale') self.sampletype = self.addthing( bika_setup.bika_sampletypes, 'SampleType', Prefix='H2O') self.service = self.addthing( bika_setup.bika_analysisservices, 'AnalysisService', title='Calcium', Keyword='Ca') transaction.commit()
def purge_review_states(self): """Purges unnecessary review statuses """ remove_filters = [] setup = api.get_bika_setup() if not setup.getSamplingWorkflowEnabled(): remove_filters.append("to_be_sampled") if not setup.getScheduleSamplingEnabled(): remove_filters.append("scheduled_sampling") if not setup.getSamplePreservationEnabled(): remove_filters.append("to_be_preserved") if not setup.getRejectionReasons(): remove_filters.append("rejected") self.review_states = filter(lambda r: r.get("id") not in remove_filters, self.review_states)
def __call__(self, value, *args, **kwargs): # avoid the catalog query if the option is not selected if not api.get_bika_setup().ClientPatientIDUnique: return True query = dict(getClientPatientID=value) patients = api.search(query, CATALOG_PATIENTS) instance = kwargs.get('instance') # If there are no patients with this Client Patient ID # then it is valid if not patients: return True # If there is only one patient with this Client Patient ID # and it is the patient being edited then it also valid if len(patients) == 1 and api.get_uid( patients[0]) == api.get_uid(instance): return True trans = getToolByName(instance, 'translation_service').translate msg = _("Validation failed: '${value}' is not unique", mapping={'value': safe_unicode(value)}) return to_utf8(trans(msg))
def get_config(context, **kw): """Fetch the config dict from the Bika Setup for the given portal_type """ # get the ID formatting config config_map = api.get_bika_setup().getIDFormatting() # allow portal_type override portal_type = get_type_id(context, **kw) # check if we have a config for the given portal_type for config in config_map: if config['portal_type'].lower() == portal_type.lower(): return config # return a default config default_config = { 'form': '%s-{seq}' % portal_type.lower(), 'sequence_type': 'generated', 'prefix': '%s' % portal_type.lower(), } return default_config
def get_config(context, **kw): """Fetch the config dict from the Bika Setup for the given portal_type """ # get the ID formatting config config_map = api.get_bika_setup().getIDFormatting() # allow portal_type override portal_type = kw.get("portal_type") or api.get_portal_type(context) # check if we have a config for the given portal_type for config in config_map: if config['portal_type'].lower() == portal_type.lower(): return config # return a default config default_config = { 'form': '%s-{seq}' % portal_type.lower(), 'sequence_type': 'generated', 'prefix': '%s' % portal_type.lower(), } return default_config
def __call__(self, value, *args, **kwargs): # avoid the catalog query if the option is not selected if not api.get_bika_setup().ClientPatientIDUnique: return True query = dict(getClientPatientID=value) patients = api.search(query, CATALOG_PATIENTS) instance = kwargs.get('instance') # If there are no patients with this Client Patient ID # then it is valid if not patients: return True # If there is only one patient with this Client Patient ID # and it is the patient being edited then it also valid if len(patients) == 1 and api.get_uid(patients[0]) == api.get_uid(instance): return True trans = getToolByName(instance, 'translation_service').translate msg = _( "Validation failed: '${value}' is not unique", mapping={ 'value': safe_unicode(value) }) return to_utf8(trans(msg))
def rename_bika_setup(): logger.info("Renaming Bika Setup...") bika_setup = api.get_bika_setup() bika_setup.setTitle("Setup") bika_setup.reindexObject()
def generateUniqueId(context, parent=False): """ Generate pretty content IDs. """ def getLastCounter(context, config): if config.get('counter_type', '') == 'backreference': return len(context.getBackReferences( config['counter_reference'])) - 1 elif config.get('counter_type', '') == 'contained': return len(context.objectItems(config['counter_reference'])) - 1 else: raise RuntimeError('ID Server: missing values in configuration') number_generator = getUtility(INumberGenerator) # keys = number_generator.keys() # values = number_generator.values() # for i in range(len(keys)): # print '%s : %s' % (keys[i], values[i]) def getConfigByPortalType(config_map, portal_type): config = {} for c in config_map: if c['portal_type'] == portal_type: config = c break return config config_map = api.get_bika_setup().getIDFormatting() config = getConfigByPortalType(config_map=config_map, portal_type=context.portal_type) if context.portal_type == "AnalysisRequest": variables_map = { 'sampleId': context.getSample().getId(), 'sample': context.getSample(), } elif context.portal_type == "SamplePartition": variables_map = { 'sampleId': context.aq_parent.getId(), 'sample': context.aq_parent, } elif context.portal_type == "Sample" and parent: config = getConfigByPortalType( config_map=config_map, portal_type='SamplePartition') # Override variables_map = { 'sampleId': context.getId(), 'sample': context, } elif context.portal_type == "Sample": sampleDate = None if context.getSamplingDate(): sampleDate = DT2dt(context.getSamplingDate()) variables_map = { 'clientId': context.aq_parent.getClientID(), 'sampleDate': sampleDate, 'sampleType': context.getSampleType().getPrefix(), 'year': DateTime().strftime("%Y")[2:], } else: if not config: # Provide default if no format specified on bika_setup config = { 'form': '%s-{seq}' % context.portal_type.lower(), 'sequence_type': 'generated', 'prefix': '%s' % context.portal_type.lower(), } variables_map = {} # Actual id construction starts here form = config['form'] if config['sequence_type'] == 'counter': new_seq = getLastCounter(context=variables_map[config['context']], config=config) elif config['sequence_type'] == 'generated': if config.get('split_length', None) == 0: prefix_config = '-'.join(form.split('-')[:-1]) prefix = prefix_config.format(**variables_map) elif config.get('split_length', None) > 0: prefix_config = '-'.join(form.split('-')[:config['split_length']]) prefix = prefix_config.format(**variables_map) else: prefix = config['prefix'] new_seq = number_generator(key=prefix) variables_map['seq'] = new_seq + 1 result = form.format(**variables_map) return result
def rename_bika_setup(): logger.info("Renaming Bika Setup...") bika_setup = api.get_bika_setup() bika_setup.setTitle("Setup") bika_setup.reindexObject()
def generateUniqueId(context, parent=False): """ Generate pretty content IDs. """ def getLastCounter(context, config): if config.get('counter_type', '') == 'backreference': return len(context.getBackReferences(config['counter_reference'])) - 1 elif config.get('counter_type', '') == 'contained': return len(context.objectItems(config['counter_reference'])) - 1 else: raise RuntimeError('ID Server: missing values in configuration') number_generator = getUtility(INumberGenerator) # keys = number_generator.keys() # values = number_generator.values() # for i in range(len(keys)): # print '%s : %s' % (keys[i], values[i]) def getConfigByPortalType(config_map, portal_type): config = {} for c in config_map: if c['portal_type'] == portal_type: config = c break return config config_map = api.get_bika_setup().getIDFormatting() config = getConfigByPortalType( config_map=config_map, portal_type=context.portal_type) if context.portal_type == "AnalysisRequest": variables_map = { 'sampleId': context.getSample().getId(), 'sample': context.getSample(), } elif context.portal_type == "SamplePartition": variables_map = { 'sampleId': context.aq_parent.getId(), 'sample': context.aq_parent, } elif context.portal_type == "Sample" and parent: config = getConfigByPortalType( config_map=config_map, portal_type='SamplePartition') # Override variables_map = { 'sampleId': context.getId(), 'sample': context, } elif context.portal_type == "Sample": sampleDate = None if context.getSamplingDate(): sampleDate = DT2dt(context.getSamplingDate()) variables_map = { 'clientId': context.aq_parent.getClientID(), 'sampleDate': sampleDate, 'sampleType': context.getSampleType().getPrefix(), 'year': DateTime().strftime("%Y")[2:], } else: if not config: # Provide default if no format specified on bika_setup config = { 'form': '%s-{seq}' % context.portal_type.lower(), 'sequence_type': 'generated', 'prefix': '%s' % context.portal_type.lower(), } variables_map = {} # Actual id construction starts here form = config['form'] if config['sequence_type'] == 'counter': new_seq = getLastCounter( context=variables_map[config['context']], config=config) elif config['sequence_type'] == 'generated': if config.get('split_length', None) == 0: prefix_config = '-'.join(form.split('-')[:-1]) prefix = prefix_config.format(**variables_map) elif config.get('split_length', None) > 0: prefix_config = '-'.join(form.split('-')[:config['split_length']]) prefix = prefix_config.format(**variables_map) else: prefix = config['prefix'] new_seq = number_generator(key=prefix) variables_map['seq'] = new_seq + 1 result = form.format(**variables_map) return result
def get_laboratory_formatted_email(self): """Returns the laboratory email formatted """ lab = api.get_bika_setup().laboratory return self.get_formatted_email((lab.getName(), lab.getEmailAddress()))
def global_ar_attachments_allowed(self): """Checks Bika Setup if AR Attachments are allowed """ bika_setup = api.get_bika_setup() return bika_setup.getARAttachmentsPermitted()
def getSignificantFigures(self): sfigs = self.getField('SignificantFigures').get(self) if not sfigs: bika_setup = get_bika_setup() return bika_setup.getField('SignificantFigures').get(bika_setup)
def get_sigfig_precision(analysis): sig_figures = analysis.getService().getSignificantFigures() if sig_figures == 0: # indicates to use bika setup default: sig_figures = get_bika_setup().getSignificantFigures() return sig_figures
def __init__(self, context, request): super(AnalysesView, self).__init__(context, request) self.context = context self.request = request self.analyst = None self.instrument = None self.contentFilter = { "getWorksheetUID": api.get_uid(context), "sort_on": "sortable_title", } self.icon = "{}/{}".format( self.portal_url, "++resource++bika.lims.images/worksheet_big.png") self.allow_edit = True self.show_categories = False self.expand_all_categories = False self.show_search = False self.bika_setup = api.get_bika_setup() self.uids_strpositions = self.get_uids_strpositions() self.items_rowspans = dict() self.columns = collections.OrderedDict(( ("Pos", { "sortable": False, "title": _("Position") }), ("Service", { "sortable": False, "title": _("Analysis") }), ("Method", { "sortable": False, "ajax": True, "title": _("Method") }), ("Instrument", { "sortable": False, "ajax": True, "title": _("Instrument") }), ("DetectionLimitOperand", { "title": _("DL"), "sortable": False, "ajax": True, "autosave": True, "toggle": False }), ("Result", { "title": _("Result"), "ajax": True, "sortable": False }), ("retested", { "title": get_image("retested.png", title=t(_("Retested"))), "toggle": False, "type": "boolean" }), ("Specification", { "title": _("Specification"), "sortable": False }), ("Uncertainty", { "sortable": False, "title": _("+-") }), ("DueDate", { "sortable": False, "title": _("Due Date") }), ("state_title", { "sortable": False, "title": _("State") }), ("Attachments", { "sortable": False, "title": _("Attachments") }), )) # Inject Remarks column for listing if self.is_analysis_remarks_enabled(): self.columns["Remarks"] = { "title": "Remarks", "ajax": True, "toggle": False, "sortable": False, "type": "remarks" } self.review_states = [ { "id": "default", "title": _("All"), "contentFilter": {}, "columns": self.columns.keys(), }, ]
def global_analysis_attachments_allowed(self): """Checks Bika Setup if Attachments for Analyses are allowed """ bika_setup = api.get_bika_setup() return bika_setup.getAnalysisAttachmentsPermitted()
def update(self): """Called before the listing renders """ super(AnalysisRequestsView, self).update() self.workflow = api.get_tool("portal_workflow") self.member = self.mtool.getAuthenticatedMember() self.roles = self.member.getRoles() setup = api.get_bika_setup() # remove `to_be_sampled` filter if not setup.getSamplingWorkflowEnabled(): self.review_states = filter( lambda x: x.get("id") != "to_be_sampled", self.review_states) # remove `scheduled_sampling` filter if not setup.getScheduleSamplingEnabled(): self.review_states = filter( lambda x: x.get("id") != "scheduled_sampling", self.review_states) # remove `to_be_preserved` filter if not setup.getSamplePreservationEnabled(): self.review_states = filter( lambda x: x.get("id") != "to_be_preserved", self.review_states) # remove `rejected` filter if not setup.getRejectionReasons(): self.review_states = filter(lambda x: x.get("id") != "rejected", self.review_states) self.hideclientlink = "RegulatoryInspector" in self.roles \ and "Manager" not in self.roles \ and "LabManager" not in self.roles \ and "LabClerk" not in self.roles if self.context.portal_type == "AnalysisRequestsFolder" and \ (self.mtool.checkPermission(AddAnalysisRequest, self.context)): self.context_actions[_("Add")] = \ {"url": "ar_add?ar_count=1", 'permission': 'Add portal content', "icon": "++resource++bika.lims.images/add.png"} self.editresults = -1 self.clients = {} # self.user_is_preserver = "Preserver" in self.roles # Printing workflow enabled? # If not, remove the Column self.printwfenabled = \ self.context.bika_setup.getPrintingWorkflowEnabled() printed_colname = "Printed" if not self.printwfenabled and printed_colname in self.columns: # Remove "Printed" columns del self.columns[printed_colname] tmprvs = [] for rs in self.review_states: tmprs = rs tmprs["columns"] = [ c for c in rs.get("columns", []) if c != printed_colname ] tmprvs.append(tmprs) self.review_states = tmprvs elif self.printwfenabled: # Print button to choose multiple ARs and print them. review_states = [] for review_state in self.review_states: review_state.get("custom_transitions", []).extend([ { "id": "print_sample", "title": _("Print"), "url": "workflow_action?action=print_sample" }, ]) review_states.append(review_state) self.review_states = review_states # Only "senaite.core: ManageAnalysisRequests" may see the copy to new button. # elsewhere it is hacked in where required. if self.copy_to_new_allowed: review_states = [] for review_state in self.review_states: review_state.get("custom_transitions", []).extend([ { "id": "copy_to_new", "title": _("Copy to new"), "url": "workflow_action?action=copy_to_new" }, ]) review_states.append(review_state) self.review_states = review_states
def test_LIMS_2221_DecimalMarkWithSciNotation(self): # Notations # '1' => aE+b / aE-b # '2' => ax10^b / ax10^-b # '3' => ax10^b / ax10^-b (with superscript) # '4' => a·10^b / a·10^-b # '5' => a·10^b / a·10^-b (with superscript) matrix = [ # as_prec as_exp not mark result formatted result # ------- ------ --- ---- ------ ---------------- [0, 0, 1, ',', '0', '0'], [0, 0, 2, ',', '0', '0'], [0, 0, 3, ',', '0', '0'], [0, 0, 4, ',', '0', '0'], [0, 0, 5, ',', '0', '0'], [2, 5, 1, ',', '0.01', '0,01'], [2, 5, 2, ',', '0.01', '0,01'], [2, 5, 3, ',', '0.01', '0,01'], [2, 5, 4, ',', '0.01', '0,01'], [2, 5, 5, ',', '0.01', '0,01'], [2, 1, 1, ',', '0.123', '1,2e-01'], [2, 1, 2, ',', '0.123', '1,2x10^-1'], [2, 1, 3, ',', '0.123', '1,2x10<sup>-1</sup>'], [2, 1, 4, ',', '0.123', '1,2·10^-1'], [2, 1, 5, ',', '0.123', '1,2·10<sup>-1</sup>'], [2, 1, 1, ',', '1.234', '1,23'], [2, 1, 2, ',', '1.234', '1,23'], [2, 1, 3, ',', '1.234', '1,23'], [2, 1, 4, ',', '1.234', '1,23'], [2, 1, 5, ',', '1.234', '1,23'], [2, 1, 1, ',', '12.345', '1,235e01'], [2, 1, 2, ',', '12.345', '1,235x10^1'], [2, 1, 3, ',', '12.345', '1,235x10<sup>1</sup>'], [2, 1, 4, ',', '12.345', '1,235·10^1'], [2, 1, 5, ',', '12.345', '1,235·10<sup>1</sup>'], [4, 3, 1, ',', '-123.45678', '-123,4568'], [4, 3, 2, ',', '-123.45678', '-123,4568'], [4, 3, 3, ',', '-123.45678', '-123,4568'], [4, 3, 4, ',', '-123.45678', '-123,4568'], [4, 3, 5, ',', '-123.45678', '-123,4568'], [4, 3, 1, ',', '-1234.5678', '-1,2345678e03'], [4, 3, 2, ',', '-1234.5678', '-1,2345678x10^3'], [4, 3, 3, ',', '-1234.5678', '-1,2345678x10<sup>3</sup>'], [4, 3, 4, ',', '-1234.5678', '-1,2345678·10^3'], [4, 3, 5, ',', '-1234.5678', '-1,2345678·10<sup>3</sup>'], ] serv = self.service serv.setLowerDetectionLimit('-99999') # test results below 0 too prevm = [] an = None bs = get_bika_setup() for m in matrix: as_prec = m[0] as_exp = m[1] notation = m[2] _dm = m[3] _result = m[4] _expected = m[5] bs.setResultsDecimalMark(_dm) # Create the AR and set the values to the AS, but only if necessary if not an or prevm[0] != as_prec or prevm[1] != as_exp: serv.setPrecision(as_prec) serv.setExponentialFormatPrecision(as_exp) self.assertEqual(serv.getPrecision(), as_prec) self.assertEqual( serv.Schema().getField('Precision').get(serv), as_prec) self.assertEqual(serv.getExponentialFormatPrecision(), as_exp) self.assertEqual( serv.Schema().getField( 'ExponentialFormatPrecision').get(serv), as_exp) values = {'Client': self.client.UID(), 'Contact': self.client.getContacts()[0].UID(), 'SamplingDate': '2015-01-01', 'SampleType': self.sampletype.UID()} ar = create_analysisrequest(self.client, {}, values, [serv.UID()]) do_transition_for(ar, 'receive') an = ar.getAnalyses()[0].getObject() prevm = m an.setResult(_result) self.assertEqual(an.getResult(), _result) self.assertEqual(an.Schema().getField('Result').get(an), _result) decimalmark = bs.getResultsDecimalMark() fr = an.getFormattedResult(sciformat=notation, decimalmark=decimalmark) self.assertEqual(fr, _expected)
def notify_ar_retract(self, ar, newar): bika_setup = api.get_bika_setup() laboratory = bika_setup.laboratory lab_address = "<br/>".join(laboratory.getPrintAddress()) mime_msg = MIMEMultipart('related') mime_msg['Subject'] = t(_("Erroneus result publication from ${request_id}", mapping={"request_id": ar.getRequestID()})) mime_msg['From'] = formataddr( (encode_header(laboratory.getName()), laboratory.getEmailAddress())) to = [] contact = ar.getContact() if contact: to.append(formataddr((encode_header(contact.Title()), contact.getEmailAddress()))) for cc in ar.getCCContact(): formatted = formataddr((encode_header(cc.Title()), cc.getEmailAddress())) if formatted not in to: to.append(formatted) managers = self.context.portal_groups.getGroupMembers('LabManagers') for bcc in managers: user = self.portal.acl_users.getUser(bcc) if user: uemail = user.getProperty('email') ufull = user.getProperty('fullname') formatted = formataddr((encode_header(ufull), uemail)) if formatted not in to: to.append(formatted) mime_msg['To'] = ','.join(to) aranchor = "<a href='%s'>%s</a>" % (ar.absolute_url(), ar.getRequestID()) naranchor = "<a href='%s'>%s</a>" % (newar.absolute_url(), newar.getRequestID()) addremarks = ('addremarks' in self.request and ar.getRemarks()) and ("<br/><br/>" + _("Additional remarks:") + "<br/>" + ar.getRemarks().split("===")[1].strip() + "<br/><br/>") or '' sub_d = dict(request_link=aranchor, new_request_link=naranchor, remarks=addremarks, lab_address=lab_address) body = Template("Some errors have been detected in the results report " "published from the Analysis Request $request_link. The Analysis " "Request $new_request_link has been created automatically and the " "previous has been invalidated.<br/>The possible mistake " "has been picked up and is under investigation.<br/><br/>" "$remarks $lab_address").safe_substitute(sub_d) 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(self.context, 'MailHost') host.send(mime_msg.as_string(), immediate=True) except Exception as msg: message = _('Unable to send an email to alert lab ' 'client contacts that the Analysis Request has been ' 'retracted: ${error}', mapping={'error': safe_unicode(msg)}) self.context.plone_utils.addPortalMessage(message, 'warning')
def notify_ar_retract(self, ar, newar): bika_setup = api.get_bika_setup() laboratory = bika_setup.laboratory lab_address = "<br/>".join(laboratory.getPrintAddress()) mime_msg = MIMEMultipart('related') mime_msg['Subject'] = t( _("Erroneus result publication from ${request_id}", mapping={"request_id": ar.getId()})) mime_msg['From'] = formataddr((encode_header(laboratory.getName()), laboratory.getEmailAddress())) to = [] contact = ar.getContact() if contact: to.append( formataddr((encode_header(contact.Title()), contact.getEmailAddress()))) for cc in ar.getCCContact(): formatted = formataddr( (encode_header(cc.Title()), cc.getEmailAddress())) if formatted not in to: to.append(formatted) managers = self.context.portal_groups.getGroupMembers('LabManagers') for bcc in managers: user = self.portal.acl_users.getUser(bcc) if user: uemail = user.getProperty('email') ufull = user.getProperty('fullname') formatted = formataddr((encode_header(ufull), uemail)) if formatted not in to: to.append(formatted) mime_msg['To'] = ','.join(to) aranchor = "<a href='%s'>%s</a>" % (ar.absolute_url(), ar.getId()) naranchor = "<a href='%s'>%s</a>" % (newar.absolute_url(), newar.getId()) addremarks = ('addremarks' in self.request and ar.getRemarks()) and ( "<br/><br/>" + _("Additional remarks:") + "<br/>" + ar.getRemarks().split("===")[1].strip() + "<br/><br/>") or '' sub_d = dict(request_link=aranchor, new_request_link=naranchor, remarks=addremarks, lab_address=lab_address) body = Template( "Some errors have been detected in the results report " "published from the Analysis Request $request_link. The Analysis " "Request $new_request_link has been created automatically and the " "previous has been invalidated.<br/>The possible mistake " "has been picked up and is under investigation.<br/><br/>" "$remarks $lab_address").safe_substitute(sub_d) 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(self.context, 'MailHost') host.send(mime_msg.as_string(), immediate=True) except Exception as msg: message = _( 'Unable to send an email to alert lab ' 'client contacts that the Analysis Request has been ' 'retracted: ${error}', mapping={'error': safe_unicode(msg)}) self.context.plone_utils.addPortalMessage(message, 'warning')
def get_display_rounding_type(analysis): service = analysis.getService() rounding_type = service.getDisplayRounding() if not rounding_type or rounding_type == 'DEFAULT': rounding_type = get_bika_setup().getDisplayRounding() return rounding_type