Beispiel #1
0
    def __call__(self):
        ar = self.context
        workflow = getToolByName(ar, 'portal_workflow')

        # If is a retracted AR, show the link to child AR and show a warn msg
        if workflow.getInfoFor(ar, 'review_state') == 'invalid':
            childar = hasattr(ar, 'getChildAnalysisRequest') \
                        and ar.getChildAnalysisRequest() or None
            childid = childar and childar.getRequestID() or None
            message = _('This Analysis Request has been withdrawn and is shown '
                        'for trace-ability purposes only. Retest: ${retest_child_id}.',
                        mapping={"retest_child_id":childid if childid else ''})
            self.context.plone_utils.addPortalMessage(message, 'warning')

        # If is an AR automatically generated due to a Retraction, show it's
        # parent AR information
        if hasattr(ar, 'getParentAnalysisRequest') \
            and ar.getParentAnalysisRequest():
            par = ar.getParentAnalysisRequest()
            message = _(
                'This Analysis Request has been generated automatically due to '
                'the retraction of the Analysis Request ${retracted_request_id}.',
                mapping={"retracted_request_id": par.getRequestID()})
            self.context.plone_utils.addPortalMessage(message, 'info')

        can_do = getSecurityManager().checkPermission(ResultsNotRequested, ar)
        if workflow.getInfoFor(ar, 'cancellation_state') == "cancelled":
            self.request.response.redirect(ar.absolute_url())
        elif not(can_do):
            self.request.response.redirect(ar.absolute_url())
        else:
            return self.template()
Beispiel #2
0
    def __call__(self):
        ar = self.context
        workflow = getToolByName(ar, 'portal_workflow')

        # If is a retracted AR, show the link to child AR and show a warn msg
        if workflow.getInfoFor(ar, 'review_state') == 'invalid':
            childar = hasattr(ar, 'getChildAnalysisRequest') \
                        and ar.getChildAnalysisRequest() or None
            childid = childar and childar.getRequestID() or None
            message = _(
                'This Analysis Request has been withdrawn and is shown '
                'for trace-ability purposes only. Retest: ${retest_child_id}.',
                mapping={"retest_child_id": childid if childid else ''})
            self.context.plone_utils.addPortalMessage(message, 'warning')

        # If is an AR automatically generated due to a Retraction, show it's
        # parent AR information
        if hasattr(ar, 'getParentAnalysisRequest') \
            and ar.getParentAnalysisRequest():
            par = ar.getParentAnalysisRequest()
            message = _(
                'This Analysis Request has been generated automatically due to '
                'the retraction of the Analysis Request ${retracted_request_id}.',
                mapping={"retracted_request_id": par.getRequestID()})
            self.context.plone_utils.addPortalMessage(message, 'info')

        can_do = getSecurityManager().checkPermission(ResultsNotRequested, ar)
        if workflow.getInfoFor(ar, 'cancellation_state') == "cancelled":
            self.request.response.redirect(ar.absolute_url())
        elif not (can_do):
            self.request.response.redirect(ar.absolute_url())
        else:
            return self.template()
Beispiel #3
0
    def render_field_view(self, field):
        fieldname = field.getName()
        field = self.context.Schema()[fieldname]
        ret = {'fieldName': fieldname, 'mode': 'view'}
        try:
            adapter = getAdapter(self.context,
                                 interface=IHeaderTableFieldRenderer,
                                 name=fieldname)
        except ComponentLookupError:
            adapter = None
        if adapter:
            ret = {'fieldName': fieldname,
                   'mode': 'structure',
                   'html': adapter(field)}
        else:
            if field.getType().find("ool") > -1:
                value = field.get(self.context)
                ret = {'fieldName': fieldname,
                       'mode': 'structure',
                       'html': t(_('Yes')) if value else t(_('No'))
                }
            elif field.getType().find("Reference") > -1:
                # Prioritize method retrieval over schema's field
                targets = None
                if hasattr(self.context, 'get%s' % fieldname):
                    fieldaccessor = getattr(self.context, 'get%s' % fieldname)
                    if callable(fieldaccessor):
                        targets = fieldaccessor()
                if not targets:
                    targets = field.get(self.context)

                if targets:
                    if not type(targets) == list:
                        targets = [targets,]
                    sm = getSecurityManager()
                    if all([sm.checkPermission(view, ta) for ta in targets]):
                        a = ["<a href='%s'>%s</a>" % (target.absolute_url(),
                                                      target.Title())
                             for target in targets]
                        ret = {'fieldName': fieldname,
                               'mode': 'structure',
                               'html': ", ".join(a)}
                    else:
                        ret = {'fieldName': fieldname,
                               'mode': 'structure',
                               'html': ", ".join([ta.Title() for ta in targets])}
                else:
                    ret = {'fieldName': fieldname,
                           'mode': 'structure',
                           'html': ''}
            elif field.getType().lower().find('datetime') > -1:
                value = field.get(self.context)
                ret = {'fieldName': fieldname,
                       'mode': 'structure',
                       'html': self.ulocalized_time(value, long_format=True)
                }
        return ret
Beispiel #4
0
    def __call__(self):
        ar = self.context
        workflow = getToolByName(ar, 'portal_workflow')
        if workflow.getInfoFor(ar, 'cancellation_state') == "cancelled":
            self.request.response.redirect(ar.absolute_url())
        elif not (getSecurityManager().checkPermission(EditResults, ar)):
            self.request.response.redirect(ar.absolute_url())
        else:
            self.tables = {}
            show_cats = self.context.bika_setup.getCategoriseAnalysisServices()
            for poc in POINTS_OF_CAPTURE:
                if self.context.getAnalyses(getPointOfCapture=poc):
                    t = self.createAnalysesView(ar,
                                                self.request,
                                                getPointOfCapture=poc,
                                                sort_on='getServiceTitle',
                                                show_categories=show_cats)
                    t.form_id = "ar_manage_results_%s" % poc
                    t.allow_edit = True
                    t.review_states[0]['transitions'] = [{
                        'id': 'submit'
                    }, {
                        'id': 'retract'
                    }, {
                        'id': 'verify'
                    }]
                    t.show_select_column = True
                    poc_value = POINTS_OF_CAPTURE.getValue(poc)
                    self.tables[poc_value] = t.contents_table()
            # If a general retracted is done, rise a waring
            if workflow.getInfoFor(ar, 'review_state') == 'sample_received':
                allstatus = list()
                for analysis in ar.getAnalyses():
                    status = workflow.getInfoFor(analysis.getObject(),
                                                 'review_state')
                    if status not in [
                            'retracted', 'to_be_verified', 'verified'
                    ]:
                        allstatus = []
                        break
                    else:
                        allstatus.append(status)
                if len(allstatus) > 0:
                    message = "General Retract Done"
                    self.context.plone_utils.addPortalMessage(
                        message, 'warning')

            self.checkInstrumentsValidity()
            return self.template()
Beispiel #5
0
    def __call__(self):
        CheckAuthenticator(self.request)
        field = self.context.Schema()["Remarks"]
        value = self.request['value'].strip() + "\n\n"
        existing = self.context.getRemarks(mimetype='text/x-web-intelligent').strip()

        date = DateTime().rfc822()
        user = getSecurityManager().getUser()
        divider = "=== %s (%s)\n" % (date, user)

        remarks = convertWebIntelligentPlainTextToHtml(divider) + \
            convertWebIntelligentPlainTextToHtml(value) + \
            convertWebIntelligentPlainTextToHtml(existing)

        self.context.setRemarks(divider + value + existing, mimetype='text/x-web-intelligent')

        return remarks.strip()
Beispiel #6
0
    def __call__(self):
        CheckAuthenticator(self.request)
        field = self.context.Schema()["Remarks"]
        value = self.request['value'].strip() + "\n\n"
        existing = self.context.getRemarks(
            mimetype='text/x-web-intelligent').strip()

        date = DateTime().rfc822()
        user = getSecurityManager().getUser()
        divider = "=== %s (%s)\n" % (date, user)

        remarks = convertWebIntelligentPlainTextToHtml(divider) + \
            convertWebIntelligentPlainTextToHtml(value) + \
            convertWebIntelligentPlainTextToHtml(existing)

        self.context.setRemarks(divider + value + existing,
                                mimetype='text/x-web-intelligent')

        return remarks.strip()
Beispiel #7
0
    def __call__(self):
        ar = self.context
        workflow = getToolByName(ar, 'portal_workflow')
        if workflow.getInfoFor(ar, 'cancellation_state') == "cancelled":
            self.request.response.redirect(ar.absolute_url())
        elif not(getSecurityManager().checkPermission(EditResults, ar)):
            self.request.response.redirect(ar.absolute_url())
        else:
            self.tables = {}
            show_cats = self.context.bika_setup.getCategoriseAnalysisServices()
            for poc in POINTS_OF_CAPTURE:
                if self.context.getAnalyses(getPointOfCapture=poc):
                    t = self.createAnalysesView(ar,
                                     self.request,
                                     getPointOfCapture=poc,
                                     sort_on='getServiceTitle',
                                     show_categories=show_cats)
                    t.form_id = "ar_manage_results_%s" % poc
                    t.allow_edit = True
                    t.review_states[0]['transitions'] = [{'id': 'submit'},
                                                         {'id': 'retract'},
                                                         {'id': 'verify'}]
                    t.show_select_column = True
                    poc_value = POINTS_OF_CAPTURE.getValue(poc)
                    self.tables[poc_value] = t.contents_table()
            # If a general retracted is done, rise a waring
            if workflow.getInfoFor(ar, 'review_state') == 'sample_received':
                allstatus = list()
                for analysis in ar.getAnalyses():
                    status = workflow.getInfoFor(analysis.getObject(), 'review_state')
                    if status not in ['retracted','to_be_verified','verified']:
                        allstatus = []
                        break
                    else:
                        allstatus.append(status)
                if len(allstatus) > 0:
                    message = "General Retract Done"
                    self.context.plone_utils.addPortalMessage(message, 'warning')

            self.checkInstrumentsValidity()
            return self.template()
# Can't do anything to the object if it's cancelled
# reference and duplicate analyses don't have cancellation_state
#if context.portal_type == "Analysis":
if workflow.getInfoFor(context, 'cancellation_state', 'active') == "cancelled":
    return False

# All kinds of analyses get their submitter and verifier compared
if context.portal_type in ("ReferenceAnalysis", "DuplicateAnalysis"):

    # May we verify results that we ourself submitted?
    if checkPermission(VerifyOwnResults, context):
        return True

    # Check for self-submitted Analysis.
    user_id = getSecurityManager().getUser().getId()
    self_submitted = False
    review_history = workflow.getInfoFor(context, 'review_history')
    review_history = context.reverseList(review_history)
    for event in review_history:
        if event.get('action') == 'submit':
            if event.get('actor') == user_id:
                self_submitted = True
            break
    if self_submitted:
        return False

if context.portal_type == "AnalysisRequest":

    if not checkPermission(Verify, context):
        # Allow automatic verify (Disregard permission)
Beispiel #9
0
    def folderitems(self):
        """Accumulate a list of all AnalysisRequest objects contained in
        this Batch, as well as those which are inherited.
        """
        wf = getToolByName(self.context, 'portal_workflow')
        schema = self.context.Schema()

        ars = []

        for o in schema.getField('InheritedObjects').get(self.context):
            if o.portal_type == 'AnalysisRequest':
                if o not in ars:
                    ars.append(o)
            elif o.portal_type == 'Batch':
                for ar in o.getAnalysisRequests():
                    if ar not in ars:
                        ars.append(ar)

        for ar in self.context.getAnalysisRequests():
            if ar not in ars:
                ars.append(ar)

        self.categories = []
        analyses = {}
        items = []
        services = []
        keywords = []
        for ar in ars:
            analyses[ar.id] = []
            for analysis in ar.getAnalyses(full_objects=True):
                analyses[ar.id].append(analysis)
                service = analysis.getService()
                if service.getKeyword() not in keywords:
                    # we use a keyword check, because versioned services are !=.
                    keywords.append(service.getKeyword())
                    services.append(service)

            batchlink = ""
            batch = ar.getBatch()
            if batch:
                batchlink = "<a href='%s'>%s</a>" % (
                    batch.absolute_url(), batch.Title())

            arlink = "<a href='%s'>%s</a>" % (
                ar.absolute_url(), ar.Title())

            subgroup = ar.Schema()['SubGroup'].get(ar)
            sub_title = subgroup.Title() if subgroup else 'No Subgroup'
            sub_sort = subgroup.getSortKey() if subgroup else '1'
            sub_class = re.sub(r"[^A-Za-z\w\d\-\_]", '', sub_title)

            if [sub_sort, sub_title] not in self.categories:
                self.categories.append([sub_sort, sub_title])

            review_state = wf.getInfoFor(ar, 'review_state')
            state_title = wf.getTitleForStateOnType(
                review_state, 'AnalysisRequest')

            item = {
                'obj': ar,
                'id': ar.id,
                'uid': ar.UID(),
                'category': sub_title,
                'title': ar.Title(),
                'type_class': 'contenttype-AnalysisRequest',
                'url': ar.absolute_url(),
                'relative_url': ar.absolute_url(),
                'view_url': ar.absolute_url(),
                'created': self.ulocalized_time(ar.created()),
                'sort_key': ar.created(),
                'replace': {
                    'Batch': batchlink,
                    'AnalysisRequest': arlink,
                },
                'before': {},
                'after': {},
                'choices': {},
                'class': {'Batch': 'Title'},
                'state_class': 'state-active subgroup_{0}'.format(sub_class) if sub_class else 'state-active',
                'allow_edit': [],
                'Batch': '',
                'SamplePoint': ar.getSamplePoint().Title() if ar.getSamplePoint() else '',
                'SampleType': ar.getSampleType().Title() if ar.getSampleType() else '',
                'ClientOrderNumber': ar.getClientOrderNumber(),
                'AnalysisRequest': '',
                'state_title': state_title,
            }
            items.append(item)

        unitstr = '<em class="discreet" style="white-space:nowrap;">%s</em>'
        checkPermission = getSecurityManager().checkPermission

        # Insert columns for analyses
        for service in services:
            keyword = service.getKeyword()
            self.columns[keyword] = {
                'title': service.ShortTitle if service.ShortTitle else service.title,
                'sortable': True
            }
            self.review_states[0]['columns'].insert(
                len(self.review_states[0]['columns']) - 1, keyword)

            # Insert values for analyses
            for i, item in enumerate(items):
                for analysis in analyses[item['id']]:
                    if keyword not in items[i]:
                        items[i][keyword] = ''
                    if analysis.getKeyword() != keyword:
                        continue

                    edit = checkPermission(EditResults, analysis)
                    calculation = analysis.getService().getCalculation()
                    if self.allow_edit and edit and not calculation:
                        items[i]['allow_edit'].append(keyword)
                        if not self.insert_submit_button:
                            self.insert_submit_button = True

                    value = analysis.getResult()
                    items[i][keyword] = value
                    items[i]['class'][keyword] = ''

                    if value or (edit and not calculation):

                        unit = unitstr % service.getUnit()
                        items[i]['after'][keyword] = unit

                if keyword not in items[i]['class']:
                    items[i]['class'][keyword] = 'empty'
        if self.insert_submit_button:
            custom_actions = self.review_states[0].get('custom_actions', [])
            custom_actions.append({'id': 'submit'})
            self.review_states[0]['custom_actions'] = custom_actions

        self.categories.sort()
        self.categories = [x[1] for x in self.categories]

        items = sorted(items, key=itemgetter("sort_key"))

        return items
Beispiel #10
0
    def folderitems(self, full_objects=False):
        workflow = getToolByName(self.context, "portal_workflow")
        items = BikaListingView.folderitems(self)
        mtool = getToolByName(self.context, 'portal_membership')
        member = mtool.getAuthenticatedMember()
        roles = member.getRoles()
        hideclientlink = 'RegulatoryInspector' in roles \
            and 'Manager' not in roles \
            and 'LabManager' not in roles \
            and 'LabClerk' not in roles

        for x in range(len(items)):
            if 'obj' not in items[x]:
                continue
            obj = items[x]['obj']
            sample = obj.getSample()

            if getSecurityManager().checkPermission(EditResults, obj):
                url = obj.absolute_url() + "/manage_results"
            else:
                url = obj.absolute_url()

            items[x]['Client'] = obj.aq_parent.Title()
            if (hideclientlink is False):
                items[x]['replace']['Client'] = "<a href='%s'>%s</a>" % \
                    (obj.aq_parent.absolute_url(), obj.aq_parent.Title())
            items[x]['Creator'] = self.user_fullname(obj.Creator())
            items[x]['getRequestID'] = obj.getRequestID()
            items[x]['replace']['getRequestID'] = "<a href='%s'>%s</a>" % \
                 (url, items[x]['getRequestID'])
            items[x]['getSample'] = sample
            items[x]['replace']['getSample'] = \
                "<a href='%s'>%s</a>" % (sample.absolute_url(), sample.Title())

            if obj.getAnalysesNum():
                items[x]['getAnalysesNum'] = str(
                    obj.getAnalysesNum()[0]) + '/' + str(
                        obj.getAnalysesNum()[1])
            else:
                items[x]['getAnalysesNum'] = ''

            batch = obj.getBatch()
            if batch:
                items[x]['BatchID'] = batch.getBatchID()
                items[x]['replace']['BatchID'] = "<a href='%s'>%s</a>" % \
                     (batch.absolute_url(), items[x]['BatchID'])
            else:
                items[x]['BatchID'] = ''

            val = obj.Schema().getField('SubGroup').get(obj)
            items[x]['SubGroup'] = val.Title() if val else ''

            samplingdate = obj.getSample().getSamplingDate()
            items[x]['SamplingDate'] = self.ulocalized_time(samplingdate,
                                                            long_format=1)
            items[x]['getDateReceived'] = self.ulocalized_time(
                obj.getDateReceived())
            items[x]['getDatePublished'] = self.ulocalized_time(
                obj.getDatePublished())

            deviation = sample.getSamplingDeviation()
            items[x]['SamplingDeviation'] = deviation and deviation.Title(
            ) or ''
            priority = obj.getPriority()
            items[x]['Priority'] = ''  # priority.Title()

            items[x]['getStorageLocation'] = sample.getStorageLocation(
            ) and sample.getStorageLocation().Title() or ''
            items[x]['AdHoc'] = sample.getAdHoc() and True or ''

            after_icons = ""
            state = workflow.getInfoFor(obj, 'worksheetanalysis_review_state')
            if state == 'assigned':
                after_icons += "<img src='%s/++resource++bika.lims.images/worksheet.png' title='%s'/>" % \
                    (self.portal_url, t(_("All analyses assigned")))
            if workflow.getInfoFor(obj, 'review_state') == 'invalid':
                after_icons += "<img src='%s/++resource++bika.lims.images/delete.png' title='%s'/>" % \
                    (self.portal_url, t(_("Results have been withdrawn")))
            if obj.getLate():
                after_icons += "<img src='%s/++resource++bika.lims.images/late.png' title='%s'>" % \
                    (self.portal_url, t(_("Late Analyses")))
            if samplingdate > DateTime():
                after_icons += "<img src='%s/++resource++bika.lims.images/calendar.png' title='%s'>" % \
                    (self.portal_url, t(_("Future dated sample")))
            if obj.getInvoiceExclude():
                after_icons += "<img src='%s/++resource++bika.lims.images/invoice_exclude.png' title='%s'>" % \
                    (self.portal_url, t(_("Exclude from invoice")))
            if sample.getSampleType().getHazardous():
                after_icons += "<img src='%s/++resource++bika.lims.images/hazardous.png' title='%s'>" % \
                    (self.portal_url, t(_("Hazardous")))
            if after_icons:
                items[x]['after']['getRequestID'] = after_icons

            items[x]['Created'] = self.ulocalized_time(obj.created())

            contact = obj.getContact()
            if contact:
                items[x]['ClientContact'] = contact.Title()
                items[x]['replace']['ClientContact'] = "<a href='%s'>%s</a>" % \
                    (contact.absolute_url(), contact.Title())
            else:
                items[x]['ClientContact'] = ""

            SamplingWorkflowEnabled = sample.getSamplingWorkflowEnabled()
            if SamplingWorkflowEnabled and not samplingdate > DateTime():
                datesampled = self.ulocalized_time(sample.getDateSampled())
                if not datesampled:
                    datesampled = self.ulocalized_time(DateTime(),
                                                       long_format=1)
                    items[x]['class']['getDateSampled'] = 'provisional'
                sampler = sample.getSampler().strip()
                if sampler:
                    items[x]['replace']['getSampler'] = self.user_fullname(
                        sampler)
                if 'Sampler' in member.getRoles() and not sampler:
                    sampler = member.id
                    items[x]['class']['getSampler'] = 'provisional'
            else:
                datesampled = ''
                sampler = ''
            items[x]['getDateSampled'] = datesampled
            items[x]['getSampler'] = sampler

            # sampling workflow - inline edits for Sampler and Date Sampled
            checkPermission = self.context.portal_membership.checkPermission
            state = workflow.getInfoFor(obj, 'review_state')
            if state == 'to_be_sampled' \
                    and checkPermission(SampleSample, obj) \
                    and not samplingdate > DateTime():
                items[x]['required'] = ['getSampler', 'getDateSampled']
                items[x]['allow_edit'] = ['getSampler', 'getDateSampled']
                samplers = getUsers(sample,
                                    ['Sampler', 'LabManager', 'Manager'])
                username = member.getUserName()
                users = [({
                    'ResultValue': u,
                    'ResultText': samplers.getValue(u)
                }) for u in samplers]
                items[x]['choices'] = {'getSampler': users}
                Sampler = sampler and sampler or \
                    (username in samplers.keys() and username) or ''
                items[x]['getSampler'] = Sampler

            # These don't exist on ARs
            # XXX This should be a list of preservers...
            items[x]['getPreserver'] = ''
            items[x]['getDatePreserved'] = ''

            # inline edits for Preserver and Date Preserved
            checkPermission = self.context.portal_membership.checkPermission
            if checkPermission(PreserveSample, obj):
                items[x]['required'] = ['getPreserver', 'getDatePreserved']
                items[x]['allow_edit'] = ['getPreserver', 'getDatePreserved']
                preservers = getUsers(obj,
                                      ['Preserver', 'LabManager', 'Manager'])
                username = member.getUserName()
                users = [({
                    'ResultValue': u,
                    'ResultText': preservers.getValue(u)
                }) for u in preservers]
                items[x]['choices'] = {'getPreserver': users}
                preserver = username in preservers.keys() and username or ''
                items[x]['getPreserver'] = preserver
                items[x]['getDatePreserved'] = self.ulocalized_time(
                    DateTime(), long_format=1)
                items[x]['class']['getPreserver'] = 'provisional'
                items[x]['class']['getDatePreserved'] = 'provisional'

            # Submitting user may not verify results
            if items[x]['review_state'] == 'to_be_verified' and \
               not checkPermission(VerifyOwnResults, obj):
                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') == member.getId():
                                self_submitted = True
                            break
                    if self_submitted:
                        items[x]['after']['state_title'] = \
                             "<img src='++resource++bika.lims.images/submitted-by-current-user.png' title='%s'/>" % \
                             t(_("Cannot verify: Submitted by current user"))
                except Exception:
                    pass

        # Hide Preservation/Sampling workflow actions if the edit columns
        # are not displayed.
        toggle_cols = self.get_toggle_cols()
        new_states = []
        for i, state in enumerate(self.review_states):
            if state['id'] == self.review_state:
                if 'getSampler' not in toggle_cols \
                   or 'getDateSampled' not in toggle_cols:
                    if 'hide_transitions' in state:
                        state['hide_transitions'].append('sample')
                    else:
                        state['hide_transitions'] = [
                            'sample',
                        ]
                if 'getPreserver' not in toggle_cols \
                   or 'getDatePreserved' not in toggle_cols:
                    if 'hide_transitions' in state:
                        state['hide_transitions'].append('preserve')
                    else:
                        state['hide_transitions'] = [
                            'preserve',
                        ]
            new_states.append(state)
        self.review_states = new_states

        return items
Beispiel #11
0
    def create(self, context, request):
        """/@@API/create: Create new object.

        Required parameters:

            - obj_type = portal_type of new object.
            - obj_path = path of new object, from plone site root. - Not required for
             obj_type=AnalysisRequest

        Optionally:

            - obj_id = ID of new object.

        All other parameters in the request are matched against the object's
        Schema.  If a matching field is found in the schema, then the value is
        taken from the request and sent to the field's mutator.

        Reference fields may have their target value(s) specified with a
        delimited string query syntax, containing the portal_catalog search:

            <FieldName>=index1:value1|index2:value2

        eg to set the Client of a batch:

            ...@@API/update?obj_path=<path>...
            ...&Client=title:<client_title>&...

        And, to set a multi-valued reference, these both work:

            ...@@API/update?obj_path=<path>...
            ...&InheritedObjects:list=title:AR1...
            ...&InheritedObjects:list=title:AR2...

            ...@@API/update?obj_path=<path>...
            ...&InheritedObjects[]=title:AR1...
            ...&InheritedObjects[]=title:AR2...

        The Analysis_Specification parameter is special, it mimics
        the format of the python dictionaries, and only service Keyword
        can be used to reference services.  Even if the keyword is not
        actively required, it must be supplied:

            <service_keyword>:min:max:error tolerance

        The function returns a dictionary as a json string:

        {
            runtime: Function running time.
            error: true or string(message) if error. false if no error.
            success: true or string(message) if success. false if no success.
        }

        >>> portal = layer['portal']
        >>> portal_url = portal.absolute_url()
        >>> from plone.app.testing import SITE_OWNER_NAME
        >>> from plone.app.testing import SITE_OWNER_PASSWORD

        Simple AR creation, no obj_path parameter is required:

        >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD)
        >>> browser.open(portal_url+"/@@API/create", "&".join([
        ... "obj_type=AnalysisRequest",
        ... "Client=portal_type:Client|id:client-1",
        ... "SampleType=portal_type:SampleType|title:Apple Pulp",
        ... "Contact=portal_type:Contact|getFullname:Rita Mohale",
        ... "Services:list=portal_type:AnalysisService|title:Calcium",
        ... "Services:list=portal_type:AnalysisService|title:Copper",
        ... "Services:list=portal_type:AnalysisService|title:Magnesium",
        ... "SamplingDate=2013-09-29",
        ... "Specification=portal_type:AnalysisSpec|title:Apple Pulp",
        ... ]))
        >>> browser.contents
        '{..."success": true...}'

        If some parameters are specified and are not located as existing fields or properties
        of the created instance, the create should fail:

        >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD)
        >>> browser.open(portal_url+"/@@API/create?", "&".join([
        ... "obj_type=Batch",
        ... "obj_path=/batches",
        ... "title=Test",
        ... "Thing=Fish"
        ... ]))
        >>> browser.contents
        '{...The following request fields were not used: ...Thing...}'

        Now we test that the AR create also fails if some fields are spelled wrong

        >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD)
        >>> browser.open(portal_url+"/@@API/create", "&".join([
        ... "obj_type=AnalysisRequest",
        ... "thing=Fish",
        ... "Client=portal_type:Client|id:client-1",
        ... "SampleType=portal_type:SampleType|title:Apple Pulp",
        ... "Contact=portal_type:Contact|getFullname:Rita Mohale",
        ... "Services:list=portal_type:AnalysisService|title:Calcium",
        ... "Services:list=portal_type:AnalysisService|title:Copper",
        ... "Services:list=portal_type:AnalysisService|title:Magnesium",
        ... "SamplingDate=2013-09-29"
        ... ]))
        >>> browser.contents
        '{...The following request fields were not used: ...thing...}'

        """
        savepoint = transaction.savepoint()
        self.context = context
        self.request = request
        self.unused = [x for x in self.request.form.keys()]
        self.used("form.submitted")
        self.used("__ac_name")
        self.used("__ac_password")
        # always require obj_type
        self.require("obj_type")
        obj_type = self.request['obj_type']
        self.used("obj_type")
        # AnalysisRequest shortcut: creates Sample, Partition, AR, Analyses.
        if obj_type == "AnalysisRequest":
            try:
                return self._create_ar(context, request)
            except:
                savepoint.rollback()
                raise
        # Other object types require explicit path as their parent
        self.require("obj_path")
        obj_path = self.request['obj_path']
        if not obj_path.startswith("/"):
            obj_path = "/" + obj_path
        self.used("obj_path")
        site_path = request['PATH_INFO'].replace("/@@API/create", "")
        parent = context.restrictedTraverse(str(site_path + obj_path))
        # normal permissions still apply for this user
        if not getSecurityManager().checkPermission(AccessJSONAPI, parent):
            msg = "You don't have the '{0}' permission on {1}".format(
                AccessJSONAPI, parent.absolute_url())
            raise Unauthorized(msg)

        obj_id = request.get("obj_id", "")
        _renameAfterCreation = False
        if not obj_id:
            _renameAfterCreation = True
            obj_id = tmpID()
        self.used(obj_id)

        ret = {
            "url": router.url_for("create", force_external=True),
            "success": True,
            "error": False,
        }

        try:
            obj = _createObjectByType(obj_type, parent, obj_id)
            obj.unmarkCreationFlag()
            if _renameAfterCreation:
                renameAfterCreation(obj)
            ret['obj_id'] = obj.getId()
            used_fields = set_fields_from_request(obj, request)
            for field in used_fields:
                self.used(field)
            obj.reindexObject()
            obj.aq_parent.reindexObject()
            event.notify(ObjectInitializedEvent(obj))
            obj.at_post_create_script()
        except:
            savepoint.rollback()
            raise

        if self.unused:
            raise BadRequest("The following request fields were not used: %s.  Request aborted." % self.unused)

        return ret
Beispiel #12
0
    def __call__(self):
        ar = self.context
        workflow = getToolByName(self.context, 'portal_workflow')
        if 'transition' in self.request.form:
            doActionFor(self.context, self.request.form['transition'])
        # Contacts get expanded for view
        contact = self.context.getContact()
        contacts = []
        for cc in self.context.getCCContact():
            contacts.append(cc)
        if contact in contacts:
            contacts.remove(contact)
        ccemails = []
        for cc in contacts:
            ccemails.append("%s &lt;<a href='mailto:%s'>%s</a>&gt;"
                % (cc.Title(), cc.getEmailAddress(), cc.getEmailAddress()))
        # CC Emails become mailto links
        emails = self.context.getCCEmails()
        if isinstance(emails, str):
            emails = emails and [emails, ] or []
        cc_emails = []
        cc_hrefs = []
        for cc in emails:
            cc_emails.append(cc)
            cc_hrefs.append("<a href='mailto:%s'>%s</a>" % (cc, cc))
        # render header table
        self.header_table = HeaderTableView(self.context, self.request)()
        # Create Partitions View for this ARs sample
        p = SamplePartitionsView(self.context.getSample(), self.request)
        p.show_column_toggles = False
        self.parts = p.contents_table()
        # Create Field and Lab Analyses tables
        self.tables = {}
        for poc in POINTS_OF_CAPTURE:
            if self.context.getAnalyses(getPointOfCapture=poc):
                t = self.createAnalysesView(ar,
                                 self.request,
                                 getPointOfCapture=poc,
                                 show_categories=self.context.bika_setup.getCategoriseAnalysisServices())
                t.allow_edit = True
                t.form_id = "%s_analyses" % poc
                t.review_states[0]['transitions'] = [{'id': 'submit'},
                                                     {'id': 'retract'},
                                                     {'id': 'verify'}]
                t.show_workflow_action_buttons = True
                t.show_select_column = True
                if getSecurityManager().checkPermission(EditFieldResults, self.context) \
                   and poc == 'field':
                    t.review_states[0]['columns'].remove('DueDate')
                self.tables[POINTS_OF_CAPTURE.getValue(poc)] = t.contents_table()
        # Un-captured field analyses may cause confusion
        if ar.getAnalyses(getPointOfCapture='field',
                          review_state=['sampled', 'sample_due']):
            message = _("There are field analyses without submitted results.")
            self.addMessage(message, 'info')
        # Create QC Analyses View for this AR
        show_cats = self.context.bika_setup.getCategoriseAnalysisServices()
        qcview = self.createQCAnalyesView(ar,
                                self.request,
                                show_categories=show_cats)
        qcview.allow_edit = False
        qcview.show_select_column = False
        qcview.show_workflow_action_buttons = False
        qcview.form_id = "%s_qcanalyses"
        qcview.review_states[0]['transitions'] = [{'id': 'submit'},
                                                  {'id': 'retract'},
                                                  {'id': 'verify'}]
        self.qctable = qcview.contents_table()

        # Create the ResultsInterpretation by department view
        from dependencies.dependency import ARResultsInterpretationView
        self.riview = ARResultsInterpretationView(ar, self.request)

        # If a general retracted is done, rise a waring
        if workflow.getInfoFor(ar, 'review_state') == 'sample_received':
            allstatus = list()
            for analysis in ar.getAnalyses():
                status = workflow.getInfoFor(analysis.getObject(), 'review_state')
                if status not in ['retracted','to_be_verified','verified']:
                    allstatus = []
                    break
                else:
                    allstatus.append(status)
            if len(allstatus) > 0:
                self.addMessage("General Retract Done", 'warning')

        # If is a retracted AR, show the link to child AR and show a warn msg
        if workflow.getInfoFor(ar, 'review_state') == 'invalid':
            childar = hasattr(ar, 'getChildAnalysisRequest') \
                        and ar.getChildAnalysisRequest() or None
            message = _('These results have been withdrawn and are '
                        'listed here for trace-ability purposes. Please follow '
                        'the link to the retest')
            if childar:
                message = (message + " %s.") % childar.getRequestID()
            else:
                message = message + "."
            self.addMessage(message, 'warning')
        # If is an AR automatically generated due to a Retraction, show it's
        # parent AR information
        if hasattr(ar, 'getParentAnalysisRequest') \
            and ar.getParentAnalysisRequest():
            par = ar.getParentAnalysisRequest()
            message = _('This Analysis Request has been '
                        'generated automatically due to '
                        'the retraction of the Analysis '
                        'Request ${retracted_request_id}.',
                        mapping={'retracted_request_id': par.getRequestID()})
            self.addMessage(message, 'info')
        self.renderMessages()
        return self.template()
Beispiel #13
0
    def render_field_view(self, field):
        fieldname = field.getName()
        field = self.context.Schema()[fieldname]
        ret = {'fieldName': fieldname, 'mode': 'view'}
        try:
            adapter = getAdapter(self.context,
                                 interface=IHeaderTableFieldRenderer,
                                 name=fieldname)
        except ComponentLookupError:
            adapter = None
        if adapter:
            ret = {
                'fieldName': fieldname,
                'mode': 'structure',
                'html': adapter(field)
            }
        else:
            if field.getType().find("ool") > -1:
                value = field.get(self.context)
                ret = {
                    'fieldName': fieldname,
                    'mode': 'structure',
                    'html': t(_('Yes')) if value else t(_('No'))
                }
            elif field.getType().find("Reference") > -1:
                # Prioritize method retrieval over schema's field
                targets = None
                if hasattr(self.context, 'get%s' % fieldname):
                    fieldaccessor = getattr(self.context, 'get%s' % fieldname)
                    if callable(fieldaccessor):
                        targets = fieldaccessor()
                if not targets:
                    targets = field.get(self.context)

                if targets:
                    if not type(targets) == list:
                        targets = [
                            targets,
                        ]
                    sm = getSecurityManager()
                    if all([sm.checkPermission(view, ta) for ta in targets]):
                        a = [
                            "<a href='%s'>%s</a>" %
                            (target.absolute_url(), target.Title())
                            for target in targets
                        ]
                        ret = {
                            'fieldName': fieldname,
                            'mode': 'structure',
                            'html': ", ".join(a)
                        }
                    else:
                        ret = {
                            'fieldName': fieldname,
                            'mode': 'structure',
                            'html': ", ".join([ta.Title() for ta in targets])
                        }
                else:
                    ret = {
                        'fieldName': fieldname,
                        'mode': 'structure',
                        'html': ''
                    }
            elif field.getType().lower().find('datetime') > -1:
                value = field.get(self.context)
                ret = {
                    'fieldName': fieldname,
                    'mode': 'structure',
                    'html': self.ulocalized_time(value, long_format=True)
                }
        return ret
Beispiel #14
0
    def folderitems(self, full_objects=False):
        workflow = getToolByName(self.context, "portal_workflow")
        items = BikaListingView.folderitems(self)
        mtool = getToolByName(self.context, 'portal_membership')
        member = mtool.getAuthenticatedMember()
        roles = member.getRoles()
        hideclientlink = 'RegulatoryInspector' in roles \
            and 'Manager' not in roles \
            and 'LabManager' not in roles \
            and 'LabClerk' not in roles

        for x in range(len(items)):
            if 'obj' not in items[x]:
                continue
            obj = items[x]['obj']
            sample = obj.getSample()

            if getSecurityManager().checkPermission(EditResults, obj):
                url = obj.absolute_url() + "/manage_results"
            else:
                url = obj.absolute_url()

            items[x]['Client'] = obj.aq_parent.Title()
            if (hideclientlink is False):
                items[x]['replace']['Client'] = "<a href='%s'>%s</a>" % \
                    (obj.aq_parent.absolute_url(), obj.aq_parent.Title())
            items[x]['Creator'] = self.user_fullname(obj.Creator())
            items[x]['getRequestID'] = obj.getRequestID()
            items[x]['replace']['getRequestID'] = "<a href='%s'>%s</a>" % \
                 (url, items[x]['getRequestID'])
            items[x]['getSample'] = sample
            items[x]['replace']['getSample'] = \
                "<a href='%s'>%s</a>" % (sample.absolute_url(), sample.Title())

            if obj.getAnalysesNum():
                items[x]['getAnalysesNum'] = str(obj.getAnalysesNum()[0]) + '/' + str(obj.getAnalysesNum()[1])
            else:
                items[x]['getAnalysesNum'] = ''

            batch = obj.getBatch()
            if batch:
                items[x]['BatchID'] = batch.getBatchID()
                items[x]['replace']['BatchID'] = "<a href='%s'>%s</a>" % \
                     (batch.absolute_url(), items[x]['BatchID'])
            else:
                items[x]['BatchID'] = ''

            val = obj.Schema().getField('SubGroup').get(obj)
            items[x]['SubGroup'] = val.Title() if val else ''

            samplingdate = obj.getSample().getSamplingDate()
            items[x]['SamplingDate'] = self.ulocalized_time(samplingdate, long_format=1)
            items[x]['getDateReceived'] = self.ulocalized_time(obj.getDateReceived())
            items[x]['getDatePublished'] = self.ulocalized_time(obj.getDatePublished())

            deviation = sample.getSamplingDeviation()
            items[x]['SamplingDeviation'] = deviation and deviation.Title() or ''
            priority = obj.getPriority()
            items[x]['Priority'] = '' # priority.Title()

            items[x]['getStorageLocation'] = sample.getStorageLocation() and sample.getStorageLocation().Title() or ''
            items[x]['AdHoc'] = sample.getAdHoc() and True or ''

            after_icons = ""
            state = workflow.getInfoFor(obj, 'worksheetanalysis_review_state')
            if state == 'assigned':
                after_icons += "<img src='%s/++resource++bika.lims.images/worksheet.png' title='%s'/>" % \
                    (self.portal_url, t(_("All analyses assigned")))
            if workflow.getInfoFor(obj, 'review_state') == 'invalid':
                after_icons += "<img src='%s/++resource++bika.lims.images/delete.png' title='%s'/>" % \
                    (self.portal_url, t(_("Results have been withdrawn")))
            if obj.getLate():
                after_icons += "<img src='%s/++resource++bika.lims.images/late.png' title='%s'>" % \
                    (self.portal_url, t(_("Late Analyses")))
            if samplingdate > DateTime():
                after_icons += "<img src='%s/++resource++bika.lims.images/calendar.png' title='%s'>" % \
                    (self.portal_url, t(_("Future dated sample")))
            if obj.getInvoiceExclude():
                after_icons += "<img src='%s/++resource++bika.lims.images/invoice_exclude.png' title='%s'>" % \
                    (self.portal_url, t(_("Exclude from invoice")))
            if sample.getSampleType().getHazardous():
                after_icons += "<img src='%s/++resource++bika.lims.images/hazardous.png' title='%s'>" % \
                    (self.portal_url, t(_("Hazardous")))
            if after_icons:
                items[x]['after']['getRequestID'] = after_icons

            items[x]['Created'] = self.ulocalized_time(obj.created())

            contact = obj.getContact()
            if contact:
                items[x]['ClientContact'] = contact.Title()
                items[x]['replace']['ClientContact'] = "<a href='%s'>%s</a>" % \
                    (contact.absolute_url(), contact.Title())
            else:
                items[x]['ClientContact'] = ""

            SamplingWorkflowEnabled = sample.getSamplingWorkflowEnabled()
            if SamplingWorkflowEnabled and not samplingdate > DateTime():
                datesampled = self.ulocalized_time(sample.getDateSampled())
                if not datesampled:
                    datesampled = self.ulocalized_time(
                        DateTime(),
                        long_format=1)
                    items[x]['class']['getDateSampled'] = 'provisional'
                sampler = sample.getSampler().strip()
                if sampler:
                    items[x]['replace']['getSampler'] = self.user_fullname(sampler)
                if 'Sampler' in member.getRoles() and not sampler:
                    sampler = member.id
                    items[x]['class']['getSampler'] = 'provisional'
            else:
                datesampled = ''
                sampler = ''
            items[x]['getDateSampled'] = datesampled
            items[x]['getSampler'] = sampler

            # sampling workflow - inline edits for Sampler and Date Sampled
            checkPermission = self.context.portal_membership.checkPermission
            state = workflow.getInfoFor(obj, 'review_state')
            if state == 'to_be_sampled' \
                    and checkPermission(SampleSample, obj) \
                    and not samplingdate > DateTime():
                items[x]['required'] = ['getSampler', 'getDateSampled']
                items[x]['allow_edit'] = ['getSampler', 'getDateSampled']
                samplers = getUsers(sample, ['Sampler', 'LabManager', 'Manager'])
                username = member.getUserName()
                users = [({'ResultValue': u, 'ResultText': samplers.getValue(u)})
                         for u in samplers]
                items[x]['choices'] = {'getSampler': users}
                Sampler = sampler and sampler or \
                    (username in samplers.keys() and username) or ''
                items[x]['getSampler'] = Sampler

            # These don't exist on ARs
            # XXX This should be a list of preservers...
            items[x]['getPreserver'] = ''
            items[x]['getDatePreserved'] = ''

            # inline edits for Preserver and Date Preserved
            checkPermission = self.context.portal_membership.checkPermission
            if checkPermission(PreserveSample, obj):
                items[x]['required'] = ['getPreserver', 'getDatePreserved']
                items[x]['allow_edit'] = ['getPreserver', 'getDatePreserved']
                preservers = getUsers(obj, ['Preserver', 'LabManager', 'Manager'])
                username = member.getUserName()
                users = [({'ResultValue': u, 'ResultText': preservers.getValue(u)})
                         for u in preservers]
                items[x]['choices'] = {'getPreserver': users}
                preserver = username in preservers.keys() and username or ''
                items[x]['getPreserver'] = preserver
                items[x]['getDatePreserved'] = self.ulocalized_time(
                    DateTime(),
                    long_format=1)
                items[x]['class']['getPreserver'] = 'provisional'
                items[x]['class']['getDatePreserved'] = 'provisional'

            # Submitting user may not verify results
            if items[x]['review_state'] == 'to_be_verified' and \
               not checkPermission(VerifyOwnResults, obj):
                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') == member.getId():
                                self_submitted = True
                            break
                    if self_submitted:
                        items[x]['after']['state_title'] = \
                             "<img src='++resource++bika.lims.images/submitted-by-current-user.png' title='%s'/>" % \
                             t(_("Cannot verify: Submitted by current user"))
                except Exception:
                    pass

        # Hide Preservation/Sampling workflow actions if the edit columns
        # are not displayed.
        toggle_cols = self.get_toggle_cols()
        new_states = []
        for i, state in enumerate(self.review_states):
            if state['id'] == self.review_state:
                if 'getSampler' not in toggle_cols \
                   or 'getDateSampled' not in toggle_cols:
                    if 'hide_transitions' in state:
                        state['hide_transitions'].append('sample')
                    else:
                        state['hide_transitions'] = ['sample', ]
                if 'getPreserver' not in toggle_cols \
                   or 'getDatePreserved' not in toggle_cols:
                    if 'hide_transitions' in state:
                        state['hide_transitions'].append('preserve')
                    else:
                        state['hide_transitions'] = ['preserve', ]
            new_states.append(state)
        self.review_states = new_states

        return items
# Can't do anything to the object if it's cancelled
# reference and duplicate analyses don't have cancellation_state
#if context.portal_type == "Analysis":
if workflow.getInfoFor(context, 'cancellation_state', 'active') == "cancelled":
    return False

# All kinds of analyses get their submitter and verifier compared
if context.portal_type in ("ReferenceAnalysis",
                           "DuplicateAnalysis"):

    # May we verify results that we ourself submitted?
    if checkPermission(VerifyOwnResults, context):
        return True

    # Check for self-submitted Analysis.
    user_id = getSecurityManager().getUser().getId()
    self_submitted = False
    review_history = workflow.getInfoFor(context, 'review_history')
    review_history = context.reverseList(review_history)
    for event in review_history:
        if event.get('action') == 'submit':
            if event.get('actor') == user_id:
                self_submitted = True
            break
    if self_submitted:
        return False

if context.portal_type == "AnalysisRequest":

    if not checkPermission(Verify, context):
        # Allow automatic verify (Disregard permission)
Beispiel #16
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]['DetectionLimit'] = ''
            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:
                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 ''
                items[i]['st_uid'] = st_uid

            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)

                # 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() == True:
                    unc = obj.getUncertainty(result)
                    item['allow_edit'].append('Uncertainty')
                    items[i]['Uncertainty'] = unc if unc else ''
                    items[i]['before']['Uncertainty'] = '&plusmn;&nbsp;';
                    items[i]['after']['Uncertainty'] = '<em class="discreet" style="white-space:nowrap;"> %s</em>' % items[i]['Unit'];
                elif fu:
                    items[i]['Uncertainty'] = fu
                    items[i]['before']['Uncertainty'] = '&plusmn;&nbsp;';
                    items[i]['after']['Uncertainty'] = '<em class="discreet" style="white-space:nowrap;"> %s</em>' % items[i]['Unit'];

                # 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() == 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:
                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
Beispiel #17
0
    def __call__(self):
        ar = self.context
        workflow = getToolByName(self.context, 'portal_workflow')
        if 'transition' in self.request.form:
            doActionFor(self.context, self.request.form['transition'])
        # Contacts get expanded for view
        contact = self.context.getContact()
        contacts = []
        for cc in self.context.getCCContact():
            contacts.append(cc)
        if contact in contacts:
            contacts.remove(contact)
        ccemails = []
        for cc in contacts:
            ccemails.append(
                "%s &lt;<a href='mailto:%s'>%s</a>&gt;" %
                (cc.Title(), cc.getEmailAddress(), cc.getEmailAddress()))
        # CC Emails become mailto links
        emails = self.context.getCCEmails()
        if isinstance(emails, str):
            emails = emails and [
                emails,
            ] or []
        cc_emails = []
        cc_hrefs = []
        for cc in emails:
            cc_emails.append(cc)
            cc_hrefs.append("<a href='mailto:%s'>%s</a>" % (cc, cc))
        # render header table
        self.header_table = HeaderTableView(self.context, self.request)()
        # Create Partitions View for this ARs sample
        p = SamplePartitionsView(self.context.getSample(), self.request)
        p.show_column_toggles = False
        self.parts = p.contents_table()
        # Create Field and Lab Analyses tables
        self.tables = {}
        for poc in POINTS_OF_CAPTURE:
            if self.context.getAnalyses(getPointOfCapture=poc):
                t = self.createAnalysesView(
                    ar,
                    self.request,
                    getPointOfCapture=poc,
                    show_categories=self.context.bika_setup.
                    getCategoriseAnalysisServices())
                t.allow_edit = True
                t.form_id = "%s_analyses" % poc
                t.review_states[0]['transitions'] = [{
                    'id': 'submit'
                }, {
                    'id': 'retract'
                }, {
                    'id': 'verify'
                }]
                t.show_workflow_action_buttons = True
                t.show_select_column = True
                if getSecurityManager().checkPermission(EditFieldResults, self.context) \
                   and poc == 'field':
                    t.review_states[0]['columns'].remove('DueDate')
                self.tables[POINTS_OF_CAPTURE.getValue(
                    poc)] = t.contents_table()
        # Un-captured field analyses may cause confusion
        if ar.getAnalyses(getPointOfCapture='field',
                          review_state=['sampled', 'sample_due']):
            message = _("There are field analyses without submitted results.")
            self.addMessage(message, 'info')
        # Create QC Analyses View for this AR
        show_cats = self.context.bika_setup.getCategoriseAnalysisServices()
        qcview = self.createQCAnalyesView(ar,
                                          self.request,
                                          show_categories=show_cats)
        qcview.allow_edit = False
        qcview.show_select_column = False
        qcview.show_workflow_action_buttons = False
        qcview.form_id = "%s_qcanalyses"
        qcview.review_states[0]['transitions'] = [{
            'id': 'submit'
        }, {
            'id': 'retract'
        }, {
            'id': 'verify'
        }]
        self.qctable = qcview.contents_table()

        # Create the ResultsInterpretation by department view
        from dependencies.dependency import ARResultsInterpretationView
        self.riview = ARResultsInterpretationView(ar, self.request)

        # If a general retracted is done, rise a waring
        if workflow.getInfoFor(ar, 'review_state') == 'sample_received':
            allstatus = list()
            for analysis in ar.getAnalyses():
                status = workflow.getInfoFor(analysis.getObject(),
                                             'review_state')
                if status not in ['retracted', 'to_be_verified', 'verified']:
                    allstatus = []
                    break
                else:
                    allstatus.append(status)
            if len(allstatus) > 0:
                self.addMessage("General Retract Done", 'warning')

        # If is a retracted AR, show the link to child AR and show a warn msg
        if workflow.getInfoFor(ar, 'review_state') == 'invalid':
            childar = hasattr(ar, 'getChildAnalysisRequest') \
                        and ar.getChildAnalysisRequest() or None
            message = _(
                'These results have been withdrawn and are '
                'listed here for trace-ability purposes. Please follow '
                'the link to the retest')
            if childar:
                message = (message + " %s.") % childar.getRequestID()
            else:
                message = message + "."
            self.addMessage(message, 'warning')
        # If is an AR automatically generated due to a Retraction, show it's
        # parent AR information
        if hasattr(ar, 'getParentAnalysisRequest') \
            and ar.getParentAnalysisRequest():
            par = ar.getParentAnalysisRequest()
            message = _(
                'This Analysis Request has been '
                'generated automatically due to '
                'the retraction of the Analysis '
                'Request ${retracted_request_id}.',
                mapping={'retracted_request_id': par.getRequestID()})
            self.addMessage(message, 'info')
        self.renderMessages()
        return self.template()
Beispiel #18
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]['DetectionLimit'] = ''
            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:
                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 ''
                items[i]['st_uid'] = st_uid

            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)

                # 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(
                ) == True:
                    unc = obj.getUncertainty(result)
                    item['allow_edit'].append('Uncertainty')
                    items[i]['Uncertainty'] = unc if unc else ''
                    items[i]['before']['Uncertainty'] = '&plusmn;&nbsp;'
                    items[i]['after'][
                        'Uncertainty'] = '<em class="discreet" style="white-space:nowrap;"> %s</em>' % items[
                            i]['Unit']
                elif fu:
                    items[i]['Uncertainty'] = fu
                    items[i]['before']['Uncertainty'] = '&plusmn;&nbsp;'
                    items[i]['after'][
                        'Uncertainty'] = '<em class="discreet" style="white-space:nowrap;"> %s</em>' % items[
                            i]['Unit']

                # 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() == 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:
                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