Example #1
0
    def __call__(self):
        form = self.request.form
        plone.protect.CheckAuthenticator(form)
        workflow = getToolByName(self.context, 'portal_workflow')
        if self.destination_url == "":
            self.destination_url = self.request.get_header("referer",
                                   self.context.absolute_url())
        skiplist = self.request.get('workflow_skiplist', [])
        action, came_from = self._get_form_workflow_action()

        # transition the context object.
        if came_from == "workflow_action":
            obj = self.context
            # the only actions allowed on inactive/cancelled
            # items are "reinstate" and "activate"
            if not isActive(obj) and action not in ('reinstate', 'activate'):
                message = self.context.translate(_('Item is inactive.'))
                self.context.plone_utils.addPortalMessage(message, 'info')
                self.request.response.redirect(self.destination_url)
                return
            else:
                if obj.UID() not in skiplist:
                    allowed_transitions = []
                    for t in workflow.getTransitionsFor(obj):
                        allowed_transitions.append(t['id'])
                    if action in allowed_transitions:
                        workflow.doActionFor(obj, action)
                self.request.response.redirect(self.destination_url)
                return

        # transition selected items from the bika_listing/Table.
        transitioned = []
        selected_items = self._get_selected_items()
        for uid, item in selected_items.items():
            # the only action allowed on inactive items is "activate"
            if not isActive(item) and action not in ('reinstate', 'activate'):
                continue
            if uid not in skiplist:
                allowed_transitions = []
                for t in workflow.getTransitionsFor(item):
                    allowed_transitions.append(t['id'])
                if action in allowed_transitions:
                    try:
                        workflow.doActionFor(item, action)
                        transitioned.append(item.Title())
                    except WorkflowException:
                        pass

        if len(transitioned) > 0:
            message = self.context.translate(PMF('Changes saved.'))
            self.context.plone_utils.addPortalMessage(message, 'info')

        # automatic label printing
        if action == 'receive' and 'receive' in self.context.bika_setup.getAutoPrintLabels():
            q = "/sticker?size=%s&items=" % (self.context.bika_setup.getAutoLabelSize())
            # selected_items is a list of UIDs (stickers for AR_add use IDs)
            q += ",".join([i.getId() for i in selected_items.values()])
            self.request.response.redirect(self.context.absolute_url() + q)
        else:
            self.request.response.redirect(self.destination_url)
Example #2
0
    def __call__(self):
        form = self.request.form
        plone.protect.CheckAuthenticator(form)
        workflow = getToolByName(self.context, 'portal_workflow')

        if self.destination_url == "":
            self.destination_url = self.request.get_header(
                "referer", self.context.absolute_url())
        action, came_from = self._get_form_workflow_action()

        # transition the context object (plone edit bar dropdown)
        if came_from == "workflow_action":
            obj = self.context
            # the only actions allowed on inactive/cancelled
            # items are "reinstate" and "activate"
            if not isActive(obj) and action not in ('reinstate', 'activate'):
                message = self.context.translate(_('Item is inactive.'))
                self.context.plone_utils.addPortalMessage(message, 'info')
                self.request.response.redirect(self.destination_url)
                return
            if not skip(obj, action, peek=True):
                allowed_transitions = []
                for t in workflow.getTransitionsFor(obj):
                    allowed_transitions.append(t['id'])
                if action in allowed_transitions:
                    workflow.doActionFor(obj, action)
            self.request.response.redirect(self.destination_url)
            return

        # transition selected items from the bika_listing/Table.
        transitioned = []
        selected_items = self._get_selected_items()
        for uid, item in selected_items.items():
            # the only actions allowed on inactive/cancelled
            # items are "reinstate" and "activate"
            if not isActive(item) and action not in ('reinstate', 'activate'):
                continue
            if not skip(item, action, peek=True):
                allowed_transitions = []
                for t in workflow.getTransitionsFor(item):
                    allowed_transitions.append(t['id'])
                if action in allowed_transitions:
                    doActionFor(item, action)
                    transitioned.append(item.Title())

        if len(transitioned) > 0:
            message = self.context.translate(PMF('Changes saved.'))
            self.context.plone_utils.addPortalMessage(message, 'info')

        # automatic label printing
        if action == 'receive' and 'receive' in self.portal.bika_setup.getAutoPrintLabels(
        ):
            q = "/sticker?size=%s&items=" % (
                self.portal.bika_setup.getAutoLabelSize())
            # selected_items is a list of UIDs (stickers for AR_add use IDs)
            q += ",".join([i.getId() for i in selected_items.values()])
            self.request.response.redirect(self.context.absolute_url() + q)
        else:
            self.request.response.redirect(self.destination_url)
Example #3
0
 def getWSTemplates(self, contentFilter={}):
     istate = contentFilter.get("inactive_state", None)
     if istate == 'active':
         templates = [p for p in self.context.bika_setup.objectValues("WSTemplate")
                     if isActive(p)]
     elif istate == 'inactive':
         templates = [p for p in self.context.objectValues("WorksheetTemplate")
                     if not isActive(p)]
     else:
         templates = [p for p in self.context.objectValues("WorksheetTemplate")]
     return templates
Example #4
0
 def getARProfiles(self, contentFilter={}):
     istate = contentFilter.get("inactive_state", None)
     if istate == 'active':
         profiles = [p for p in self.context.objectValues("ARProfile")
                     if isActive(p)]
     elif istate == 'inactive':
         profiles = [p for p in self.context.objectValues("ARProfile")
                     if not isActive(p)]
     else:
         profiles = [p for p in self.context.objectValues("ARProfile")]
     return profiles
Example #5
0
 def getARProfiles(self, contentFilter={}):
     istate = contentFilter.get("inactive_state", None)
     if istate == 'active':
         profiles = [p for p in self.context.objectValues("ARProfile")
                     if isActive(p)]
     elif istate == 'inactive':
         profiles = [p for p in self.context.objectValues("ARProfile")
                     if not isActive(p)]
     else:
         profiles = [p for p in self.context.objectValues("ARProfile")]
     profiles.sort(lambda a,b:cmp(a.Title().lower(), b.Title().lower()))
     return profiles
Example #6
0
 def getARTemplates(self, contentFilter={}):
     istate = contentFilter.get("inactive_state", None)
     if istate == 'active':
         templates = [
             p for p in self.context.objectValues("ARTemplate")
             if isActive(p)
         ]
     elif istate == 'inactive':
         templates = [
             p for p in self.context.objectValues("ARTemplate")
             if not isActive(p)
         ]
     else:
         templates = [p for p in self.context.objectValues("ARTemplate")]
     return templates
Example #7
0
    def __call__(self):
        self.context_actions = {}
        wf = getToolByName(self.context, 'portal_workflow')
        mtool = getToolByName(self.context, 'portal_membership')
        addPortalMessage = self.context.plone_utils.addPortalMessage
        translate = self.context.translate
        # client contact required
        active_contacts = [c for c in self.context.objectValues('Contact') if
                           wf.getInfoFor(c, 'inactive_state', '') == 'active']
        if isActive(self.context):
            if self.context.portal_type == "Client" and not active_contacts:
                msg = _("Client contact required before request may be submitted")
                addPortalMessage(msg)
            else:
                if mtool.checkPermission(AddAnalysisRequest, self.context):
                    self.context_actions[t(_('Add'))] = {
                        'url': self.context.absolute_url() + "/portal_factory/"
                        "AnalysisRequest/Request new analyses/ar_add",
                        'icon': '++resource++bika.lims.images/add.png'}

            # in client context we can use a permission check for this transition
            # in multi-client listings, we must rather check against user roles.
            if mtool.checkPermission(ModifyPortalContent, self.context):
                review_states = []
                for review_state in self.review_states:
                    review_state['custom_actions'].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

        return super(ClientAnalysisRequestsView, self).__call__()
Example #8
0
    def submitTransition(self, action, came_from, items):
        """ Performs the action's transition for the specified items
            Returns (numtransitions, destination), where:
            - numtransitions: the number of objects successfully transitioned.
                If no objects have been successfully transitioned, gets 0 value
            - destination: the destination url to be loaded immediately
        """
        dest = None
        transitioned = []
        workflow = getToolByName(self.context, 'portal_workflow')

        # transition selected items from the bika_listing/Table.
        for item in items:
            # the only actions allowed on inactive/cancelled
            # items are "reinstate" and "activate"
            if not isActive(item) and action not in ('reinstate', 'activate'):
                continue
            if not skip(item, action, peek=True):
                allowed_transitions = [it['id'] for it in \
                                       workflow.getTransitionsFor(item)]
                if action in allowed_transitions:
                    success, message = doActionFor(item, action)
                    if success:
                        transitioned.append(item.id)
                    else:
                        self.context.plone_utils.addPortalMessage(message, 'error')
        # automatic label printing
        if transitioned and action == 'receive' \
            and 'receive' in self.portal.bika_setup.getAutoPrintStickers():
            q = "/sticker?template=%s&items=" % (self.portal.bika_setup.getAutoStickerTemplate())
            # selected_items is a list of UIDs (stickers for AR_add use IDs)
            q += ",".join(transitioned)
            dest = self.context.absolute_url() + q

        return len(transitioned), dest
Example #9
0
    def __call__(self):
        self.context_actions = {}
        wf = getToolByName(self.context, 'portal_workflow')
        mtool = getToolByName(self.context, 'portal_membership')
        addPortalMessage = self.context.plone_utils.addPortalMessage
        PR = self.context.getPrimaryReferrer()
        if isActive(self.context):
            if mtool.checkPermission(AddAnalysisRequest, PR):
                #client contact required
                contacts = [
                    c for c in PR.objectValues('Contact')
                    if wf.getInfoFor(c, 'inactive_state', '') == 'active'
                ]
                if contacts:
                    self.context_actions[self.context.translate(_('Add'))] = {
                        'url': PR.absolute_url() + "/ar_add",
                        'icon': '++resource++bika.lims.images/add.png'
                    }
                else:
                    msg = _(
                        "Client contact required before request may be submitted"
                    )
                    addPortalMessage(self.context.translate(msg))

        return super(AnalysisRequestsView, self).__call__()
Example #10
0
    def __call__(self):
        self.context_actions = {}
        wf = getToolByName(self.context, 'portal_workflow')
        mtool = getToolByName(self.context, 'portal_membership')
        addPortalMessage = self.context.plone_utils.addPortalMessage
        translate = self.context.translate
        # client contact required
        active_contacts = [
            c for c in self.context.objectValues('Contact')
            if wf.getInfoFor(c, 'inactive_state', '') == 'active'
        ]
        if isActive(self.context):
            if self.context.portal_type == "Client" and not active_contacts:
                msg = _(
                    "Client contact required before request may be submitted")
                addPortalMessage(msg)
            else:
                if mtool.checkPermission(AddAnalysisRequest, self.context):
                    self.context_actions[t(_('Add'))] = {
                        'url': self.context.absolute_url() + "/portal_factory/"
                        "AnalysisRequest/Request new analyses/ar_add",
                        'icon': '++resource++bika.lims.images/add.png'
                    }

        return super(ClientAnalysisRequestsView, self).__call__()
Example #11
0
    def __call__(self):
        self.context_actions = {}
        wf = getToolByName(self.context, 'portal_workflow')
        mtool = getToolByName(self.context, 'portal_membership')
        addPortalMessage = self.context.plone_utils.addPortalMessage
        translate = self.context.translate
        # client contact required
        active_contacts = [c for c in self.context.objectValues('Contact') if
                           wf.getInfoFor(c, 'inactive_state', '') == 'active']
        if isActive(self.context):
            if self.context.portal_type == "Client" and not active_contacts:
                msg = _("Client contact required before request may be submitted")
                addPortalMessage(msg)
            else:
                if mtool.checkPermission(AddAnalysisRequest, self.context):
                    self.context_actions[t(_('Add'))] = {
                        'url': self.context.absolute_url() + "/portal_factory/"
                        "AnalysisRequest/Request new analyses/ar_add",
                        'icon': '++resource++bika.lims.images/add.png'}

            # in client context we can use a permission check for this transition
            # in multi-client listings, we must rather check against user roles.
            if mtool.checkPermission(ModifyPortalContent, self.context):
                review_states = []
                for review_state in self.review_states:
                    review_state['custom_actions'].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

        return super(ClientAnalysisRequestsView, self).__call__()
Example #12
0
 def workflow_action_publish(self):
     action, came_from = WorkflowAction._get_form_workflow_action(self)
     if not isActive(self.context):
         message = _('Item is inactive.')
         self.context.plone_utils.addPortalMessage(message, 'info')
         self.request.response.redirect(self.context.absolute_url())
         return
     # AR publish preview
     self.request.response.redirect(self.context.absolute_url() + "/publish")
Example #13
0
 def workflow_action_publish(self):
     action, came_from = WorkflowAction._get_form_workflow_action(self)
     if not isActive(self.context):
         message = _('Item is inactive.')
         self.context.plone_utils.addPortalMessage(message, 'info')
         self.request.response.redirect(self.context.absolute_url())
         return
     # AR publish preview
     self.request.response.redirect(self.context.absolute_url() + "/publish")
Example #14
0
    def __call__(self):
        form = self.request.form
        plone.protect.CheckAuthenticator(form)
        workflow = getToolByName(self.context, "portal_workflow")
        if self.destination_url == "":
            self.destination_url = self.request.get_header("referer", self.context.absolute_url())
        skiplist = self.request.get("workflow_skiplist", [])
        action, came_from = self._get_form_workflow_action()

        # transition the context object.
        if came_from == "workflow_action":
            obj = self.context
            # the only actions allowed on inactive/cancelled
            # items are "reinstate" and "activate"
            if not isActive(obj) and action not in ("reinstate", "activate"):
                message = _("Item is inactive.")
                self.context.plone_utils.addPortalMessage(message, "info")
                self.request.response.redirect(self.destination_url)
                return
            else:
                if obj.UID() not in skiplist:
                    workflow.doActionFor(obj, action)
                self.request.response.redirect(self.destination_url)
                return

        # transition selected items from the bika_listing/Table.
        transitioned = []
        selected_items = self._get_selected_items()
        for uid, item in selected_items.items():
            # the only action allowed on inactive items is "activate"
            if not isActive(item) and action not in ("reinstate", "activate"):
                continue
            if uid not in skiplist:
                try:
                    workflow.doActionFor(item, action)
                    transitioned.append(item.Title())
                except WorkflowException:
                    pass

        if len(transitioned) > 0:
            message = _("Changes saved.")
            self.context.plone_utils.addPortalMessage(message, "info")

        self.request.response.redirect(self.destination_url)
Example #15
0
 def __call__(self):
     mtool = getToolByName(self.context, 'portal_membership')
     checkPermission = mtool.checkPermission
     if isActive(self.context):
         if checkPermission(AddAnalysisSpec, self.context):
             self.context_actions[_('Add')] = \
                 {'url': 'createObject?type_name=AnalysisSpec',
                  'permission': 'Add portal content',
                  'icon': '++resource++bika.lims.images/add.png'}
     return super(ClientAnalysisSpecsView, self).__call__()
Example #16
0
 def getContacts(self, dl=True):
     pairs = []
     objects = []
     for contact in self.aq_parent.objectValues('Contact'):
         if isActive(contact) and contact.UID() != self.UID():
             pairs.append((contact.UID(), contact.Title()))
             if not dl:
                 objects.append(contact)
     pairs.sort(lambda x, y: cmp(x[1].lower(), y[1].lower()))
     return dl and DisplayList(pairs) or objects
Example #17
0
 def getContacts(self, dl=True):
     pairs = []
     objects = []
     for contact in self.aq_parent.objectValues('Contact'):
         if isActive(contact) and contact.UID() != self.UID():
             pairs.append((contact.UID(), contact.Title()))
             if not dl:
                 objects.append(contact)
     pairs.sort(lambda x, y: cmp(x[1].lower(), y[1].lower()))
     return dl and DisplayList(pairs) or objects
Example #18
0
 def getCCContacts(self):
     """Return a JSON value, containing all Contacts and their default CCs
     for this client.  This function is used to set form values for javascript.
     """
     contact_data = []
     for contact in self.objectValues('Contact'):
         if isActive(contact):
             this_contact_data = {'title': contact.Title(),
                                  'uid': contact.UID(), }
             ccs = []
             for cc in contact.getCCContact():
                 if isActive(cc):
                     ccs.append({'title': cc.Title(),
                                 'uid': cc.UID(),})
             this_contact_data['ccs_json'] = json.dumps(ccs)
             this_contact_data['ccs'] = ccs
             contact_data.append(this_contact_data)
     contact_data.sort(lambda x, y:cmp(x['title'].lower(),
                                       y['title'].lower()))
     return contact_data
Example #19
0
 def artemplates(self):
     """ Return applicable client and Lab ARTemplate records
     """
     res = []
     templates = []
     client = self.context.portal_type == 'AnalysisRequest' \
         and self.context.aq_parent or self.context
     for template in client.objectValues("ARTemplate"):
         if isActive(template):
             templates.append((template.Title(), template))
     templates.sort(lambda x, y: cmp(x[0], y[0]))
     res += templates
     templates = []
     for template in self.context.bika_setup.bika_artemplates.objectValues("ARTemplate"):
         if isActive(template):
             lab = t(_('Lab'))
             title = to_utf8(template.Title())
             templates.append(("%s: %s" % (lab, title), template))
     templates.sort(lambda x, y: cmp(x[0], y[0]))
     res += templates
     return res
Example #20
0
 def analysisprofiles(self):
     """ Return applicable client and Lab AnalysisProfile records
     """
     res = []
     profiles = []
     client = self.context.portal_type == 'AnalysisRequest' \
         and self.context.aq_parent or self.context
     for profile in client.objectValues("AnalysisProfile"):
         if isActive(profile):
             profiles.append((profile.Title(), profile))
     profiles.sort(lambda x, y: cmp(x[0], y[0]))
     res += profiles
     profiles = []
     for profile in self.context.bika_setup.bika_analysisprofiles.objectValues("AnalysisProfile"):
         if isActive(profile):
             lab = t(_('Lab'))
             title = to_utf8(profile.Title())
             profiles.append(("%s: %s" % (lab, title), profile))
     profiles.sort(lambda x, y: cmp(x[0], y[0]))
     res += profiles
     return res
Example #21
0
 def __call__(self):
     self.context_actions = {}
     bc = getToolByName(self.context, 'bika_catalog')
     wf = getToolByName(self.context, 'portal_workflow')
     mtool = getToolByName(self.context, 'portal_membership')
     addPortalMessage = self.context.plone_utils.addPortalMessage
     if isActive(self.context):
         if mtool.checkPermission(AddAnalysisRequest, self.portal):
             self.context_actions[self.context.translate(_('Add new'))] = {
                 'url':self.context.absolute_url() + '/ar_add?col_count=1',
                 'icon': '++resource++bika.lims.images/add.png'}
     return super(BatchAnalysisRequestsView, self).__call__()
Example #22
0
 def artemplates(self):
     """ Return applicable client and Lab ARTemplate records
     """
     res = []
     templates = []
     client = self.context.portal_type == 'AnalysisRequest' \
         and self.context.aq_parent or self.context
     for template in client.objectValues("ARTemplate"):
         if isActive(template):
             templates.append((template.Title(), template))
     templates.sort(lambda x, y: cmp(x[0], y[0]))
     res += templates
     templates = []
     for template in self.context.bika_setup.bika_artemplates.objectValues("ARTemplate"):
         if isActive(template):
             lab = t(_('Lab'))
             title = to_utf8(template.Title())
             templates.append(("%s: %s" % (lab, title), template))
     templates.sort(lambda x, y: cmp(x[0], y[0]))
     res += templates
     return res
Example #23
0
 def analysisprofiles(self):
     """ Return applicable client and Lab AnalysisProfile records
     """
     res = []
     profiles = []
     client = self.context.portal_type == 'AnalysisRequest' \
         and self.context.aq_parent or self.context
     for profile in client.objectValues("AnalysisProfile"):
         if isActive(profile):
             profiles.append((profile.Title(), profile))
     profiles.sort(lambda x, y: cmp(x[0], y[0]))
     res += profiles
     profiles = []
     for profile in self.context.bika_setup.bika_analysisprofiles.objectValues("AnalysisProfile"):
         if isActive(profile):
             lab = t(_('Lab'))
             title = to_utf8(profile.Title())
             profiles.append(("%s: %s" % (lab, title), profile))
     profiles.sort(lambda x, y: cmp(x[0], y[0]))
     res += profiles
     return res
Example #24
0
 def __call__(self):
     mtool = getToolByName(self.context, 'portal_membership')
     addPortalMessage = self.context.plone_utils.addPortalMessage
     w_tool = getToolByName(self.context, 'portal_workflow')
     active_contacts = [
         c for c in self.context.objectValues('Contact')
         if w_tool.getInfoFor(c, 'inactive_state', '') == 'active'
     ]
     if isActive(self.context):
         if self.context.portal_type == "Client":
             if not active_contacts:
                 msg = _(
                     "Client contact required before request may be submitted"
                 )
                 addPortalMessage(msg)
             else:
                 if mtool.checkPermission(AddProject, self.context):
                     self.context_actions[_('Add')] = {
                         'url': 'createObject?type_name=Project',
                         'icon': '++resource++bika.lims.images/add.png'
                     }
     if mtool.checkPermission(ManageProjects, self.context):
         self.review_states[0]['transitions'].append({'id': 'deactivate'})
         self.review_states.append({
             'id':
             'inactive',
             'title':
             _('Dormant'),
             'contentFilter': {
                 'inactive_state': 'inactive'
             },
             'transitions': [
                 {
                     'id': 'activate'
                 },
             ],
             'columns': ['Title', 'getClient', 'getStudyType']
         })
         self.review_states.append({
             'id':
             'all',
             'title':
             _('All'),
             'contentFilter': {},
             'transitions': [{
                 'id': 'empty'
             }],
             'columns': ['Title', 'getClient', 'getStudyType']
         })
         stat = self.request.get("%s_review_state" % self.form_id,
                                 'default')
         self.show_select_column = stat != 'all'
     return BikaListingView.__call__(self)
Example #25
0
    def folderitems(self, full_objects=False):
        items = BikaListingView.folderitems(self)
        bsc = getToolByName(self.context, 'bika_setup_catalog')
        brains = bsc(portal_type='SampleType', inactive_state='active')
        biospecimen_types = [{
            'ResultValue': brain.UID,
            'ResultText': brain.title
        } for brain in brains]
        ret = []
        for x, item in enumerate(items):
            if not items[x].has_key('obj'):
                continue
            obj = items[x]['obj']
            if not ISample.providedBy(obj):
                continue
            items[x]['Type'] = obj.getSampleType() and obj.getSampleType(
            ).Title() or ''
            items[x]['Volume'] = obj.getField('Volume').get(obj)
            items[x]['Unit'] = VOLUME_UNITS[0]['ResultText']
            items[x]['SubjectID'] = obj.getField('SubjectID').get(obj)
            kit = obj.getField('Kit').get(obj)
            project = obj.getField('Project').get(obj)
            items[x]['Kit'] = kit
            items[x]['Project'] = project
            if project:
                items[x]['replace']['Project'] = \
                    '<a href="%s">%s</a>' % (project.absolute_url(),
                                             project.Title())
            if kit:
                items[x]['replace']['Kit'] = \
                    '<a href="%s">%s</a>' % (kit.absolute_url(), kit.Title())

                # TODO: IF STATUS IS RECEIVED EXECUTE THIS
                # items[x]['replace']['Type'] = \
                #     '<a href="%s">%s</a>' % (obj.getSampleType().absolute_url(),
                #                              obj.getSampleType().Title())
            items[x]['Barcode'] = obj.getField('Barcode').get(obj)
            items[x]['replace']['Title'] = "<a href='%s'>%s</a>" % \
                                           (items[x]['url'], items[x]['Title'])
            # TODO: SPECIFY OBJ STATES WHERE USER CAN EDIT BARCODE
            if self.allow_edit and isActive(self.context) and \
                    getSecurityManager().checkPermission(ManageProjects, obj):
                if items[x]['review_state'] == "sample_registered":
                    items[x]['allow_edit'] = ['Type', 'Barcode']
                    items[x]['choices']['Type'] = biospecimen_types
                elif items[x]['review_state'] == "sample_due":
                    items[x]['allow_edit'] = ['SubjectID', 'Volume', 'Unit']

                    if not items[x]['Unit']:
                        items[x]['choices']['Unit'] = VOLUME_UNITS
            ret.append(item)
        return ret
Example #26
0
 def __call__(self):
     self.context_actions = {}
     bc = getToolByName(self.context, 'bika_catalog')
     wf = getToolByName(self.context, 'portal_workflow')
     mtool = getToolByName(self.context, 'portal_membership')
     addPortalMessage = self.context.plone_utils.addPortalMessage
     if isActive(self.context):
         if mtool.checkPermission(AddAnalysisRequest, self.portal):
             self.context_actions[self.context.translate(_('Add new'))] = {
                 'url': self.context.absolute_url() + '/ar_add?col_count=1',
                 'icon': '++resource++bika.lims.images/add.png'
             }
     return super(BatchAnalysisRequestsView, self).__call__()
Example #27
0
 def __call__(self):
     self.context_actions = {}
     bc = getToolByName(self.context, "bika_catalog")
     wf = getToolByName(self.context, "portal_workflow")
     mtool = getToolByName(self.context, "portal_membership")
     addPortalMessage = self.context.plone_utils.addPortalMessage
     if isActive(self.context):
         if mtool.checkPermission(AddAnalysisRequest, self.portal):
             self.context_actions[self.context.translate(_("Add new"))] = {
                 "url": self.context.absolute_url() + "/ar_add?col_count=1",
                 "icon": "++resource++bika.lims.images/add.png",
             }
     return super(BatchAnalysisRequestsView, self).__call__()
 def getContacts(self, dl=True):
     pc = getToolByName(self, 'portal_catalog')
     bc = getToolByName(self, 'bika_catalog')
     bsc = getToolByName(self, 'bika_setup_catalog')
     pairs = []
     objects = []
     for contact in self.objectValues('Contact'):
         if isActive(contact):
             pairs.append((contact.UID(), contact.Title()))
             if not dl:
                 objects.append(contact)
     pairs.sort(lambda x, y:cmp(x[1].lower(), y[1].lower()))
     return dl and DisplayList(pairs) or objects
Example #29
0
 def workflow_action_publish(self):
     action, came_from = WorkflowAction._get_form_workflow_action(self)
     if not isActive(self.context):
         message = _('Item is inactive.')
         self.context.plone_utils.addPortalMessage(message, 'info')
         self.request.response.redirect(self.context.absolute_url())
         return
     # AR publish preview
     uids = self.request.form.get('uids', [self.context.UID()])
     items = ",".join(uids)
     self.request.response.redirect(self.context.portal_url() +
                                    "/analysisrequests/publish?items=" +
                                    items)
Example #30
0
 def __call__(self):
     mtool = getToolByName(self.context, 'portal_membership')
     checkPermission = mtool.checkPermission
     if isActive(self.context):
         if checkPermission(AddAnalysisSpec, self.context):
             self.context_actions[_('Add')] = \
                 {'url': 'createObject?type_name=AnalysisSpec',
                  'icon': '++resource++bika.lims.images/add.png'}
         if checkPermission("Modify portal content", self.context):
             self.context_actions[_('Set to lab defaults')] = \
                 {'url': 'set_to_lab_defaults',
                  'icon': '++resource++bika.lims.images/analysisspec.png'}
     return super(ClientAnalysisSpecsView, self).__call__()
 def getCCs(self):
     items = []
     for contact in self.getContacts(dl=False):
         item = {"uid": contact.UID(), "title": contact.Title()}
         ccs = []
         if hasattr(contact, "getCCContact"):
             for cc in contact.getCCContact():
                 if isActive(cc):
                     ccs.append({"title": cc.Title(), "uid": cc.UID()})
         item["ccs_json"] = json.dumps(ccs)
         item["ccs"] = ccs
         items.append(item)
     items.sort(lambda x, y: cmp(x["title"].lower(), y["title"].lower()))
     return items
Example #32
0
 def __call__(self):
     mtool = getToolByName(self.context, "portal_membership")
     checkPermission = mtool.checkPermission
     if isActive(self.context):
         if checkPermission(AddAnalysisSpec, self.context):
             self.context_actions[_("Add")] = {
                 "url": "createObject?type_name=AnalysisSpec",
                 "icon": "++resource++bika.lims.images/add.png",
             }
         if checkPermission(ManageClients, self.context):
             self.context_actions[_("Set to lab defaults")] = {
                 "url": "set_to_lab_defaults",
                 "icon": "++resource++bika.lims.images/analysisspec.png",
             }
     return super(ClientAnalysisSpecsView, self).__call__()
Example #33
0
 def getCCContacts(self):
     """Return a JSON value, containing all Contacts and their default CCs
     for this client.  This function is used to set form values for javascript.
     """
     contact_data = []
     for contact in self.objectValues('Contact'):
         if isActive(contact):
             this_contact_data = {
                 'title': contact.Title(),
                 'uid': contact.UID(),
             }
             ccs = []
             for cc in contact.getCCContact():
                 if isActive(cc):
                     ccs.append({
                         'title': cc.Title(),
                         'uid': cc.UID(),
                     })
             this_contact_data['ccs_json'] = json.dumps(ccs)
             this_contact_data['ccs'] = ccs
         contact_data.append(this_contact_data)
     contact_data.sort(
         lambda x, y: cmp(x['title'].lower(), y['title'].lower()))
     return contact_data
 def getCCs(self):
     items = []
     for contact in self.getContacts(dl=False):
         item = {'uid': contact.UID(), 'title': contact.Title()}
         ccs = []
         if hasattr(contact, 'getCCContact'):
             for cc in contact.getCCContact():
                 if isActive(cc):
                     ccs.append({'title': cc.Title(),
                                 'uid': cc.UID(),})
         item['ccs_json'] = json.dumps(ccs)
         item['ccs'] = ccs
         items.append(item)
     items.sort(lambda x, y:cmp(x['title'].lower(), y['title'].lower()))
     return items
Example #35
0
 def __call__(self):
     mtool = getToolByName(self.context, 'portal_membership')
     checkPermission = mtool.checkPermission
     if isActive(self.context):
         if checkPermission(AddAnalysisSpec, self.context):
             self.context_actions[_('Add')] = \
                 {'url': 'createObject?type_name=AnalysisSpec',
                  'icon': '++resource++bika.lims.images/add.png'}
             #
             # @lemoene with the changes made in AR-specs, I dont know how much
             # sense this makes anymore.
             # if checkPermission("Modify portal content", self.context):
             #     self.context_actions[_('Set to lab defaults')] = \
             #         {'url': 'set_to_lab_defaults',
             #          'icon': '++resource++bika.lims.images/analysisspec.png'}
     return super(ClientAnalysisSpecsView, self).__call__()
Example #36
0
 def __call__(self):
     mtool = getToolByName(self.context, 'portal_membership')
     checkPermission = mtool.checkPermission
     if isActive(self.context):
         if checkPermission(AddAnalysisSpec, self.context):
             self.context_actions[_('Add')] = \
                 {'url': 'createObject?type_name=AnalysisSpec',
                  'icon': '++resource++bika.lims.images/add.png'}
             #
             # @lemoene with the changes made in AR-specs, I dont know how much
             # sense this makes anymore.
             # if checkPermission("Modify portal content", self.context):
             #     self.context_actions[_('Set to lab defaults')] = \
             #         {'url': 'set_to_lab_defaults',
             #          'icon': '++resource++bika.lims.images/analysisspec.png'}
     return super(ClientAnalysisSpecsView, self).__call__()
Example #37
0
    def __init__(self, context, request):
        super(ClientAnalysisSpecsView, self).__init__(context, request)
        bsc = getToolByName(context, 'bika_setup_catalog')
        wf = getToolByName(context, 'portal_workflow')
        self.contentFilter = {
            'portal_type': 'AnalysisSpec',
            'getClientUID': context.UID(),
            'path': {
                "query": "/".join(context.getPhysicalPath()),
                "level": 0
            }
        }
        self.contentsMethod = bsc

        checkPermission = self.context.portal_membership.checkPermission
        self.context_actions = {}
        if isActive(self.context):
            if checkPermission(AddAnalysisSpec, self.context):
                self.context_actions[translate(_('Add'))] = \
                    {'url': 'createObject?type_name=AnalysisSpec',
                     'icon': '++resource++bika.lims.images/add.png'}
            if checkPermission(ManageClients, self.context):
                self.context_actions[translate(_('Set to lab defaults'))] = \
                    {'url': 'set_to_lab_defaults',
                     'icon': '++resource++bika.lims.images/analysisspec.png'}

        self.show_sort_column = False
        self.show_select_row = False
        self.show_select_column = True
        self.pagesize = 50

        self.icon = "++resource++bika.lims.images/analysisspec_big.png"
        self.title = _("Analysis Specifications")

        self.columns = {
            'SampleType': {
                'title': _('Sample Type'),
                'index': 'getSampleTypeTitle'
            },
        }
        self.review_states = [
            {
                'id': 'all',
                'title': _('All'),
                'columns': ['SampleType'],
            },
        ]
Example #38
0
 def __call__(self):
     plone.protect.CheckAuthenticator(self.request.form)
     rc = getToolByName(self.context, REFERENCE_CATALOG)
     uid = self.request.form.keys() and self.request.form.keys()[0] or None
     if not uid:
         return
     contact = rc.lookupObject(uid)
     cc_uids = []
     cc_titles = []
     for cc in contact.getCCContact():
         active = isActive(contact)
         if not active:
             continue
         cc_uids.append(cc.UID())
         cc_titles.append(cc.Title())
     return json.dumps([",".join(cc_uids),
                        ",".join(cc_titles)])
Example #39
0
 def __call__(self):
     self.context_actions = {}
     wf = getToolByName(self.context, 'portal_workflow')
     mtool = getToolByName(self.context, 'portal_membership')
     addPortalMessage = self.context.plone_utils.addPortalMessage
     # client contact required
     active_contacts = [c for c in self.context.objectValues('Contact') if
                        wf.getInfoFor(c, 'inactive_state', '') == 'active']
     if isActive(self.context):
         if self.context.portal_type == "Client" and not active_contacts:
             msg = _("Client contact required before request may be submitted")
             addPortalMessage(self.context.translate(msg))
         else:
             if mtool.checkPermission(AddAnalysisRequest, self.context):
                 self.context_actions[self.context.translate(_('Add'))] = {
                     'url':'ar_add',
                     'icon': '++resource++bika.lims.images/add.png'}
     return super(ClientAnalysisRequestsView, self).__call__()
Example #40
0
 def folderitems(self, full_objects=False):
     items = BikaListingView.folderitems(self)
     bsc = getToolByName(self.context, 'bika_setup_catalog')
     brains = bsc(portal_type='SampleType', inactive_state='active')
     biospecimen_types = [
         {
             'ResultValue': brain.UID,
             'ResultText': brain.title
         }
         for brain in brains
         ]
     ret = []
     for x, item in enumerate(items):
         if not items[x].has_key('obj'):
             continue
         obj = items[x]['obj']
         if not IBiospecimen.providedBy(obj):
             continue
         items[x]['Type'] = obj.getSampleType() and obj.getSampleType().Title() or ''
         items[x]['Volume'] = obj.getField('Volume').get(obj)
         items[x]['Unit'] = VOLUME_UNITS[0]['ResultText']
         items[x]['SubjectID'] = obj.getField('SubjectID').get(obj)
         kit = obj.getField('Kit').get(obj)
         items[x]['Kit'] = kit
         items[x]['Project'] = obj.aq_parent
         if kit:
             items[x]['replace']['Kit'] = \
                 '<a href="%s">%s</a>' % (kit.absolute_url(), kit.Title())
             items[x]['replace']['Project'] = \
                 '<a href="%s">%s</a>' % (kit.aq_parent.absolute_url(),
                                          kit.aq_parent.Title())
         items[x]['Barcode'] = obj.getField('Barcode').get(obj)
         items[x]['replace']['Title'] = "<a href='%s'>%s</a>" % \
                                        (items[x]['url'], items[x]['Title'])
         # TODO: SPECIFY OBJ STATES WHERE USER CAN EDIT BARCODE
         if self.allow_edit and isActive(self.context) and \
                 getSecurityManager().checkPermission(ManageAliquots, obj) and \
                         items[x]['review_state'] == "sample_due":
             items[x]['allow_edit'] = ['Type', 'SubjectID', 'Barcode', 'Volume', 'Unit']
             items[x]['choices']['Type'] = biospecimen_types
             items[x]['choices']['Unit'] = VOLUME_UNITS
         ret.append(item)
     return ret
Example #41
0
 def getCCs(self):
     """Return a JSON value, containing all Contacts and their default CCs.
        This function is used to set form values for javascript.
     """
     items = []
     for contact in self.getContacts(dl=False):
         item = {'uid': contact.UID(), 'title': contact.Title()}
         ccs = []
         if hasattr(contact, 'getCCContact'):
             for cc in contact.getCCContact():
                 if isActive(cc):
                     ccs.append({
                         'title': cc.Title(),
                         'uid': cc.UID(),
                     })
         item['ccs_json'] = json.dumps(ccs)
         item['ccs'] = ccs
         items.append(item)
     items.sort(lambda x, y: cmp(x['title'].lower(), y['title'].lower()))
     return items
Example #42
0
 def __call__(self):
     self.context_actions = {}
     wf = getToolByName(self.context, "portal_workflow")
     mtool = getToolByName(self.context, "portal_membership")
     addPortalMessage = self.context.plone_utils.addPortalMessage
     # client contact required
     active_contacts = [
         c for c in self.context.objectValues("Contact") if wf.getInfoFor(c, "inactive_state", "") == "active"
     ]
     if isActive(self.context):
         if self.context.portal_type == "Client" and not active_contacts:
             msg = _("Client contact required before request may be submitted")
             addPortalMessage(self.context.translate(msg))
         else:
             if mtool.checkPermission(AddAnalysisRequest, self.context):
                 self.context_actions[self.context.translate(_("Add"))] = {
                     "url": "ar_add",
                     "icon": "++resource++bika.lims.images/add.png",
                 }
     return super(ClientAnalysisRequestsView, self).__call__()
Example #43
0
    def __call__(self):
        self.context_actions = {}
        wf = getToolByName(self.context, 'portal_workflow')
        mtool = getToolByName(self.context, 'portal_membership')
        addPortalMessage = self.context.plone_utils.addPortalMessage
        PR = self.context.getPrimaryReferrer()
        if isActive(self.context):
            if mtool.checkPermission(AddAnalysisRequest, PR):
                #client contact required
                contacts = [c for c in PR.objectValues('Contact') if
                            wf.getInfoFor(c, 'inactive_state', '') == 'active']
                if contacts:
                    self.context_actions[self.context.translate(_('Add'))] = {
                        'url': PR.absolute_url() + "/portal_factory/"
                        "AnalysisRequest/Request new analyses/ar_add",
                        'icon': '++resource++bika.lims.images/add.png'}
                else:
                    msg = _("Client contact required before request may be submitted")
                    addPortalMessage(self.context.translate(msg))

        return super(AnalysisRequestsView, self).__call__()
Example #44
0
    def folderitems(self, full_objects=False):
        items = BikaListingView.folderitems(self)
        bsc = getToolByName(self.context, 'bika_setup_catalog')
        brains = bsc(portal_type='SampleType', inactive_state='active')
        aliquot_types = [
            {
                'ResultValue': brain.UID,
                'ResultText': brain.title
            }
            for brain in brains
        ]
        ret = []
        for x, item in enumerate(items):
            if not items[x].has_key('obj'):
                continue
            obj = items[x]['obj']
            if not IAliquot.providedBy(obj):
                continue
            items[x]['replace']['Biospecimen'] = \
                "<a href='%s'>%s</a>" % (obj.getField('LinkedSample').get(obj).absolute_url(),
                                        obj.getField('LinkedSample').get(obj).Title())
            items[x]['AliquotType'] = obj.getSampleType() and obj.getSampleType().Title() or ''
            items[x]['Volume'] = items[x]['Volume'] = obj.getField('Volume').get(obj)
            items[x]['Unit'] = VOLUME_UNITS[0]['ResultText']
            items[x]['replace']['Title'] = "<a href='%s'>%s</a>" % \
                                           (items[x]['url'], items[x]['Title'])
            if self.context.portal_type == 'Aliquots':
                items[x]['replace']['Project'] = \
                    '<a href="%s">%s</a>' % (obj.aq_parent.absolute_url(),
                                             obj.aq_parent.Title())

            if self.allow_edit and isActive(self.context) and \
                    getSecurityManager().checkPermission("Modify portal content", obj) and \
                    items[x]['review_state'] == "sample_due":
                items[x]['allow_edit'] = ['AliquotType', 'Volume', 'Unit']
                items[x]['choices']['AliquotType'] = aliquot_types
                items[x]['choices']['Unit'] = VOLUME_UNITS
            ret.append(item)

        return ret
Example #45
0
    def folderitems(self, full_objects=False):
        items = BikaListingView.folderitems(self)
        bsc = getToolByName(self.context, 'bika_setup_catalog')
        brains = bsc(portal_type='SampleType', inactive_state='active')
        aliquot_types = [{
            'ResultValue': brain.UID,
            'ResultText': brain.title
        } for brain in brains]
        ret = []
        for x, item in enumerate(items):
            if not items[x].has_key('obj'):
                continue
            obj = items[x]['obj']
            if not IAliquot.providedBy(obj):
                continue
            items[x]['replace']['Biospecimen'] = \
                "<a href='%s'>%s</a>" % (obj.getField('LinkedSample').get(obj).absolute_url(),
                                        obj.getField('LinkedSample').get(obj).Title())
            items[x]['AliquotType'] = obj.getSampleType(
            ) and obj.getSampleType().Title() or ''
            items[x]['Volume'] = items[x]['Volume'] = obj.getField(
                'Volume').get(obj)
            items[x]['Unit'] = VOLUME_UNITS[0]['ResultText']
            items[x]['replace']['Title'] = "<a href='%s'>%s</a>" % \
                                           (items[x]['url'], items[x]['Title'])
            if self.context.portal_type == 'Aliquots':
                items[x]['replace']['Project'] = \
                    '<a href="%s">%s</a>' % (obj.aq_parent.absolute_url(),
                                             obj.aq_parent.Title())

            if self.allow_edit and isActive(self.context) and \
                    getSecurityManager().checkPermission("Modify portal content", obj) and \
                    items[x]['review_state'] == "sample_due":
                items[x]['allow_edit'] = ['AliquotType', 'Volume', 'Unit']
                items[x]['choices']['AliquotType'] = aliquot_types
                items[x]['choices']['Unit'] = VOLUME_UNITS
            ret.append(item)

        return ret
Example #46
0
 def __call__(self):
     mtool = getToolByName(self.context, 'portal_membership')
     addPortalMessage = self.context.plone_utils.addPortalMessage
     w_tool = getToolByName(self.context, 'portal_workflow')
     active_contacts = [c for c in self.context.objectValues('Contact') if
                        w_tool.getInfoFor(c, 'inactive_state', '') == 'active']
     if isActive(self.context):
         if self.context.portal_type == "Client":
             if not active_contacts:
                 msg = _("Client contact required before request may be submitted")
                 addPortalMessage(msg)
             else:
                 if mtool.checkPermission(AddProject, self.context):
                     self.context_actions[_('Add')] = {
                         'url': 'createObject?type_name=Project',
                         'icon': '++resource++bika.lims.images/add.png'
                     }
     if mtool.checkPermission(ManageProjects, self.context):
         self.review_states[0]['transitions'].append({'id': 'deactivate'})
         self.review_states.append(
             {'id': 'inactive',
              'title': _('Dormant'),
              'contentFilter': {'inactive_state': 'inactive'},
              'transitions': [{'id': 'activate'}, ],
              'columns': ['Title',
                          'getClient',
                          'getStudyType']})
         self.review_states.append(
             {'id': 'all',
              'title': _('All'),
              'contentFilter': {},
              'transitions': [{'id': 'empty'}],
              'columns': ['Title',
                          'getClient',
                          'getStudyType']})
         stat = self.request.get("%s_review_state" % self.form_id, 'default')
         self.show_select_column = stat != 'all'
     return super(ProjectsView, self).__call__()
Example #47
0
 def workflow_action_publish(self):
     action, came_from = WorkflowAction._get_form_workflow_action(self)
     if not isActive(self.context):
         message = _('Item is inactive.')
         self.context.plone_utils.addPortalMessage(message, 'info')
         self.request.response.redirect(self.context.absolute_url())
         return
     # publish entire AR.
     self.context.setDatePublished(DateTime())
     transitioned = self.doPublish(self.context,
                            self.request,
                            action,
                            [self.context, ])()
     if len(transitioned) == 1:
         message = _(
             '${items} published.',
             mapping={'items': safe_unicode(', '.join(transitioned))})
     else:
         message = _("No items were published")
     self.context.plone_utils.addPortalMessage(message, 'info')
     self.destination_url = self.request.get_header("referer",
                            self.context.absolute_url())
     self.request.response.redirect(self.destination_url)
Example #48
0
    def workflow_action_submit(self):
        form = self.request.form
        rc = getToolByName(self.context, REFERENCE_CATALOG)
        action, came_from = WorkflowAction._get_form_workflow_action(self)
        checkPermission = self.context.portal_membership.checkPermission
        if not isActive(self.context):
            message = _('Item is inactive.')
            self.context.plone_utils.addPortalMessage(message, 'info')
            self.request.response.redirect(self.context.absolute_url())
            return
        # calcs.js has kept item_data and form input interim values synced,
        # so the json strings from item_data will be the same as the form values
        item_data = {}
        if 'item_data' in form:
            if isinstance(form['item_data'], list):
                for i_d in form['item_data']:
                    for i, d in json.loads(i_d).items():
                        item_data[i] = d
            else:
                item_data = json.loads(form['item_data'])
        selected_analyses = WorkflowAction._get_selected_items(self)
        results = {}
        hasInterims = {}
        # check that the form values match the database
        # save them if not.
        for uid, result in self.request.form.get('Result', [{}])[0].items():
            # Do not save data for analyses that are not selected.
            if uid not in selected_analyses:
                continue
            analysis = selected_analyses[uid]
            # never save any part of rows with empty result values.
            # https://jira.bikalabs.com/browse/LIMS-1944:
            if not result:
                continue
            # ignore result if analysis object no longer exists
            if not analysis:
                continue
            # Prevent saving data if the analysis is already transitioned
            if not (checkPermission(EditResults, analysis)
                    or checkPermission(EditFieldResults, analysis)):
                title = safe_unicode(analysis.getService().Title())
                msgid = _('Result for ${analysis} could not be saved because '
                          'it was already submitted by another user.',
                          mapping={'analysis': title})
                message = safe_unicode(t(msgid))
                self.context.plone_utils.addPortalMessage(message)
                continue
            # if the AR has ReportDryMatter set, get dry_result from form.
            dry_result = ''
            if hasattr(self.context, 'getReportDryMatter') \
               and self.context.getReportDryMatter():
                for k, v in self.request.form['ResultDM'][0].items():
                    if uid == k:
                        dry_result = v
                        break
            results[uid] = result
            interimFields = item_data[uid]
            if len(interimFields) > 0:
                hasInterims[uid] = True
            else:
                hasInterims[uid] = False
            retested = 'retested' in form and uid in form['retested']
            remarks = form.get('Remarks', [{}, ])[0].get(uid, '')
            # Don't save uneccessary things
            # https://github.com/bikalabs/Bika-LIMS/issues/766:
            #    Somehow, using analysis.edit() fails silently when
            #    logged in as Analyst.
            if analysis.getInterimFields() != interimFields or \
               analysis.getRetested() != retested or \
               analysis.getRemarks() != remarks:
                analysis.setInterimFields(interimFields)
                analysis.setRetested(retested)
                analysis.setRemarks(remarks)
            # save results separately, otherwise capture date is rewritten
            if analysis.getResult() != result or \
               analysis.getResultDM() != dry_result:
                analysis.setResultDM(dry_result)
                analysis.setResult(result)
        methods = self.request.form.get('Method', [{}])[0]
        instruments = self.request.form.get('Instrument', [{}])[0]
        analysts = self.request.form.get('Analyst', [{}])[0]
        uncertainties = self.request.form.get('Uncertainty', [{}])[0]
        dlimits = self.request.form.get('DetectionLimit', [{}])[0]
        # discover which items may be submitted
        submissable = []
        for uid, analysis in selected_analyses.items():
            analysis_active = isActive(analysis)

            # Need to save the instrument?
            if uid in instruments and analysis_active:
                # TODO: Add SetAnalysisInstrument permission
                # allow_setinstrument = sm.checkPermission(SetAnalysisInstrument)
                allow_setinstrument = True
                # ---8<-----
                if allow_setinstrument == True:
                    # The current analysis allows the instrument regards
                    # to its analysis service and method?
                    if (instruments[uid]==''):
                        previnstr = analysis.getInstrument()
                        if previnstr:
                            previnstr.removeAnalysis(analysis)
                        analysis.setInstrument(None);
                    elif analysis.isInstrumentAllowed(instruments[uid]):
                        previnstr = analysis.getInstrument()
                        if previnstr:
                            previnstr.removeAnalysis(analysis)
                        analysis.setInstrument(instruments[uid])
                        instrument = analysis.getInstrument()
                        instrument.addAnalysis(analysis)

            # Need to save the method?
            if uid in methods and analysis_active:
                # TODO: Add SetAnalysisMethod permission
                # allow_setmethod = sm.checkPermission(SetAnalysisMethod)
                allow_setmethod = True
                # ---8<-----
                if allow_setmethod == True and analysis.isMethodAllowed(methods[uid]):
                    analysis.setMethod(methods[uid])

            # Need to save the analyst?
            if uid in analysts and analysis_active:
                analysis.setAnalyst(analysts[uid])

            # Need to save the uncertainty?
            if uid in uncertainties and analysis_active:
                analysis.setUncertainty(uncertainties[uid])

            # Need to save the detection limit?
            if analysis_active:
                analysis.setDetectionLimitOperand(dlimits.get(uid, None))

            if uid not in results or not results[uid]:
                continue
            can_submit = True
            # guard_submit does a lot of the same stuff, too.
            # the code there has also been commented.
            # we must find a better way to allow dependencies to control
            # this process.
            # for dependency in analysis.getDependencies():
            #     dep_state = workflow.getInfoFor(dependency, 'review_state')
            #     if hasInterims[uid]:
            #         if dep_state in ('to_be_sampled', 'to_be_preserved',
            #                          'sample_due', 'sample_received',
            #                          'attachment_due', 'to_be_verified',):
            #             can_submit = False
            #             break
            #     else:
            #         if dep_state in ('to_be_sampled', 'to_be_preserved',
            #                          'sample_due', 'sample_received',):
            #             can_submit = False
            #             break
            if can_submit and analysis not in submissable:
                submissable.append(analysis)
        # and then submit them.
        for analysis in submissable:
            doActionFor(analysis, 'submit')

        # LIMS-2366: Finally, when we are done processing all applicable
        # analyses, we must attempt to initiate the submit transition on the
        # AR itself. This is for the case where "general retraction" has been
        # done, or where the last "received" analysis has been removed, and
        # the AR is in state "received" while there are no "received" analyses
        # left to trigger the parent transition.
        if self.context.portal_type == 'Sample':
            ar = self.context.getAnalysisRequests()[0]
        elif self.context.portal_type == 'Analysis':
            ar = self.context.aq_parent
        else:
            ar = self.context
        doActionFor(ar, 'submit')

        message = PMF("Changes saved.")
        self.context.plone_utils.addPortalMessage(message, 'info')
        if checkPermission(EditResults, self.context):
            self.destination_url = self.context.absolute_url() + "/manage_results"
        else:
            self.destination_url = self.context.absolute_url()
        self.request.response.redirect(self.destination_url)
Example #49
0
    def folderitems(self):
        bsc = getToolByName(self.context, 'bika_setup_catalog')
        analysis_categories = bsc(portal_type="AnalysisCategory",
                                  sort_on="sortable_title")
        analysis_categories_order = dict([
            (b.Title, "{:04}".format(a))
            for a, b in enumerate(analysis_categories)
        ])
        workflow = getToolByName(self.context, 'portal_workflow')
        mtool = getToolByName(self.context, 'portal_membership')
        checkPermission = mtool.checkPermission
        if not self.allow_edit:
            can_edit_analyses = False
        else:
            if self.contentFilter.get('getPointOfCapture', '') == 'field':
                can_edit_analyses = checkPermission(EditFieldResults,
                                                    self.context)
            else:
                can_edit_analyses = checkPermission(EditResults, self.context)
            self.allow_edit = can_edit_analyses
        self.show_select_column = self.allow_edit
        context_active = isActive(self.context)

        self.categories = []
        items = super(AnalysesView, self).folderitems(full_objects=True)

        member = mtool.getAuthenticatedMember()

        self.interim_fields = {}
        self.interim_columns = {}
        self.specs = {}
        show_methodinstr_columns = False
        dmk = self.context.bika_setup.getResultsDecimalMark()

        for item in items:
            if 'obj' not in item:
                logger.warn(
                    "Missing 'obj' key in Analysis item '{}'".format(item))
                continue

            # self.contentsMethod may return brains or objects.
            obj = api.get_object(item["obj"])

            if workflow.getInfoFor(obj, 'review_state') == 'retracted' \
               and not checkPermission(ViewRetractedAnalyses, self.context):
                logger.info("Skipping retracted analysis {}".format(
                    obj.getId()))
                continue

            result = obj.getResult()
            service = obj.getService()
            calculation = service.getCalculation()
            unit = service.getUnit()
            keyword = service.getKeyword()

            if self.show_categories:
                cat = obj.getService().getCategoryTitle()
                cat_order = analysis_categories_order.get(cat)
                item['category'] = cat
                if (cat, cat_order) not in self.categories:
                    self.categories.append((cat, cat_order))

            # Check for InterimFields attribute on our object,
            interim_fields = hasattr(obj, 'getInterimFields') \
                and obj.getInterimFields() or []
            # kick some pretty display values in.
            for x in range(len(interim_fields)):
                interim_fields[x]['formatted_value'] = \
                    formatDecimalMark(interim_fields[x]['value'], dmk)
            self.interim_fields[obj.UID()] = interim_fields
            item['service_uid'] = service.UID()
            item['Service'] = service.Title()
            item['Keyword'] = keyword
            item['Unit'] = format_supsub(unit) if unit else ''
            item['Result'] = ''
            item['formatted_result'] = ''
            item['interim_fields'] = interim_fields
            item['Remarks'] = obj.getRemarks()
            item['Uncertainty'] = ''
            item['DetectionLimit'] = ''
            item['retested'] = obj.getRetested()
            item['class']['retested'] = 'center'
            item['result_captured'] = self.ulocalized_time(
                obj.getResultCaptureDate(), long_format=0)
            item['calculation'] = calculation and True or False
            try:
                item['Partition'] = obj.getSamplePartition().getId()
            except AttributeError:
                item['Partition'] = ''
            if obj.portal_type == "ReferenceAnalysis":
                item['DueDate'] = self.ulocalized_time(
                    obj.aq_parent.getExpiryDate(), long_format=0)
            else:
                item['DueDate'] = self.ulocalized_time(obj.getDueDate(),
                                                       long_format=1)
            cd = obj.getResultCaptureDate()
            item['CaptureDate'] = cd and self.ulocalized_time(
                cd, long_format=1) or ''
            item['Attachments'] = ''

            item['allow_edit'] = []

            tblrowclass = item.get('table_row_class')
            if obj.portal_type == 'ReferenceAnalysis':
                item['st_uid'] = obj.aq_parent.UID()
                item['table_row_class'] = ' '.join(
                    [tblrowclass, 'qc-analysis'])
            elif obj.portal_type == 'DuplicateAnalysis' and \
                    obj.getAnalysis().portal_type == 'ReferenceAnalysis':
                item['st_uid'] = obj.aq_parent.UID()
                item['table_row_class'] = ' '.join(
                    [tblrowclass, 'qc-analysis'])
            else:
                sample = None
                if self.context.portal_type == 'AnalysisRequest':
                    sample = self.context.getSample()
                elif self.context.portal_type == 'Worksheet':
                    if obj.portal_type in ('DuplicateAnalysis',
                                           'RejectAnalysis'):
                        sample = obj.getAnalysis().getSample()
                    else:
                        sample = obj.aq_parent.getSample()
                elif self.context.portal_type == 'Sample':
                    sample = self.context
                st_uid = sample.getSampleType().UID() if sample else ''
                item['st_uid'] = st_uid

            if checkPermission(ManageBika, self.context):
                # service_uid = service.UID()
                # latest = rc.lookupObject(service_uid).version_id
                item['Service'] = service.Title()
                item['class']['Service'] = "service_title"

            # Show version number of out-of-date objects
            # No: This should be done in another column, if at all.
            # The (vX) value confuses some more fragile forms.
            #     if hasattr(obj, 'reference_versions') and \
            #        service_uid in obj.reference_versions and \
            #        latest != obj.reference_versions[service_uid]:
            #         items[i]['after']['Service'] = "(v%s)" % \
            #              (obj.reference_versions[service_uid])

            # choices defined on Service apply to result fields.
            choices = service.getResultOptions()
            if choices:
                item['choices']['Result'] = choices

            # permission to view this item's results
            can_view_result = \
                getSecurityManager().checkPermission(ViewResults, obj)

            # permission to edit this item's results
            # Editing Field Results is possible while in Sample Due.
            poc = self.contentFilter.get("getPointOfCapture", 'lab')
            can_edit_analysis = self.allow_edit and context_active and \
                ((poc == 'field' and getSecurityManager().checkPermission(EditFieldResults, obj)) or
                 (poc != 'field' and getSecurityManager().checkPermission(EditResults, obj)))

            allowed_method_states = [
                'to_be_sampled',
                'to_be_preserved',
                'sample_received',
                'sample_registered',
                'sampled',
                'assigned',
            ]

            # Prevent from being edited if the instrument assigned
            # is not valid (out-of-date or uncalibrated), except if
            # the analysis is a QC with assigned status
            can_edit_analysis = can_edit_analysis \
                and (obj.isInstrumentValid() or
                     (obj.portal_type == 'ReferenceAnalysis' and
                      item['review_state'] in allowed_method_states))

            if can_edit_analysis:
                item['allow_edit'].extend(['Analyst', 'Result', 'Remarks'])
                # if the Result field is editable, our interim fields are too
                for f in self.interim_fields[obj.UID()]:
                    item['allow_edit'].append(f['keyword'])

                # if there isn't a calculation then result must be re-testable,
                # and if there are interim fields, they too must be re-testable.
                if not item['calculation'] or \
                   (item['calculation'] and self.interim_fields[obj.UID()]):
                    item['allow_edit'].append('retested')

            # TODO: Only the labmanager must be able to change the method
            # can_set_method = getSecurityManager().checkPermission(SetAnalysisMethod, obj)
            can_set_method = can_edit_analysis \
                and item['review_state'] in allowed_method_states
            method = obj.getMethod() \
                if hasattr(obj, 'getMethod') and obj.getMethod() else service.getMethod()

            # Display the methods selector if the AS has at least one
            # method assigned
            item['Method'] = ''
            item['replace']['Method'] = ''
            if can_set_method:
                voc = self.get_methods_vocabulary(obj)
                if voc:
                    # The service has at least one method available
                    item['Method'] = method.UID() if method else ''
                    item['choices']['Method'] = voc
                    item['allow_edit'].append('Method')
                    show_methodinstr_columns = True

                elif method:
                    # This should never happen
                    # The analysis has set a method, but its parent
                    # service hasn't any method available O_o
                    item['Method'] = method.Title()
                    item['replace']['Method'] = "<a href='%s'>%s</a>" % \
                        (method.absolute_url(), method.Title())
                    show_methodinstr_columns = True

            elif method:
                # Edition not allowed, but method set
                item['Method'] = method.Title()
                item['replace']['Method'] = "<a href='%s'>%s</a>" % \
                    (method.absolute_url(), method.Title())
                show_methodinstr_columns = True

            # TODO: Instrument selector dynamic behavior in worksheet Results
            # Only the labmanager must be able to change the instrument to be used. Also,
            # the instrument selection should be done in accordance with the method selected
            # can_set_instrument = service.getInstrumentEntryOfResults() and getSecurityManager().checkPermission(SetAnalysisInstrument, obj)
            can_set_instrument = service.getInstrumentEntryOfResults() \
                and can_edit_analysis \
                and item['review_state'] in allowed_method_states

            item['Instrument'] = ''
            item['replace']['Instrument'] = ''
            if service.getInstrumentEntryOfResults():
                instrument = None

                # If the analysis has an instrument already assigned, use it
                if service.getInstrumentEntryOfResults() \
                    and hasattr(obj, 'getInstrument') \
                        and obj.getInstrument():
                    instrument = obj.getInstrument()

                # Otherwise, use the Service's default instrument
                elif service.getInstrumentEntryOfResults():
                    instrument = service.getInstrument()

                if can_set_instrument:
                    # Edition allowed
                    voc = self.get_instruments_vocabulary(obj)
                    if voc:
                        # The service has at least one instrument available
                        item['Instrument'] = instrument.UID(
                        ) if instrument else ''
                        item['choices']['Instrument'] = voc
                        item['allow_edit'].append('Instrument')
                        show_methodinstr_columns = True

                    elif instrument:
                        # This should never happen
                        # The analysis has an instrument set, but the
                        # service hasn't any available instrument
                        item['Instrument'] = instrument.Title()
                        item['replace']['Instrument'] = "<a href='%s'>%s</a>" % \
                            (instrument.absolute_url(), instrument.Title())
                        show_methodinstr_columns = True

                elif instrument:
                    # Edition not allowed, but instrument set
                    item['Instrument'] = instrument.Title()
                    item['replace']['Instrument'] = "<a href='%s'>%s</a>" % \
                        (instrument.absolute_url(), instrument.Title())
                    show_methodinstr_columns = True

            else:
                # Manual entry of results, instrument not allowed
                item['Instrument'] = _('Manual')
                msgtitle = t(
                    _(
                        "Instrument entry of results not allowed for ${service}",
                        mapping={"service": safe_unicode(service.Title())},
                    ))
                item['replace']['Instrument'] = \
                    '<a href="#" title="%s">%s</a>' % (msgtitle, t(_('Manual')))

            # Sets the analyst assigned to this analysis
            if can_edit_analysis:
                analyst = obj.getAnalyst()
                # widget default: current user
                if not analyst:
                    analyst = mtool.getAuthenticatedMember().getUserName()
                item['Analyst'] = analyst
                item['choices']['Analyst'] = self.getAnalysts()
            else:
                item['Analyst'] = obj.getAnalystName()

            # If the user can attach files to analyses, show the attachment col
            can_add_attachment = \
                getSecurityManager().checkPermission(AddAttachment, obj)
            if can_add_attachment or can_view_result:
                attachments = ""
                if hasattr(obj, 'getAttachment'):
                    for attachment in obj.getAttachment():
                        af = attachment.getAttachmentFile()
                        icon = af.icon
                        attachments += "<span class='attachment' attachment_uid='%s'>" % (
                            attachment.UID())
                        if icon:
                            attachments += "<img src='%s/%s'/>" % (
                                self.portal_url, icon)
                        attachments += '<a href="%s/at_download/AttachmentFile"/>%s</a>' % (
                            attachment.absolute_url(), af.filename)
                        if can_edit_analysis:
                            attachments += "<img class='deleteAttachmentButton' attachment_uid='%s' src='%s'/>" % (
                                attachment.UID(),
                                "++resource++bika.lims.images/delete.png")
                        attachments += "</br></span>"
                item['replace']['Attachments'] = attachments[:-12] + "</span>"

            # Only display data bearing fields if we have ViewResults
            # permission, otherwise just put an icon in Result column.
            if can_view_result:
                item['Result'] = result
                scinot = self.context.bika_setup.getScientificNotationResults()
                item['formatted_result'] = obj.getFormattedResult(
                    sciformat=int(scinot), decimalmark=dmk)

                # LIMS-1379 Allow manual uncertainty value input
                # https://jira.bikalabs.com/browse/LIMS-1379
                fu = format_uncertainty(obj,
                                        result,
                                        decimalmark=dmk,
                                        sciformat=int(scinot))
                fu = fu if fu else ''
                if can_edit_analysis and service.getAllowManualUncertainty(
                ) is True:
                    unc = obj.getUncertainty(result)
                    item['allow_edit'].append('Uncertainty')
                    item['Uncertainty'] = unc if unc else ''
                    item['before']['Uncertainty'] = '&plusmn;&nbsp;'
                    item['after'][
                        'Uncertainty'] = '<em class="discreet" style="white-space:nowrap;"> %s</em>' % item[
                            'Unit']
                    item['structure'] = False
                elif fu:
                    item['Uncertainty'] = fu
                    item['before']['Uncertainty'] = '&plusmn;&nbsp;'
                    item['after'][
                        'Uncertainty'] = '<em class="discreet" style="white-space:nowrap;"> %s</em>' % item[
                            'Unit']
                    item['structure'] = True

                # LIMS-1700. Allow manual input of Detection Limits
                # LIMS-1775. Allow to select LDL or UDL defaults in results with readonly mode
                # https://jira.bikalabs.com/browse/LIMS-1700
                # https://jira.bikalabs.com/browse/LIMS-1775
                if can_edit_analysis and \
                    hasattr(obj, 'getDetectionLimitOperand') and \
                    hasattr(service, 'getDetectionLimitSelector') and \
                        service.getDetectionLimitSelector() is True:
                    isldl = obj.isBelowLowerDetectionLimit()
                    isudl = obj.isAboveUpperDetectionLimit()
                    dlval = ''
                    if isldl or isudl:
                        dlval = '<' if isldl else '>'
                    item['allow_edit'].append('DetectionLimit')
                    item['DetectionLimit'] = dlval
                    choices = [{
                        'ResultValue': '<',
                        'ResultText': '<'
                    }, {
                        'ResultValue': '>',
                        'ResultText': '>'
                    }]
                    item['choices']['DetectionLimit'] = choices
                    self.columns['DetectionLimit']['toggle'] = True
                    srv = obj.getService()
                    defdls = {
                        'min': srv.getLowerDetectionLimit(),
                        'max': srv.getUpperDetectionLimit(),
                        'manual': srv.getAllowManualDetectionLimit()
                    }
                    defin = '<input type="hidden" id="DefaultDLS.%s" value=\'%s\'/>'
                    defin = defin % (obj.UID(), json.dumps(defdls))
                    item['after']['DetectionLimit'] = defin

                # LIMS-1769. Allow to use LDL and UDL in calculations.
                # https://jira.bikalabs.com/browse/LIMS-1769
                # Since LDL, UDL, etc. are wildcards that can be used
                # in calculations, these fields must be loaded always
                # for 'live' calculations.
                if can_edit_analysis:
                    dls = {
                        'default_ldl': 'none',
                        'default_udl': 'none',
                        'below_ldl': False,
                        'above_udl': False,
                        'is_ldl': False,
                        'is_udl': False,
                        'manual_allowed': False,
                        'dlselect_allowed': False
                    }
                    if hasattr(obj, 'getDetectionLimits'):
                        dls['below_ldl'] = obj.isBelowLowerDetectionLimit()
                        dls['above_udl'] = obj.isBelowLowerDetectionLimit()
                        dls['is_ldl'] = obj.isLowerDetectionLimit()
                        dls['is_udl'] = obj.isUpperDetectionLimit()
                        dls['default_ldl'] = service.getLowerDetectionLimit()
                        dls['default_udl'] = service.getUpperDetectionLimit()
                        dls['manual_allowed'] = service.getAllowManualDetectionLimit(
                        )
                        dls['dlselect_allowed'] = service.getDetectionLimitSelector(
                        )
                    dlsin = '<input type="hidden" id="AnalysisDLS.%s" value=\'%s\'/>'
                    dlsin = dlsin % (obj.UID(), json.dumps(dls))
                    item['after']['Result'] = dlsin

            else:
                item['Specification'] = ""
                if 'Result' in item['allow_edit']:
                    item['allow_edit'].remove('Result')
                item['before']['Result'] = \
                    '<img width="16" height="16" ' + \
                    'src="%s/++resource++bika.lims.images/to_follow.png"/>' % \
                    (self.portal_url)
            # Everyone can see valid-ranges
            spec = self.get_analysis_spec(obj)
            if spec:
                min_val = spec.get('min', '')
                min_str = ">{0}".format(min_val) if min_val else ''
                max_val = spec.get('max', '')
                max_str = "<{0}".format(max_val) if max_val else ''
                error_val = spec.get('error', '')
                error_str = "{0}%".format(error_val) if error_val else ''
                rngstr = ",".join(
                    [x for x in [min_str, max_str, error_str] if x])
            else:
                rngstr = ""
            item['Specification'] = rngstr
            # Add this analysis' interim fields to the interim_columns list
            for f in self.interim_fields[obj.UID()]:
                if f['keyword'] not in self.interim_columns and not f.get(
                        'hidden', False):
                    self.interim_columns[f['keyword']] = f['title']
                # and to the item itself
                item[f['keyword']] = f
                item['class'][f['keyword']] = 'interim'

            # check if this analysis is late/overdue

            resultdate = obj.aq_parent.getDateSampled() \
                if obj.portal_type == 'ReferenceAnalysis' \
                else obj.getResultCaptureDate()

            duedate = obj.aq_parent.getExpiryDate() \
                if obj.portal_type == 'ReferenceAnalysis' \
                else obj.getDueDate()

            item['replace']['DueDate'] = \
                self.ulocalized_time(duedate, long_format=1)

            if item['review_state'] not in [
                    'to_be_sampled', 'to_be_preserved', 'sample_due',
                    'published'
            ]:

                if (resultdate and resultdate > duedate) \
                   or (not resultdate and DateTime() > duedate):

                    item['replace']['DueDate'] = '%s <img width="16" height="16" src="%s/++resource++bika.lims.images/late.png" title="%s"/>' % \
                        (self.ulocalized_time(duedate, long_format=1),
                         self.portal_url,
                         t(_("Late Analysis")))

            after_icons = []
            # Submitting user may not verify results unless the user is labman
            # or manager and the AS has isSelfVerificationEnabled set to True
            if item['review_state'] == 'to_be_verified':
                # If multi-verification required, place an informative icon
                numverifications = obj.getNumberOfRequiredVerifications()
                if numverifications > 1:
                    # More than one verification required, place an icon
                    # Get the number of verifications already done:
                    done = obj.getNumberOfVerifications()
                    pending = numverifications - done
                    ratio = float(done) / float(numverifications) \
                        if done > 0 else 0
                    scale = '' if ratio < 0.25 else '25' \
                            if ratio < 0.50 else '50' \
                            if ratio < 0.75 else '75'
                    anchor = "<a href='#' title='%s &#13;%s %s' " \
                             "class='multi-verification scale-%s'>%s/%s</a>"
                    anchor = anchor % (t(
                        _("Multi-verification required")), str(pending),
                                       t(_("verification(s) pending")), scale,
                                       str(done), str(numverifications))
                    after_icons.append(anchor)

                username = member.getUserName()
                allowed = ploneapi.user.has_permission(VerifyPermission,
                                                       username=username)
                if allowed and not obj.isUserAllowedToVerify(member):
                    after_icons.append(
                        "<img src='++resource++bika.lims.images/submitted-by-current-user.png' title='%s'/>"
                        % (t(
                            _("Cannot verify, submitted or verified by current user before"
                              ))))
                elif allowed:
                    if obj.getSubmittedBy() == member.getUser().getId():
                        after_icons.append(
                            "<img src='++resource++bika.lims.images/warning.png' title='%s'/>"
                            %
                            (t(_("Can verify, but submitted by current user")))
                        )
            # If analysis Submitted and Verified by the same person, then warning icon will appear.
            submitter = obj.getSubmittedBy()
            if submitter and obj.wasVerifiedByUser(submitter):
                after_icons.append(
                    "<img src='++resource++bika.lims.images/warning.png' title='%s'/>"
                    % (t(
                        _("Submited and verified by the same user- " +
                          submitter))))

            # add icon for assigned analyses in AR views
            if self.context.portal_type == 'AnalysisRequest':
                obj = item['obj']
                if obj.portal_type in ['ReferenceAnalysis',
                                       'DuplicateAnalysis'] or \
                   workflow.getInfoFor(obj, 'worksheetanalysis_review_state') == 'assigned':
                    br = obj.getBackReferences('WorksheetAnalysis')
                    if len(br) > 0:
                        ws = br[0]
                        after_icons.append(
                            "<a href='%s'><img src='++resource++bika.lims.images/worksheet.png' title='%s'/></a>"
                            % (ws.absolute_url(),
                               t(
                                   _("Assigned to: ${worksheet_id}",
                                     mapping={
                                         'worksheet_id': safe_unicode(ws.id)
                                     }))))
            item['after']['state_title'] = '&nbsp;'.join(after_icons)

        # the TAL requires values for all interim fields on all
        # items, so we set blank values in unused cells
        for item in items:
            for field in self.interim_columns:
                if field not in item:
                    item[field] = ''

        # XXX order the list of interim columns
        interim_keys = self.interim_columns.keys()
        interim_keys.reverse()

        # add InterimFields keys to columns
        for col_id in interim_keys:
            if col_id not in self.columns:
                self.columns[col_id] = {
                    'title': self.interim_columns[col_id],
                    'input_width': '6',
                    'input_class': 'ajax_calculate numeric',
                    'sortable': False
                }

        if can_edit_analyses:
            new_states = []
            for state in self.review_states:
                # InterimFields are displayed in review_state
                # They are anyway available through View.columns though.
                # In case of hidden fields, the calcs.py should check calcs/services
                # for additional InterimFields!!
                pos = 'Result' in state['columns'] and \
                    state['columns'].index('Result') or len(state['columns'])
                for col_id in interim_keys:
                    if col_id not in state['columns']:
                        state['columns'].insert(pos, col_id)
                # retested column is added after Result.
                pos = 'Result' in state['columns'] and \
                    state['columns'].index('Uncertainty') + 1 or len(state['columns'])
                state['columns'].insert(pos, 'retested')
                new_states.append(state)
            self.review_states = new_states
            # Allow selecting individual analyses
            self.show_select_column = True

        # Dry Matter.
        # The Dry Matter column is never enabled for reference sample contexts
        # and refers to getReportDryMatter in ARs.
        if items and \
            (hasattr(self.context, 'getReportDryMatter') and
             self.context.getReportDryMatter()):

            # look through all items
            # if the item's Service supports ReportDryMatter, add getResultDM().
            for item in items:
                if item['obj'].getService().getReportDryMatter():
                    item['ResultDM'] = item['obj'].getResultDM()
                else:
                    item['ResultDM'] = ''
                if item['ResultDM']:
                    item['after']['ResultDM'] = "<em class='discreet'>%</em>"

            # modify the review_states list to include the ResultDM column
            new_states = []
            for state in self.review_states:
                pos = 'Result' in state['columns'] and \
                    state['columns'].index('Uncertainty') + 1 or len(state['columns'])
                state['columns'].insert(pos, 'ResultDM')
                new_states.append(state)
            self.review_states = new_states

        if self.show_categories:
            self.categories = map(lambda x: x[0],
                                  sorted(self.categories, key=lambda x: x[1]))
        else:
            self.categories.sort()

        # self.json_specs = json.dumps(self.specs)
        self.json_interim_fields = json.dumps(self.interim_fields)
        self.items = items

        # Method and Instrument columns must be shown or hidden at the
        # same time, because the value assigned to one causes
        # a value reassignment to the other (one method can be performed
        # by different instruments)
        self.columns['Method']['toggle'] = show_methodinstr_columns
        self.columns['Instrument']['toggle'] = show_methodinstr_columns

        return items
Example #50
0
    def folderitems(self, full_objects=False):
        # Show only ISharable samples for EMS.  Skip others.
        pm = getToolByName(self.context, 'portal_membership')
        roles = pm.getAuthenticatedMember().getRoles()

        #print roles
        if 'EMS' in roles:
            self.contentFilter['object_provides'] = ISharableSample.__identifier__

        items = BikaListingView.folderitems(self)
        bsc = getToolByName(self.context, 'bika_setup_catalog')
        brains = bsc(portal_type='SampleType', inactive_state='active')
        biospecimen_types = [
            {
                'ResultValue': brain.UID,
                'ResultText': brain.title
            }
            for brain in brains
            ]
        ret = []
        for x, item in enumerate(items):
            if not items[x].has_key('obj'):
                continue
            obj = items[x]['obj']

            if not ISample.providedBy(obj):
                continue
            items[x]['Type'] = obj.getSampleType() and obj.getSampleType().Title() or ''
            items[x]['Volume'] = obj.getField('Volume').get(obj)
            items[x]['Unit'] = obj.getField('Unit').get(obj)
            items[x]['SubjectID'] = obj.getField('SubjectID').get(obj)
            project = obj.getField('Project').get(obj)
            if not project:
               project = obj.aq_parent
            items[x]['Project'] = project
            storage_location = obj.getField('StorageLocation').get(obj)
            if storage_location:
                items[x]['StorageLocation'] = storage_location.Title()
            if project:
                items[x]['replace']['Project'] = \
                    '<a href="%s">%s</a>' % (project.absolute_url(),
                                             project.Title())

            items[x]['Barcode'] = obj.getField('Barcode').get(obj)
            items[x]['replace']['Title'] = "<a href='%s'>%s</a>" % \
                                           (items[x]['url'], items[x]['Title'])
            frozen_time = obj.getField('FrozenTime').get(obj)
            if frozen_time:
                try:
                    items[x]['FrozenTime'] = frozen_time.strftime("%Y-%m-%d %H:%M")
                except:
                    items[x]['FrozenTime'] = str(frozen_time)

            batch = obj.getField('Batch').get(obj)

            try:
                items[x]['CFGTime'] = batch.getField('CfgDateTime').get(batch).strftime("%Y/%m/%d %H:%M")
            except:
                items[x]['CFGTime'] = ''
            try:
                items[x]['SamplingTime'] = obj.getField('SamplingDate').get(obj).strftime("%Y/%m/%d %H:%M")
            except:
                items[x]['SamplingTime'] = ''

            if self.allow_edit and isActive(self.context) and \
                    getSecurityManager().checkPermission(ModifyPortalContent, obj):
                if items[x]['review_state'] == "sample_registered":
                    items[x]['allow_edit'] = ['Type', 'Barcode', 'FrozenTime']
                    items[x]['choices']['Type'] = biospecimen_types
                elif items[x]['review_state'] == "sample_due":
                    # items[x]['allow_edit'] = ['SubjectID', 'Volume', 'Unit']
                    items[x]['allow_edit'] = ['Volume', 'Unit']

                    if not items[x]['Unit']:
                        items[x]['choices']['Unit'] = VOLUME_UNITS
                elif items[x]['review_state'] == "sample_shipped":
                    # items[x]['allow_edit'] = ['SubjectID', 'Volume']
                    items[x]['allow_edit'] = ['Volume']

            ret.append(item)
        return ret
Example #51
0
    def folderitems(self):
        rc = getToolByName(self.context, REFERENCE_CATALOG)
        bsc = getToolByName(self.context, 'bika_setup_catalog')
        workflow = getToolByName(self.context, 'portal_workflow')
        mtool = getToolByName(self.context, 'portal_membership')
        checkPermission = mtool.checkPermission
        if not self.allow_edit:
            can_edit_analyses = False
        else:
            if self.contentFilter.get('getPointOfCapture', '') == 'field':
                can_edit_analyses = checkPermission(EditFieldResults, self.context)
            else:
                can_edit_analyses = checkPermission(EditResults, self.context)
            self.allow_edit = can_edit_analyses
        self.show_select_column = self.allow_edit
        context_active = isActive(self.context)

        self.categories = []
        items = super(AnalysesView, self).folderitems(full_objects = True)

        # manually skim retracted analyses from the list
        new_items = []
        for i,item in enumerate(items):
            # self.contentsMethod may return brains or objects.
            if not ('obj' in items[i]):
                continue
            obj = hasattr(items[i]['obj'], 'getObject') and \
                items[i]['obj'].getObject() or \
                items[i]['obj']
            if workflow.getInfoFor(obj, 'review_state') == 'retracted' \
                and not checkPermission(ViewRetractedAnalyses, self.context):
                continue
            new_items.append(item)
        items = new_items

        methods = self.get_methods_vocabulary()

        self.interim_fields = {}
        self.interim_columns = {}
        self.specs = {}
        show_methodinstr_columns = False
        for i, item in enumerate(items):
            # self.contentsMethod may return brains or objects.
            obj = hasattr(items[i]['obj'], 'getObject') and \
                items[i]['obj'].getObject() or \
                items[i]['obj']
            if workflow.getInfoFor(obj, 'review_state') == 'retracted' \
                and not checkPermission(ViewRetractedAnalyses, self.context):
                continue

            result = obj.getResult()
            service = obj.getService()
            calculation = service.getCalculation()
            unit = service.getUnit()
            keyword = service.getKeyword()

            if self.show_categories:
                cat = obj.getService().getCategoryTitle()
                items[i]['category'] = cat
                if cat not in self.categories:
                    self.categories.append(cat)

            # Check for InterimFields attribute on our object,
            interim_fields = hasattr(obj, 'getInterimFields') \
                and obj.getInterimFields() or []
            # kick some pretty display values in.
            for x in range(len(interim_fields)):
                interim_fields[x]['formatted_value'] = \
                    format_numeric_result(obj, interim_fields[x]['value'])
            self.interim_fields[obj.UID()] = interim_fields
            items[i]['service_uid'] = service.UID()
            items[i]['Service'] = service.Title()
            items[i]['Keyword'] = keyword
            items[i]['Unit'] = format_supsub(unit) if unit else ''
            items[i]['Result'] = ''
            items[i]['formatted_result'] = ''
            items[i]['interim_fields'] = interim_fields
            items[i]['Remarks'] = obj.getRemarks()
            items[i]['Uncertainty'] = ''
            items[i]['retested'] = obj.getRetested()
            items[i]['class']['retested'] = 'center'
            items[i]['result_captured'] = self.ulocalized_time(
                obj.getResultCaptureDate(), long_format=0)
            items[i]['calculation'] = calculation and True or False
            try:
                items[i]['Partition'] = obj.getSamplePartition().getId()
            except AttributeError:
                items[i]['Partition'] = ''
            if obj.portal_type == "ReferenceAnalysis":
                items[i]['DueDate'] = self.ulocalized_time(obj.aq_parent.getExpiryDate(), long_format=0)
            else:
                items[i]['DueDate'] = self.ulocalized_time(obj.getDueDate(), long_format=1)
            cd = obj.getResultCaptureDate()
            items[i]['CaptureDate'] = cd and self.ulocalized_time(cd, long_format=1) or ''
            items[i]['Attachments'] = ''

            item['allow_edit'] = []
            client_or_lab = ""

            tblrowclass = items[i].get('table_row_class');
            if obj.portal_type == 'ReferenceAnalysis':
                items[i]['st_uid'] = obj.aq_parent.UID()
                items[i]['table_row_class'] = ' '.join([tblrowclass, 'qc-analysis']);
            elif obj.portal_type == 'DuplicateAnalysis' and \
                obj.getAnalysis().portal_type == 'ReferenceAnalysis':
                items[i]['st_uid'] = obj.aq_parent.UID()
                items[i]['table_row_class'] = ' '.join([tblrowclass, 'qc-analysis']);
            else:
                if self.context.portal_type == 'AnalysisRequest':
                    sample = self.context.getSample()
                    st_uid = sample.getSampleType().UID()
                    items[i]['st_uid'] = st_uid
                    if st_uid not in self.specs:
                        proxies = bsc(portal_type = 'AnalysisSpec',
                                      getSampleTypeUID = st_uid)
                elif self.context.portal_type == "Worksheet":
                    if obj.portal_type == "DuplicateAnalysis":
                        sample = obj.getAnalysis().getSample()
                    elif obj.portal_type == "RejectAnalysis":
                        sample = obj.getAnalysis().getSample()
                    else:
                        sample = obj.aq_parent.getSample()
                    st_uid = sample.getSampleType().UID()
                    items[i]['st_uid'] = st_uid
                    if st_uid not in self.specs:
                        proxies = bsc(portal_type = 'AnalysisSpec',
                                      getSampleTypeUID = st_uid)
                elif self.context.portal_type == 'Sample':
                    st_uid = self.context.getSampleType().UID()
                    items[i]['st_uid'] = st_uid
                    if st_uid not in self.specs:
                        proxies = bsc(portal_type = 'AnalysisSpec',
                                      getSampleTypeUID = st_uid)
                else:
                    proxies = []
                if st_uid not in self.specs:
                    for spec in (p.getObject() for p in proxies):
                        if spec.getClientUID() == obj.getClientUID():
                            client_or_lab = 'client'
                        elif spec.getClientUID() == self.context.bika_setup.bika_analysisspecs.UID():
                            client_or_lab = 'lab'
                        else:
                            continue
                        for keyword, results_range in \
                            spec.getResultsRangeDict().items():
                            # hidden form field 'specs' keyed by sampletype uid:
                            # {st_uid: {'lab/client':{keyword:{min,max,error}}}}
                            if st_uid in self.specs:
                                if client_or_lab in self.specs[st_uid]:
                                    self.specs[st_uid][client_or_lab][keyword] = results_range
                                else:
                                    self.specs[st_uid][client_or_lab] = {keyword: results_range}
                            else:
                                self.specs[st_uid] = {client_or_lab: {keyword: results_range}}

            if checkPermission(ManageBika, self.context):
                service_uid = service.UID()
                latest = rc.lookupObject(service_uid).version_id
                items[i]['Service'] = service.Title()
                items[i]['class']['Service'] = "service_title"

            # Show version number of out-of-date objects
            # No: This should be done in another column, if at all.
            # The (vX) value confuses some more fragile forms.
            #     if hasattr(obj, 'reference_versions') and \
            #        service_uid in obj.reference_versions and \
            #        latest != obj.reference_versions[service_uid]:
            #         items[i]['after']['Service'] = "(v%s)" % \
            #              (obj.reference_versions[service_uid])

            # choices defined on Service apply to result fields.
            choices = service.getResultOptions()
            if choices:
                item['choices']['Result'] = choices

            # permission to view this item's results
            can_view_result = \
                getSecurityManager().checkPermission(ViewResults, obj)

            # permission to edit this item's results
            # Editing Field Results is possible while in Sample Due.
            poc = self.contentFilter.get("getPointOfCapture", 'lab')
            can_edit_analysis = self.allow_edit and context_active and \
                ( (poc == 'field' and getSecurityManager().checkPermission(EditFieldResults, obj))
                  or
                  (poc != 'field' and getSecurityManager().checkPermission(EditResults, obj)) )


            allowed_method_states = ['to_be_sampled',
                                     'to_be_preserved',
                                     'sample_received',
                                     'sample_registered',
                                     'sampled',
                                     'assigned']

            # Prevent from being edited if the instrument assigned
            # is not valid (out-of-date or uncalibrated), except if
            # the analysis is a QC with assigned status
            can_edit_analysis = can_edit_analysis \
                and (obj.isInstrumentValid() \
                    or (obj.portal_type == 'ReferenceAnalysis' \
                        and item['review_state'] in allowed_method_states))

            if can_edit_analysis:
                items[i]['allow_edit'].extend(['Analyst',
                                               'Result',
                                               'Remarks'])
                # if the Result field is editable, our interim fields are too
                for f in self.interim_fields[obj.UID()]:
                    items[i]['allow_edit'].append(f['keyword'])

                # if there isn't a calculation then result must be re-testable,
                # and if there are interim fields, they too must be re-testable.
                if not items[i]['calculation'] or \
                   (items[i]['calculation'] and self.interim_fields[obj.UID()]):
                    items[i]['allow_edit'].append('retested')


            # TODO: Only the labmanager must be able to change the method
            # can_set_method = getSecurityManager().checkPermission(SetAnalysisMethod, obj)
            can_set_method = can_edit_analysis \
                and item['review_state'] in allowed_method_states
            method = obj.getMethod() \
                        if hasattr(obj, 'getMethod') and obj.getMethod() \
                        else service.getMethod()

            # Display the methods selector if the AS has at least one
            # method assigned
            item['Method'] = ''
            item['replace']['Method'] = ''
            if can_set_method:
                voc = self.get_methods_vocabulary(obj)
                if voc:
                    # The service has at least one method available
                    item['Method'] = method.UID() if method else ''
                    item['choices']['Method'] = voc
                    item['allow_edit'].append('Method')
                    show_methodinstr_columns = True

                elif method:
                    # This should never happen
                    # The analysis has set a method, but its parent
                    # service hasn't any method available O_o
                    item['Method'] = method.Title()
                    item['replace']['Method'] = "<a href='%s'>%s</a>" % \
                        (method.absolute_url(), method.Title())
                    show_methodinstr_columns = True

            elif method:
                # Edition not allowed, but method set
                item['Method'] = method.Title()
                item['replace']['Method'] = "<a href='%s'>%s</a>" % \
                    (method.absolute_url(), method.Title())
                show_methodinstr_columns = True

            # TODO: Instrument selector dynamic behavior in worksheet Results
            # Only the labmanager must be able to change the instrument to be used. Also,
            # the instrument selection should be done in accordance with the method selected
            # can_set_instrument = service.getInstrumentEntryOfResults() and getSecurityManager().checkPermission(SetAnalysisInstrument, obj)
            can_set_instrument = service.getInstrumentEntryOfResults() \
                and can_edit_analysis \
                and item['review_state'] in allowed_method_states

            item['Instrument'] = ''
            item['replace']['Instrument'] = ''
            if service.getInstrumentEntryOfResults():
                instrument = None

                # If the analysis has an instrument already assigned, use it
                if service.getInstrumentEntryOfResults() \
                    and hasattr(obj, 'getInstrument') \
                    and obj.getInstrument():
                        instrument = obj.getInstrument()

                # Otherwise, use the Service's default instrument
                elif service.getInstrumentEntryOfResults():
                        instrument = service.getInstrument()

                if can_set_instrument:
                    # Edition allowed
                    voc = self.get_instruments_vocabulary(obj)
                    if voc:
                        # The service has at least one instrument available
                        item['Instrument'] = instrument.UID() if instrument else ''
                        item['choices']['Instrument'] = voc
                        item['allow_edit'].append('Instrument')
                        show_methodinstr_columns = True

                    elif instrument:
                        # This should never happen
                        # The analysis has an instrument set, but the
                        # service hasn't any available instrument
                        item['Instrument'] = instrument.Title()
                        item['replace']['Instrument'] = "<a href='%s'>%s</a>" % \
                            (instrument.absolute_url(), instrument.Title())
                        show_methodinstr_columns = True

                elif instrument:
                    # Edition not allowed, but instrument set
                    item['Instrument'] = instrument.Title()
                    item['replace']['Instrument'] = "<a href='%s'>%s</a>" % \
                        (instrument.absolute_url(), instrument.Title())
                    show_methodinstr_columns = True

            else:
                # Manual entry of results, instrument not allowed
                item['Instrument'] = _('Manual')
                msgtitle = t(_(
                    "Instrument entry of results not allowed for ${service}",
                    mapping={"service": safe_unicode(service.Title())},
                ))
                item['replace']['Instrument'] = \
                    '<a href="#" title="%s">%s</a>' % (msgtitle, t(_('Manual')))

            # Sets the analyst assigned to this analysis
            if can_edit_analysis:
                analyst = obj.getAnalyst()
                # widget default: current user
                if not analyst:
                    analyst = mtool.getAuthenticatedMember().getUserName()
                items[i]['Analyst'] = analyst
                item['choices']['Analyst'] = self.getAnalysts()
            else:
                items[i]['Analyst'] = obj.getAnalystName()

            # If the user can attach files to analyses, show the attachment col
            can_add_attachment = \
                getSecurityManager().checkPermission(AddAttachment, obj)
            if can_add_attachment or can_view_result:
                attachments = ""
                if hasattr(obj, 'getAttachment'):
                    for attachment in obj.getAttachment():
                        af = attachment.getAttachmentFile()
                        icon = af.getBestIcon()
                        attachments += "<span class='attachment' attachment_uid='%s'>" % (attachment.UID())
                        if icon: attachments += "<img src='%s/%s'/>" % (self.portal_url, icon)
                        attachments += '<a href="%s/at_download/AttachmentFile"/>%s</a>' % (attachment.absolute_url(), af.filename)
                        if can_edit_analysis:
                            attachments += "<img class='deleteAttachmentButton' attachment_uid='%s' src='%s'/>" % (attachment.UID(), "++resource++bika.lims.images/delete.png")
                        attachments += "</br></span>"
                items[i]['replace']['Attachments'] = attachments[:-12] + "</span>"

            # Only display data bearing fields if we have ViewResults
            # permission, otherwise just put an icon in Result column.
            if can_view_result:
                items[i]['Result'] = result
                scinot = self.context.bika_setup.getScientificNotationResults()
                dmk = self.context.bika_setup.getResultsDecimalMark()
                items[i]['formatted_result'] = obj.getFormattedResult(sciformat=int(scinot),decimalmark=dmk)
                items[i]['Uncertainty'] = format_uncertainty(obj, result, decimalmark=dmk, sciformat=int(scinot))

            else:
                items[i]['Specification'] = ""
                if 'Result' in items[i]['allow_edit']:
                    items[i]['allow_edit'].remove('Result')
                items[i]['before']['Result'] = \
                    '<img width="16" height="16" ' + \
                    'src="%s/++resource++bika.lims.images/to_follow.png"/>' % \
                    (self.portal_url)
            # Everyone can see valid-ranges
            spec = self.get_analysis_spec(obj)
            if spec:
                min_val = spec.get('min', '')
                min_str = ">{0}".format(min_val) if min_val else ''
                max_val = spec.get('max', '')
                max_str = "<{0}".format(max_val) if max_val else ''
                error_val = spec.get('error', '')
                error_str = "{0}%".format(error_val) if error_val else ''
                rngstr = ",".join([x for x in [min_str, max_str, error_str] if x])
            else:
                rngstr = ""
            items[i]['Specification'] = rngstr
            # Add this analysis' interim fields to the interim_columns list
            for f in self.interim_fields[obj.UID()]:
                if f['keyword'] not in self.interim_columns and not f.get('hidden', False):
                    self.interim_columns[f['keyword']] = f['title']
                # and to the item itself
                items[i][f['keyword']] = f
                items[i]['class'][f['keyword']] = 'interim'

            # check if this analysis is late/overdue

            resultdate = obj.aq_parent.getDateSampled() \
                if obj.portal_type == 'ReferenceAnalysis' \
                else obj.getResultCaptureDate()

            duedate = obj.aq_parent.getExpiryDate() \
                if obj.portal_type == 'ReferenceAnalysis' \
                else obj.getDueDate()

            items[i]['replace']['DueDate'] = \
                self.ulocalized_time(duedate, long_format=1)

            if items[i]['review_state'] not in ['to_be_sampled',
                                                'to_be_preserved',
                                                'sample_due',
                                                'published']:

                if (resultdate and resultdate > duedate) \
                    or (not resultdate and DateTime() > duedate):

                    items[i]['replace']['DueDate'] = '%s <img width="16" height="16" src="%s/++resource++bika.lims.images/late.png" title="%s"/>' % \
                        (self.ulocalized_time(duedate, long_format=1),
                         self.portal_url,
                         t(_("Late Analysis")))

            # Submitting user may not verify results (admin can though)
            if items[i]['review_state'] == 'to_be_verified' and \
               not checkPermission(VerifyOwnResults, obj):
                user_id = getSecurityManager().getUser().getId()
                self_submitted = False
                try:
                    review_history = list(workflow.getInfoFor(obj, 'review_history'))
                    review_history.reverse()
                    for event in review_history:
                        if event.get('action') == 'submit':
                            if event.get('actor') == user_id:
                                self_submitted = True
                            break
                    if self_submitted:
                        items[i]['after']['state_title'] = \
                             "<img src='++resource++bika.lims.images/submitted-by-current-user.png' title='%s'/>" % \
                             (t(_("Cannot verify: Submitted by current user")))
                except WorkflowException:
                    pass

            # add icon for assigned analyses in AR views
            if self.context.portal_type == 'AnalysisRequest':
                obj = items[i]['obj']
                if obj.portal_type in ['ReferenceAnalysis',
                                       'DuplicateAnalysis'] or \
                   workflow.getInfoFor(obj, 'worksheetanalysis_review_state') == 'assigned':
                    br = obj.getBackReferences('WorksheetAnalysis')
                    if len(br) > 0:
                        ws = br[0]
                        items[i]['after']['state_title'] = \
                             "<a href='%s'><img src='++resource++bika.lims.images/worksheet.png' title='%s'/></a>" % \
                             (ws.absolute_url(),
                              t(_("Assigned to: ${worksheet_id}",
                                  mapping={'worksheet_id': safe_unicode(ws.id)})))

        # the TAL requires values for all interim fields on all
        # items, so we set blank values in unused cells
        for item in items:
            for field in self.interim_columns:
                if field not in item:
                    item[field] = ''

        # XXX order the list of interim columns
        interim_keys = self.interim_columns.keys()
        interim_keys.reverse()

        # add InterimFields keys to columns
        for col_id in interim_keys:
            if col_id not in self.columns:
                self.columns[col_id] = {'title': self.interim_columns[col_id],
                                        'input_width': '6',
                                        'input_class': 'ajax_calculate numeric',
                                        'sortable': False}

        if can_edit_analyses:
            new_states = []
            for state in self.review_states:
                # InterimFields are displayed in review_state
                # They are anyway available through View.columns though.
                # In case of hidden fields, the calcs.py should check calcs/services
                # for additional InterimFields!!
                pos = 'Result' in state['columns'] and \
                    state['columns'].index('Result') or len(state['columns'])
                for col_id in interim_keys:
                    if col_id not in state['columns']:
                        state['columns'].insert(pos, col_id)
                # retested column is added after Result.
                pos = 'Result' in state['columns'] and \
                    state['columns'].index('Uncertainty') + 1 or len(state['columns'])
                state['columns'].insert(pos, 'retested')
                new_states.append(state)
            self.review_states = new_states
            # Allow selecting individual analyses
            self.show_select_column = True

        # Dry Matter.
        # The Dry Matter column is never enabled for reference sample contexts
        # and refers to getReportDryMatter in ARs.
        if items and \
            (hasattr(self.context, 'getReportDryMatter') and \
             self.context.getReportDryMatter()):

            # look through all items
            # if the item's Service supports ReportDryMatter, add getResultDM().
            for item in items:
                if item['obj'].getService().getReportDryMatter():
                    item['ResultDM'] = item['obj'].getResultDM()
                else:
                    item['ResultDM'] = ''
                if item['ResultDM']:
                    item['after']['ResultDM'] = "<em class='discreet'>%</em>"

            # modify the review_states list to include the ResultDM column
            new_states = []
            for state in self.review_states:
                pos = 'Result' in state['columns'] and \
                    state['columns'].index('Uncertainty') + 1 or len(state['columns'])
                state['columns'].insert(pos, 'ResultDM')
                new_states.append(state)
            self.review_states = new_states

        self.categories.sort()

        # self.json_specs = json.dumps(self.specs)
        self.json_interim_fields = json.dumps(self.interim_fields)
        self.items = items

        # Method and Instrument columns must be shown or hidden at the
        # same time, because the value assigned to one causes
        # a value reassignment to the other (one method can be performed
        # by different instruments)
        self.columns['Method']['toggle'] = show_methodinstr_columns
        self.columns['Instrument']['toggle'] = show_methodinstr_columns

        return items
Example #52
0
    def __call__(self):
        form = self.request.form
        plone.protect.CheckAuthenticator(form)
        self.context = aq_inner(self.context)
        workflow = getToolByName(self.context, 'portal_workflow')
        checkPermission = self.context.portal_membership.checkPermission
        context = self.context
        context_url = context.absolute_url()

        # use came_from to decide which UI action was clicked.
        # "workflow_action" is the action name specified in the
        # portal_workflow transition url.
        came_from = "workflow_action"
        action = form.get(came_from, '')
        if not action and not form.get('bika_listing_filter_bar_submit', ''):
            # workflow_action_button is the action name specified in
            # the bika_listing_view table buttons.
            came_from = "workflow_action_button"
            action = form.get('workflow_action_id', '')
            if not action:
                if self.destination_url == "":
                    self.destination_url = self.request.get_header("referer",
                                                                   context_url)
                self.request.response.redirect(self.destination_url)
                return

        if action == "sample":
            objects = AnalysisRequestWorkflowAction._get_selected_items(self)
            transitioned = {'to_be_preserved': [], 'sample_due': []}
            dsfn = 'getDateSampled'
            for obj_uid, obj in objects.items():
                if obj.portal_type == "AnalysisRequest":
                    ar = obj
                    sample = obj.getSample()
                else:
                    # If it is a Sample, then fieldname is DateSampled
                    dsfn = 'DateSampled'
                    sample = obj
                    ar = sample.aq_parent
                # can't transition inactive items
                if workflow.getInfoFor(sample, 'inactive_state', '') == 'inactive':
                    continue

                # grab this object's Sampler and DateSampled from the form
                # (if the columns are available and edit controls exist)
                if 'getSampler' in form:
                    try:
                        Sampler = form['getSampler'][0][obj_uid].strip()
                    except KeyError:
                        continue
                    Sampler = Sampler and Sampler or ''
                    sample.setSampler(Sampler)
                    sample.reindexObject()

                if dsfn in form:
                    try:
                        DateSampled = form[dsfn][0][obj_uid].strip()
                    except KeyError:
                        continue
                    DateSampled = DateSampled and DateTime(DateSampled) or ''
                    sample.setDateSampled(DateSampled)
                    sample.reindexObject()

                # write them to the sample
                if not sample.getSampler():
                    # Make the message more specific if the reason for not
                    # transitioning is that no Sampler has been selected
                    message = _('Sampler is required for the Sampling transition of ${sample}',
                                mapping={'sample': sample.Title()})
                    self.context.plone_utils.addPortalMessage(message, 'info')
                    continue
                if not sample.getDateSampled():
                    continue
                Sampler = sample.getSampler()
                DateSampled = sample.getDateSampled()

                sample.reindexObject()
                ars = sample.getAnalysisRequests()
                # Analyses and AnalysisRequets have calculated fields
                # that are indexed; re-index all these objects.
                for ar in ars:
                    ar.reindexObject()
                    analyses = sample.getAnalyses({'review_state': 'to_be_sampled'})
                    for a in analyses:
                        a.getObject().reindexObject()

                # transition the object if both values are present
                if Sampler and DateSampled:
                    workflow.doActionFor(sample, action)
                    new_state = workflow.getInfoFor(sample, 'review_state')
                    doActionFor(ar, action)
                    transitioned[new_state].append(sample.Title())

            message = None
            for state in transitioned:
                tlist = transitioned[state]
                if len(tlist) > 1:
                    if state == 'to_be_preserved':
                        message = _('${items} are waiting for preservation.',
                                    mapping={'items': ', '.join(tlist)})
                    else:
                        message = _('${items} are waiting to be received.',
                                    mapping={'items': ', '.join(tlist)})
                    self.context.plone_utils.addPortalMessage(message, 'info')
                elif len(tlist) == 1:
                    if state == 'to_be_preserved':
                        message = _('${item} is waiting for preservation.',
                                    mapping={'item': ', '.join(tlist)})
                    else:
                        message = _('${item} is waiting to be received.',
                                    mapping={'item': ', '.join(tlist)})
                    self.context.plone_utils.addPortalMessage(message, 'info')

            if not message:
                message = _('No changes made.')
                self.context.plone_utils.addPortalMessage(message, 'info')
            self.destination_url = self.request.get_header("referer",
                                                           context_url)
            self.request.response.redirect(self.destination_url)

        elif action == "preserve":
            objects = AnalysisRequestWorkflowAction._get_selected_items(self)
            transitioned = {}
            not_transitioned = []
            Preserver = str()
            DatePreserved = str()
            for obj_uid, obj in objects.items():
                if obj.portal_type == "AnalysisRequest":
                    ar = obj
                    sample = obj.getSample()
                else:
                    sample = obj
                    ar = sample.aq_parent
                # can't transition inactive items
                if workflow.getInfoFor(sample, 'inactive_state', '') == 'inactive':
                    continue
                if not checkPermission(PreserveSample, sample):
                    continue

                # grab this object's Preserver and DatePreserved from the form
                # (if the columns are available and edit controls exist)
                if 'getPreserver' in form and 'getDatePreserved' in form:
                    try:
                        Preserver = form['getPreserver'][0][obj_uid].strip()
                        DatePreserved = form['getDatePreserved'][0][obj_uid].strip()
                    except KeyError:
                        continue
                    Preserver = Preserver and Preserver or ''
                    DatePreserved = DatePreserved and DateTime(DatePreserved) or ''
                else:
                    continue

                for sp in sample.objectValues("SamplePartition"):
                    if workflow.getInfoFor(sp, 'review_state') == 'to_be_preserved':
                        sp.setDatePreserved(DatePreserved)
                        sp.setPreserver(Preserver)
                for sp in sample.objectValues("SamplePartition"):
                    if workflow.getInfoFor(sp, 'review_state') == 'to_be_preserved':
                        if Preserver and DatePreserved:
                            doActionFor(sp, action)
                            transitioned[sp.aq_parent.Title()] = sp.Title()
                        else:
                            not_transitioned.append(sp)

            if len(transitioned.keys()) > 1:
                message = _('${items}: partitions are waiting to be received.',
                            mapping={'items': ', '.join(transitioned.keys())})
            else:
                message = _('${item}: ${part} is waiting to be received.',
                            mapping={'item': ', '.join(transitioned.keys()),
                                     'part': ', '.join(transitioned.values()), })
            self.context.plone_utils.addPortalMessage(message, 'info')

            # And then the sample itself
            if Preserver and DatePreserved and not not_transitioned:
                doActionFor(sample, action)
                # message = _('${item} is waiting to be received.',
                #            mapping = {'item': sample.Title()})
                # message = t(message)
                # self.context.plone_utils.addPortalMessage(message, 'info')

            self.destination_url = self.request.get_header(
                "referer", self.context.absolute_url())
            self.request.response.redirect(self.destination_url)

        elif action in ('prepublish', 'publish', 'republish'):
            # We pass a list of AR objects to Publish.
            # it returns a list of AR IDs which were actually published.
            objects = AnalysisRequestWorkflowAction._get_selected_items(self)
            its = []
            for uid, obj in objects.items():
                if isActive(obj):
                    its.append(uid)
            its = ",".join(its)
            q = "/publish?items=" + its
            self.destination_url = self.context.absolute_url() + q
            self.request.response.redirect(self.destination_url)

        else:
            AnalysisRequestWorkflowAction.__call__(self)
Example #53
0
    def workflow_action_retract_ar(self):
        workflow = getToolByName(self.context, 'portal_workflow')
        # AR should be retracted
        # Can't transition inactive ARs
        if not isActive(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'
        workflow.doActionFor(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.
        laboratory = self.context.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')

        message = _('${items} invalidated.',
                    mapping={'items': ar.getRequestID()})
        self.context.plone_utils.addPortalMessage(message, 'warning')
        self.request.response.redirect(newar.absolute_url())
Example #54
0
    def submit(self):
        """ Saves the form
        """

        form = self.request.form
        remarks = form.get('Remarks', [{}])[0]
        results = form.get('Result', [{}])[0]
        retested = form.get('retested', {})
        methods = form.get('Method', [{}])[0]
        instruments = form.get('Instrument', [{}])[0]
        analysts = self.request.form.get('Analyst', [{}])[0]
        uncertainties = self.request.form.get('Uncertainty', [{}])[0]
        dlimits = self.request.form.get('DetectionLimit', [{}])[0]
        selected = WorkflowAction._get_selected_items(self)
        workflow = getToolByName(self.context, 'portal_workflow')
        rc = getToolByName(self.context, REFERENCE_CATALOG)
        sm = getSecurityManager()

        hasInterims = {}
        # XXX combine data from multiple bika listing tables.
        item_data = {}
        if 'item_data' in form:
            if type(form['item_data']) == list:
                for i_d in form['item_data']:
                    for i, d in json.loads(i_d).items():
                        item_data[i] = d
            else:
                item_data = json.loads(form['item_data'])

        # Iterate for each selected analysis and save its data as needed
        for uid, analysis in selected.items():

            allow_edit = sm.checkPermission(EditResults, analysis)
            analysis_active = isActive(analysis)

            # Need to save remarks?
            if uid in remarks and allow_edit and analysis_active:
                analysis.setRemarks(remarks[uid])

            # Retested?
            if uid in retested and allow_edit and analysis_active:
                analysis.setRetested(retested[uid])

            # Need to save the instrument?
            if uid in instruments and analysis_active:
                # TODO: Add SetAnalysisInstrument permission
                # allow_setinstrument = sm.checkPermission(SetAnalysisInstrument)
                allow_setinstrument = True
                # ---8<-----
                if allow_setinstrument == True:
                    # The current analysis allows the instrument regards
                    # to its analysis service and method?
                    if (instruments[uid] == ''):
                        previnstr = analysis.getInstrument()
                        if previnstr:
                            previnstr.removeAnalysis(analysis)
                        analysis.setInstrument(None)
                    elif analysis.isInstrumentAllowed(instruments[uid]):
                        previnstr = analysis.getInstrument()
                        if previnstr:
                            previnstr.removeAnalysis(analysis)
                        analysis.setInstrument(instruments[uid])
                        instrument = analysis.getInstrument()
                        instrument.addAnalysis(analysis)
                        if analysis.meta_type == 'ReferenceAnalysis':
                            instrument.setDisposeUntilNextCalibrationTest(
                                False)

            # Need to save the method?
            if uid in methods and analysis_active:
                # TODO: Add SetAnalysisMethod permission
                # allow_setmethod = sm.checkPermission(SetAnalysisMethod)
                allow_setmethod = True
                # ---8<-----
                if allow_setmethod == True and analysis.isMethodAllowed(
                        methods[uid]):
                    analysis.setMethod(methods[uid])

            # Need to save the analyst?
            if uid in analysts and analysis_active:
                analysis.setAnalyst(analysts[uid])

            # Need to save the uncertainty?
            if uid in uncertainties and analysis_active:
                analysis.setUncertainty(uncertainties[uid])

            # Need to save the detection limit?
            if analysis_active and uid in dlimits and dlimits[uid]:
                analysis.setDetectionLimitOperand(dlimits[uid])

            # Need to save results?
            if uid in results and results[uid] and allow_edit \
                and analysis_active:
                interims = item_data.get(uid, [])
                analysis.setInterimFields(interims)
                analysis.setResult(results[uid])
                analysis.reindexObject()

                can_submit = True
                deps = analysis.getDependencies() \
                        if hasattr(analysis, 'getDependencies') else []
                for dependency in deps:
                    if workflow.getInfoFor(dependency, 'review_state') in \
                       ('to_be_sampled', 'to_be_preserved',
                        'sample_due', 'sample_received'):
                        can_submit = False
                        break
                if can_submit:
                    # doActionFor transitions the analysis to verif pending,
                    # so must only be done when results are submitted.
                    doActionFor(analysis, 'submit')

        # Maybe some analyses need to be retracted due to a QC failure
        # Done here because don't know if the last selected analysis is
        # a valid QC for the instrument used in previous analyses.
        # If we add this logic in subscribers.analyses, there's the
        # possibility to retract analyses before the QC being reached.
        self.retractInvalidAnalyses()

        message = PMF("Changes saved.")
        self.context.plone_utils.addPortalMessage(message, 'info')
        self.destination_url = self.request.get_header(
            "referer", self.context.absolute_url())
        self.request.response.redirect(self.destination_url)
Example #55
0
    def workflow_action_retract_ar(self):
        workflow = getToolByName(self.context, 'portal_workflow')
        # AR should be retracted
        # Can't transition inactive ARs
        if not isActive(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'
        workflow.doActionFor(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.
        laboratory = self.context.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 ''

        body = _("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}",
                 mapping={"request_link":aranchor,
                          "new_request_link":naranchor,
                          "remarks": addremarks,
                          "lab_address": lab_address})
        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')

        message = _('${items} invalidated.',
                    mapping={'items': ar.getRequestID()})
        self.context.plone_utils.addPortalMessage(message, 'warning')
        self.request.response.redirect(newar.absolute_url())
Example #56
0
    def __call__(self):
        # Do generic bika.lims stuff
        _AnalysisRequestWorkflowAction.__call__(self)
        # Do bika-health specific actions when submit
        action = _AnalysisRequestWorkflowAction._get_form_workflow_action(self)
        addPortalMessage = self.context.plone_utils.addPortalMessage
        if action[0] == 'submit' and isActive(self.context):
            inpanicanalyses = []
            workflow = getToolByName(self.context, 'portal_workflow')
            translate = self.context.translate
            rc = getToolByName(self.context, REFERENCE_CATALOG)
            uc = getToolByName(self.context, 'uid_catalog')
            # retrieve the results from database and check if
            # the values are exceeding panic levels
            alerts = {}
            for uid in self.request.form.get('Result', [{}])[0].keys():
                analysis = rc.lookupObject(uid)
                analysis = analysis.getObject() if hasattr(analysis, 'getObject') else analysis
                if not analysis:
                    continue
                astate = workflow.getInfoFor(analysis, 'review_state')
                if astate == 'retracted':
                    continue
                alerts.update(ResultOutOfRange(analysis)())
            if alerts:
                message = translate(_('Some results exceeded the '
                                      'panic levels that may '
                                      'indicate an imminent '
                                      'life-threatening condition'
                                      ))
                addPortalMessage(message, 'warning')
                self.request.response.redirect(self.context.absolute_url())

                # If panic levels alert email enabled, send an email to
                # labmanagers
                bs = self.context.bika_setup
                if hasattr(bs, 'getEnablePanicAlert') \
                        and bs.getEnablePanicAlert():
                    laboratory = self.context.bika_setup.laboratory
                    lab_address = "<br/>".join(laboratory.getPrintAddress())
                    managers = self.context.portal_groups.getGroupMembers('LabManagers')
                    mime_msg = MIMEMultipart('related')
                    mime_msg['Subject'] = _("Panic alert")
                    mime_msg['From'] = formataddr(
                        (encode_header(laboratory.getName()),
                         laboratory.getEmailAddress()))
                    to = []
                    for manager in managers:
                        user = self.portal.acl_users.getUser(manager)
                        uemail = user.getProperty('email')
                        ufull = user.getProperty('fullname')
                        to.append(formataddr((encode_header(ufull), uemail)))
                    mime_msg['To'] = ','.join(to)
                    strans = []
                    ars = {}
                    for analysis_uid, alertlist in alerts:
                        analysis = uc(analysis_uid).getObject()
                        for alert in alertlist:
                            ars[analysis.aq_parent.Title()] = 1
                            strans.append("- {0}, {1}: {2}".format(
                                          analysis.Title(),
                                          translate(_("Result")),
                                          analysis.getResult()))
                    ars = ", ".join(ars.keys())
                    stran = "<br/>".join(strans)
                    text = translate(_(
                        "Some results from ${items} exceeded the panic levels "
                        "that may indicate an imminent life-threatening "
                        "condition: <br/><br/>{analysisresults}<br/><br/>"
                        "<b>Please, check the Analysis Request if you "
                        "want to re-test the analysis or immediately "
                        "alert the client.</b><br/><br/>{lab_address}",
                        mapping={'items': ars,
                                 'analysisresults': stran,
                                 'lab_address': lab_address}))
                    msg_txt = MIMEText(safe_unicode(text).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:
                        ar = inpanicanalyses[0].getRequestID()
                        logger.error("Panic level email %s: %s" %
                                     (ar, str(msg)))
                        message = translate(
                            _('Unable to send an email to alert lab '
                              'managers that some analyses exceeded the '
                              'panic levels') + (": %s" % str(msg)))
                        addPortalMessage(message, 'warning')
Example #57
0
    def folderitems(self):
        rc = getToolByName(self.context, REFERENCE_CATALOG)
        bsc = getToolByName(self.context, 'bika_setup_catalog')
        workflow = getToolByName(self.context, 'portal_workflow')
        portal = getToolByName(self.context, 'portal_url').getPortalObject()
        translate = self.context.translation_service.translate
        mtool = getToolByName(self.context, 'portal_membership')
        checkPermission = mtool.checkPermission
        if not self.allow_edit:
            can_edit_analyses = False
        else:
            if self.contentFilter.get('getPointOfCapture', '') == 'field':
                can_edit_analyses = checkPermission(EditFieldResults,
                                                    self.context)
            else:
                can_edit_analyses = checkPermission(EditResults, self.context)

        context_active = isActive(self.context)

        items = super(AnalysesView, self).folderitems(full_objects=True)

        self.interim_fields = {}
        self.interim_columns = {}
        self.specs = {}
        for i, item in enumerate(items):
            # self.contentsMethod may return brains or objects.
            obj = hasattr(items[i]['obj'], 'getObject') and \
                items[i]['obj'].getObject() or \
                items[i]['obj']

            result = obj.getResult()
            service = obj.getService()
            calculation = service.getCalculation()
            unit = service.getUnit()
            keyword = service.getKeyword()
            precision = service.getPrecision()

            # Check for InterimFields attribute on our object,
            interim_fields = hasattr(obj, 'getInterimFields') \
                and obj.getInterimFields() or []
            self.interim_fields[obj.UID()] = interim_fields

            items[i]['Keyword'] = keyword
            items[i]['Unit'] = unit and unit or ''
            items[i]['Result'] = ''
            items[i]['formatted_result'] = ''
            items[i]['interim_fields'] = interim_fields
            items[i]['Uncertainty'] = ''
            items[i]['retested'] = obj.getRetested()
            items[i]['class']['retested'] = 'center'
            items[i]['calculation'] = calculation and True or False
            items[i]['Partition'] = obj.getSamplePartition().Title()
            if obj.portal_type == "ReferenceAnalysis":
                items[i]['DueDate'] = ''
                items[i]['CaptureDate'] = ''
            else:
                items[i]['DueDate'] = obj.getDueDate()
                cd = obj.getResultCaptureDate()
                items[i]['CaptureDate'] = cd and TimeOrDate(self.context,
                                                            cd) or ''
            items[i]['Attachments'] = ''

            # calculate specs
            if obj.portal_type == 'ReferenceAnalysis':
                items[i]['st_uid'] = obj.aq_parent.UID()
            else:
                if self.context.portal_type == 'AnalysisRequest':
                    sample = self.context.getSample()
                    st_uid = sample.getSampleType().UID()
                    items[i]['st_uid'] = st_uid
                    if st_uid not in self.specs:
                        proxies = bsc(portal_type='AnalysisSpec',
                                      getSampleTypeUID=st_uid)
                elif self.context.portal_type == "Worksheet":
                    if obj.portal_type == "DuplicateAnalysis":
                        sample = obj.getAnalysis().getSample()
                    else:
                        sample = obj.aq_parent.getSample()
                    st_uid = sample.getSampleType().UID()
                    items[i]['st_uid'] = st_uid
                    if st_uid not in self.specs:
                        proxies = bsc(portal_type='AnalysisSpec',
                                      getSampleTypeUID=st_uid)
                elif self.context.portal_type == 'Sample':
                    st_uid = self.context.getSampleType().UID()
                    items[i]['st_uid'] = st_uid
                    if st_uid not in self.specs:
                        proxies = bsc(portal_type='AnalysisSpec',
                                      getSampleTypeUID=st_uid)
                else:
                    proxies = []
                if st_uid not in self.specs:
                    for spec in (p.getObject() for p in proxies):
                        client_or_lab = ""
                        if spec.getClientUID() == obj.getClientUID():
                            client_or_lab = 'client'
                        elif spec.getClientUID(
                        ) == self.context.bika_setup.bika_analysisspecs.UID():
                            client_or_lab = 'lab'
                        else:
                            continue
                        for keyword, results_range in \
                            spec.getResultsRangeDict().items():
                            # hidden form field 'specs' keyed by sampletype uid:
                            # {st_uid: {'lab/client':{keyword:{min,max,error}}}}
                            if st_uid in self.specs:
                                if client_or_lab in self.specs[st_uid]:
                                    self.specs[st_uid][client_or_lab][
                                        keyword] = results_range
                                else:
                                    self.specs[st_uid][client_or_lab] = {
                                        keyword: results_range
                                    }
                            else:
                                self.specs[st_uid] = {
                                    client_or_lab: {
                                        keyword: results_range
                                    }
                                }

            method = service.getMethod()
            items[i]['Method'] = method and method.Title() or ''
            if method:
                items[i]['replace']['Method'] = "<a href='%s'>%s</a>" % \
                    (method.absolute_url(), method.Title())

            # Show version number of out-of-date objects
            if checkPermission(ManageBika, self.context):
                service_uid = service.UID()
                latest = rc.lookupObject(service_uid).version_id
                items[i]['Service'] = service.Title()
                items[i]['class']['Service'] = "service_title"
                if hasattr(obj, 'reference_versions') and \
                   service_uid in obj.reference_versions and \
                   latest != obj.reference_versions[service_uid]:
                    items[i]['after']['Service'] = "(v%s)" % \
                         (obj.reference_versions[service_uid])

            # choices defined on Service apply to result fields.
            choices = service.getResultOptions()
            if choices:
                items[i]['choices'] = {'Result': choices}

            # permission to view this item's results
            can_view_result = \
                getSecurityManager().checkPermission(ViewResults, obj)

            # permission to edit this item's results
            # Editing Field Results is possible while in Sample Due.
            poc = self.contentFilter.get("getPointOfCapture", 'lab')
            can_edit_analysis = self.allow_edit and context_active and \
                ( (poc == 'field' and getSecurityManager().checkPermission(EditFieldResults, obj))
                  or
                  (poc != 'field' and getSecurityManager().checkPermission(EditResults, obj)) )

            if can_edit_analysis:
                items[i]['allow_edit'] = [
                    'Result',
                ]
                # if the Result field is editable, our interim fields are too
                for f in self.interim_fields[obj.UID()]:
                    items[i]['allow_edit'].append(f['keyword'])

                # if there isn't a calculation then result must be re-testable,
                # and if there are interim fields, they too must be re-testable.
                if not items[i]['calculation'] or \
                   (items[i]['calculation'] and self.interim_fields[obj.UID()]):
                    items[i]['allow_edit'].append('retested')

            # Only display data bearing fields if we have ViewResults
            # permission, otherwise just put an icon in Result column.
            if can_view_result:
                items[i]['Result'] = result
                items[i]['formatted_result'] = result
                if result != '':
                    if 'Result' in items[i]['choices'] and items[i]['choices'][
                            'Result']:
                        items[i]['formatted_result'] = \
                            [r['ResultText'] for r in items[i]['choices']['Result'] \
                                              if str(r['ResultValue']) == str(result)][0]
                    else:
                        try:
                            items[i]['formatted_result'] = precision and \
                                str("%%.%sf" % precision) % float(result) or result
                        except:
                            items[i]['formatted_result'] = result
                            indet = translate(_('Indet'))
                            if result == indet:
                                # 'Indeterminate' results flag a specific error
                                Indet = translate(_("Indeterminate result"))
                                items[i]['after']['Result'] = \
                                    '<img width="16" height="16" title="%s"' % Indet + \
                                    'src="%s/++resource++bika.lims.images/exclamation.png"/>' % \
                                    (portal.absolute_url())
                            else:
                                # result being un-floatable, is an error.
                                msg = translate(_("Invalid result"))
                                items[i]['after']['Result'] = \
                                    '<img width="16" height="16" title="%s"' % msg + \
                                    'src="%s/++resource++bika.lims.images/exclamation.png"/>' % \
                                    (portal.absolute_url())
                items[i]['Uncertainty'] = obj.getUncertainty(result)

                attachments = ""
                if hasattr(obj, 'getAttachment'):
                    for attachment in obj.getAttachment():
                        af = attachment.getAttachmentFile()
                        icon = af.getBestIcon()
                        attachments += "<span class='attachment' attachment_uid='%s'>" % (
                            attachment.UID())
                        if icon:
                            attachments += "<img src='%s/%s'/>" % (
                                portal.absolute_url(), icon)
                        attachments += '<a href="%s/at_download/AttachmentFile"/>%s</a>' % (
                            attachment.absolute_url(), af.filename)
                        attachments += "<img class='deleteAttachmentButton' attachment_uid='%s' src='%s'/>" % (
                            attachment.UID(),
                            "++resource++bika.lims.images/delete.png")
                        attachments += "</br></span>"
                items[i]['replace'][
                    'Attachments'] = attachments[:-12] + "</span>"

                if hasattr(obj, 'result_in_range'):
                    items[i]['result_in_range'] = obj.result_in_range(
                        result, self.chosen_spec)
                else:
                    items[i]['result_in_range'] = (True, None)

            if not can_view_result or \
               (not items[i]['Result'] and not can_edit_analysis):
                if 'Result' in items[i]['allow_edit']:
                    items[i]['allow_edit'].remove('Result')
                items[i]['before']['Result'] = \
                    '<img width="16" height="16" ' + \
                    'src="%s/++resource++bika.lims.images/to_follow.png"/>' % \
                    (portal.absolute_url())

            # Add this analysis' interim fields to the interim_columns list
            for f in self.interim_fields[obj.UID()]:
                if f['keyword'] not in self.interim_columns:
                    self.interim_columns[f['keyword']] = f['title']
                # and to the item itself
                items[i][f['keyword']] = f
                items[i]['class'][f['keyword']] = 'interim'

            # check if this analysis is late/overdue
            if items[i]['obj'].portal_type != "DuplicateAnalysis":
                if (not calculation or (calculation and not calculation.getDependentServices())) and \
                   items[i]['review_state'] not in ['to_be_sampled', 'to_be_preserved', 'sample_due', 'published'] and \
                   items[i]['DueDate'] < DateTime():
                    DueDate = TimeOrDate(self.context,
                                         item['DueDate'],
                                         long_format=0)
                    if self.context.portal_type == 'AnalysisRequest':
                        items[i]['replace']['DueDate'] = '%s <img width="16" height="16" src="%s/++resource++bika.lims.images/late.png" title="%s"/>' % \
                            (DueDate, portal.absolute_url(),
                             translate(_("Due Date")) + ": %s"%DueDate)
                    else:
                        items[i]['replace']['DueDate'] = '%s <img width="16" height="16" src="%s/++resource++bika.lims.images/late.png" title="%s"/>' % \
                            (DueDate, portal.absolute_url(),
                             translate(_("Late Analysis")))
                else:
                    items[i]['replace']['DueDate'] = TimeOrDate(
                        self.context, item['DueDate'])

            # add icon for assigned analyses in AR views
            if self.context.portal_type == 'AnalysisRequest' and \
               workflow.getInfoFor(items[i]['obj'], 'worksheetanalysis_review_state') == 'assigned':
                ws = items[i]['obj'].getBackReferences('WorksheetAnalysis')[0]
                items[i]['after']['state_title'] = \
                     "<a href='%s'><img src='++resource++bika.lims.images/worksheet.png' title='Assigned: %s'/></a>" % \
                     (ws.absolute_url(), ws.id)

        # the TAL requires values for all interim fields on all
        # items, so we set blank values in unused cells
        for item in items:
            for field in self.interim_columns:
                if field not in item:
                    item[field] = ''

        # XXX order the list of interim columns
        interim_keys = self.interim_columns.keys()
        interim_keys.reverse()

        # add InterimFields keys to columns
        for col_id in interim_keys:
            if col_id not in self.columns:
                self.columns[col_id] = {
                    'title': self.interim_columns[col_id],
                    'input_width': '6',
                    'input_class': 'ajax_calculate numeric',
                    'sortable': False
                }

        if can_edit_analyses:
            new_states = []
            for state in self.review_states:
                # InterimFields are displayed in review_state
                # They are anyway available through View.columns though.
                pos = 'Result' in state['columns'] and \
                    state['columns'].index('Result') or len(state['columns'])
                for col_id in interim_keys:
                    if col_id not in state['columns']:
                        state['columns'].insert(pos, col_id)
                # retested column is added after Result.
                pos = 'Result' in state['columns'] and \
                    state['columns'].index('Uncertainty') + 1 or len(state['columns'])
                state['columns'].insert(pos, 'retested')
                new_states.append(state)
            self.review_states = new_states
            # Allow selecting individual analyses
            self.show_select_column = True

        # Dry Matter.
        # The Dry Matter column is always enabled for worksheets,
        # never enabled for reference sample contexts, and refers to
        # getReportDryMatter in ARs.
        # XXX It should be enabled only if any of the ARs present asked for DM.
        if items and \
           (self.context.portal_type == 'Worksheet' or \
            (hasattr(self.context, 'getReportDryMatter') and \
             self.context.getReportDryMatter())):

            # look through all items
            # if the item's Service supports ReportDryMatter, add getResultDM().
            for item in items:
                if item['obj'].getService().getReportDryMatter():
                    item['ResultDM'] = item['obj'].getResultDM()
                else:
                    item['ResultDM'] = ''
                if item['ResultDM']:
                    item['after']['ResultDM'] = "<em class='discreet'>%</em>"

            # modify the review_states list to include the ResultDM column
            new_states = []
            for state in self.review_states:
                pos = 'Result' in state['columns'] and \
                    state['columns'].index('Uncertainty') + 1 or len(state['columns'])
                state['columns'].insert(pos, 'ResultDM')
                new_states.append(state)
            self.review_states = new_states

        self.json_specs = json.dumps(self.specs)
        self.json_interim_fields = json.dumps(self.interim_fields)

        return items
Example #58
0
    def __call__(self):
        # Do generic bika.lims stuff
        BaseClass.__call__(self)
        # Do bika-health specific actions when submit
        action = BaseClass._get_form_workflow_action(self)
        addPortalMessage = self.context.plone_utils.addPortalMessage
        if action[0] == 'submit' and isActive(self.context):
            inpanicanalyses = []
            workflow = getToolByName(self.context, 'portal_workflow')
            translate = self.context.translate
            rc = getToolByName(self.context, REFERENCE_CATALOG)
            uc = getToolByName(self.context, 'uid_catalog')
            # retrieve the results from database and check if
            # the values are exceeding panic levels
            alerts = {}
            for uid in self.request.form.get('Result', [{}])[0].keys():
                analysis = rc.lookupObject(uid)
                analysis = analysis.getObject() if hasattr(analysis, 'getObject') else analysis
                if not analysis:
                    continue
                astate = workflow.getInfoFor(analysis, 'review_state')
                if astate == 'retracted':
                    continue
                alerts.update(ResultOutOfRange(analysis)())
            if alerts:
                message = translate(_('Some results exceeded the '
                                      'panic levels that may '
                                      'indicate an imminent '
                                      'life-threatening condition'
                                      ))
                addPortalMessage(message, 'warning')
                self.request.response.redirect(self.context.absolute_url())

                # If panic levels alert email enabled, send an email to
                # labmanagers
                bs = self.context.bika_setup
                if hasattr(bs, 'getEnablePanicAlert') \
                        and bs.getEnablePanicAlert():
                    laboratory = self.context.bika_setup.laboratory
                    lab_address = "<br/>".join(laboratory.getPrintAddress())
                    managers = self.context.portal_groups.getGroupMembers('LabManagers')
                    mime_msg = MIMEMultipart('related')
                    mime_msg['Subject'] = _("Panic alert")
                    mime_msg['From'] = formataddr(
                        (encode_header(laboratory.getName()),
                         laboratory.getEmailAddress()))
                    to = []
                    for manager in managers:
                        user = self.portal.acl_users.getUser(manager)
                        uemail = user.getProperty('email')
                        ufull = user.getProperty('fullname')
                        to.append(formataddr((encode_header(ufull), uemail)))
                    mime_msg['To'] = ','.join(to)
                    strans = []
                    ars = {}
                    for analysis_uid, alertlist in alerts:
                        analysis = uc(analysis_uid).getObject()
                        for alert in alertlist:
                            ars[analysis.aq_parent.Title()] = 1
                            strans.append("- {0}, {1}: {2}".format(
                                          analysis.getService().Title(),
                                          translate(_("Result")),
                                          analysis.getResult()))
                    ars = ", ".join(ars.keys())
                    stran = "<br/>".join(strans)
                    text = translate(_(
                        "Some results from ${items} exceeded the panic levels "
                        "that may indicate an imminent life-threatening "
                        "condition: <br/><br/>{analysisresults}<br/><br/>"
                        "<b>Please, check the Analysis Request if you "
                        "want to re-test the analysis or immediately "
                        "alert the client.</b><br/><br/>{lab_address}",
                        mapping={'items': ars,
                                 'analysisresults': stran,
                                 'lab_address': lab_address}))
                    msg_txt = MIMEText(safe_unicode(text).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:
                        ar = inpanicanalyses[0].getRequestID()
                        logger.error("Panic level email %s: %s" %
                                     (ar, str(msg)))
                        message = translate(
                            _('Unable to send an email to alert lab '
                              'managers that some analyses exceeded the '
                              'panic levels') + (": %s" % str(msg)))
                        addPortalMessage(message, 'warning')
Example #59
0
    def __call__(self):
        form = self.request.form
        plone.protect.CheckAuthenticator(form)
        workflow = getToolByName(self.context, 'portal_workflow')
        rc = getToolByName(self.context, REFERENCE_CATALOG)
        bsc = getToolByName(self.context, 'bika_setup_catalog')
        bac = getToolByName(self.context, 'bika_analysis_catalog')
        action, came_from = WorkflowAction._get_form_workflow_action(self)

        # XXX combine data from multiple bika listing tables.
        item_data = {}
        if 'item_data' in form:
            if type(form['item_data']) == list:
                for i_d in form['item_data']:
                    for i, d in json.loads(i_d).items():
                        item_data[i] = d
            else:
                item_data = json.loads(form['item_data'])

        if action == 'submit' and self.request.form.has_key("Result"):
            selected_analyses = WorkflowAction._get_selected_items(self)
            results = {}
            hasInterims = {}

            # first save results for entire form
            for uid, result in self.request.form['Result'][0].items():
                if uid in selected_analyses:
                    analysis = selected_analyses[uid]
                else:
                    analysis = rc.lookupObject(uid)
                if not analysis:
                    # ignore result if analysis object no longer exists
                    continue
                if not(getSecurityManager().checkPermission(EditResults, analysis)):
                    # or changes no longer allowed
                    continue
                if not isActive(analysis):
                    # or it's cancelled
                    continue
                results[uid] = result
                service = analysis.getService()
                interimFields = item_data[uid]
                if len(interimFields) > 0:
                    hasInterims[uid] = True
                else:
                    hasInterims[uid] = False
                unit = service.getUnit()
                analysis.edit(
                    Result = result,
                    InterimFields = interimFields,
                    Retested = form.has_key('retested') and \
                               form['retested'].has_key(uid),
                    Unit = unit and unit or '')

            # discover which items may be submitted
            submissable = []
            for uid, analysis in selected_analyses.items():
                if uid not in results:
                    continue
                can_submit = True
                if hasattr(analysis, 'getDependencies'):
                    dependencies = analysis.getDependencies()
                    for dependency in dependencies:
                        dep_state = workflow.getInfoFor(dependency, 'review_state')
                        if hasInterims[uid]:
                            if dep_state in ('to_be_sampled', 'to_be_preserved',
                                             'sample_due', 'sample_received',
                                             'attachment_due', 'to_be_verified',):
                                can_submit = False
                                break
                        else:
                            if dep_state in ('to_be_sampled', 'to_be_preserved',
                                             'sample_due', 'sample_received',):
                                can_submit = False
                                break
                    for dependency in dependencies:
                        if workflow.getInfoFor(dependency, 'review_state') in \
                           ('to_be_sampled', 'to_be_preserved',
                            'sample_due', 'sample_received'):
                            can_submit = False
                if can_submit:
                    submissable.append(analysis)

            # and then submit them.
            for analysis in submissable:
                doActionFor(analysis, 'submit')

            message = PMF("Changes saved.")
            self.context.plone_utils.addPortalMessage(message, 'info')
            self.destination_url = self.request.get_header("referer",
                                   self.context.absolute_url())
            self.request.response.redirect(self.destination_url)
        ## assign
        elif action == 'assign':
            if not(getSecurityManager().checkPermission(EditWorksheet, self.context)):
                self.request.response.redirect(self.context.absolute_url())
                return

            selected_analyses = WorkflowAction._get_selected_items(self)
            selected_analysis_uids = selected_analyses.keys()

            if selected_analyses:
                for uid in selected_analysis_uids:
                    analysis = rc.lookupObject(uid)
                    # Double-check the state first
                    if (workflow.getInfoFor(analysis, 'worksheetanalysis_review_state') == 'unassigned'
                    and workflow.getInfoFor(analysis, 'review_state') == 'sample_received'
                    and workflow.getInfoFor(analysis, 'cancellation_state') == 'active'):
                        self.context.addAnalysis(analysis)

            self.destination_url = self.context.absolute_url()
            self.request.response.redirect(self.destination_url)
        ## unassign
        elif action == 'unassign':
            if not(getSecurityManager().checkPermission(EditWorksheet, self.context)):
                self.request.response.redirect(self.context.absolute_url())
                return

            selected_analyses = WorkflowAction._get_selected_items(self)
            selected_analysis_uids = selected_analyses.keys()

            for analysis_uid in selected_analysis_uids:
                try:
                    analysis = bac(UID=analysis_uid)[0].getObject()
                except IndexError:
                    # Duplicate analyses are removed when their analyses
                    # get removed, so indexerror is expected.
                    continue
                if skip(analysis, action, peek=True):
                    continue
                self.context.removeAnalysis(analysis)

            self.destination_url = self.context.absolute_url()
            self.request.response.redirect(self.destination_url)
        ## verify
        elif action == 'verify':
            # default bika_listing.py/WorkflowAction, but then go to view screen.
            self.destination_url = self.context.absolute_url()
            WorkflowAction.__call__(self)
        else:
            # default bika_listing.py/WorkflowAction for other transitions
            WorkflowAction.__call__(self)