Exemplo n.º 1
0
    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())
Exemplo n.º 2
0
    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())
Exemplo n.º 3
0
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
Exemplo n.º 4
0
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
Exemplo n.º 5
0
    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())
Exemplo n.º 6
0
 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()
Exemplo n.º 7
0
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')
Exemplo n.º 8
0
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()
Exemplo n.º 9
0
 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
Exemplo n.º 10
0
 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()
Exemplo n.º 11
0
    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)
Exemplo n.º 12
0
 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))
Exemplo n.º 13
0
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
Exemplo n.º 14
0
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
Exemplo n.º 15
0
 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))
Exemplo n.º 16
0
def rename_bika_setup():
    logger.info("Renaming Bika Setup...")
    bika_setup = api.get_bika_setup()
    bika_setup.setTitle("Setup")
    bika_setup.reindexObject()
Exemplo n.º 17
0
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
Exemplo n.º 18
0
def rename_bika_setup():
    logger.info("Renaming Bika Setup...")
    bika_setup = api.get_bika_setup()
    bika_setup.setTitle("Setup")
    bika_setup.reindexObject()
Exemplo n.º 19
0
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
Exemplo n.º 20
0
 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()))
Exemplo n.º 21
0
 def global_ar_attachments_allowed(self):
     """Checks Bika Setup if AR Attachments are allowed
     """
     bika_setup = api.get_bika_setup()
     return bika_setup.getARAttachmentsPermitted()
Exemplo n.º 22
0
 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)
Exemplo n.º 23
0
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
Exemplo n.º 24
0
    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(),
            },
        ]
Exemplo n.º 25
0
 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()
Exemplo n.º 26
0
    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
Exemplo n.º 27
0
    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)
Exemplo n.º 28
0
    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')
Exemplo n.º 29
0
    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')
Exemplo n.º 30
0
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