Exemplo n.º 1
0
def _children_are_ready(obj, transition_id, dettached_states=None):
    """Returns true if the children of the object passed in (worksheet) have
    been all transitioned in accordance with the 'transition_id' passed in. If
    detached_states is provided, children with those states are dismissed, so
    they will not be taken into account in the evaluation. Nevertheless, at
    least one child with for which the transition_id performed is required for
    this function to return true (if all children are in detached states, it
    always return False).
    """
    query = dict(getWorksheetUID=api.get_uid(obj))
    brains = api.search(query, CATALOG_ANALYSIS_LISTING)
    if not brains:
        return False
    detached_count = 0

    for brain in brains:
        if dettached_states and brain.review_state in dettached_states:
            detached_count += 1
            # dismiss the brain and skip the rest of the checks
            continue
        if not api.is_active(brain):
            return False
        analysis = api.get_object(brain)
        if not wasTransitionPerformed(analysis, transition_id):
            return False

    if detached_count == len(brains):
        # If all brains are in a detached state, it means that the
        # condition of at least having one child for which the
        # transition is performed is not satisfied so return False
        return False
    return True
Exemplo n.º 2
0
    def isUserAllowedToVerify(self, member):
        """
        Checks if the specified user has enough privileges to verify the
        current WS. Apart from the roles, this function also checks if the
        analyses in this worksheet have all been verified.
        :member: user to be tested
        :return: true or false
        """
        # Check if the user has "Bika: Verify" privileges
        username = member.getUserName()
        allowed = api.user.has_permission(VerifyPermission, username=username)
        if not allowed:
            return False

        # Check if the analyses have all been verified
        workflow = getToolByName(self, 'portal_workflow')
        for a in self.getAnalyses(full_objects=True):
            st = workflow.getInfoFor(a, 'cancellation_state', 'active')
            if st == 'cancelled':
                continue
            st = workflow.getInfoFor(a, 'review_state')
            if st in ['retracted', 'rejected']:
                continue
            if not wasTransitionPerformed(a, 'verify'):
                return False

        return True
Exemplo n.º 3
0
    def isVerifiable(self):
        """Checks it the current analysis can be verified. This is, its not a
        cancelled analysis and has no dependenant analyses not yet verified
        :return: True or False
        """
        # Check if the analysis is active
        workflow = getToolByName(self, "portal_workflow")
        objstate = workflow.getInfoFor(self, 'cancellation_state', 'active')
        if objstate == "cancelled":
            return False

        # Check if the analysis state is to_be_verified
        review_state = workflow.getInfoFor(self, "review_state")
        if review_state != 'to_be_verified':
            return False

        # If the analysis has at least one dependency that hasn't been verified
        # yet and because of its current state cannot be verified, then return
        # false. The idea is that an analysis that requires from results of
        # other analyses cannot be verified unless all its dependencies have
        # already been verified or are in a suitable state for doing so.
        for d in self.getDependencies():
            if not d.isVerifiable() \
                    and not wasTransitionPerformed(d, 'verify'):
                return False

        # All checks passed
        return True
Exemplo n.º 4
0
def _children_are_ready(obj, transition_id, dettached_states=None):
    """Returns true if the children of the object passed in (worksheet) have
    been all transitioned in accordance with the 'transition_id' passed in. If
    detached_states is provided, children with those states are dismissed, so
    they will not be taken into account in the evaluation. Nevertheless, at
    least one child with for which the transition_id performed is required for
    this function to return true (if all children are in detached states, it
    always return False).
    """
    detached_count = 0
    analyses = obj.getAnalyses()
    for analysis in analyses:
        if dettached_states:
            if api.get_review_status(analysis) in dettached_states:
                detached_count += 1
                continue
        if not api.is_active(analysis):
            return False
        if not wasTransitionPerformed(analysis, transition_id):
            return False

    if detached_count == len(analyses):
        # If all analyses are in a detached state, it means that the
        # condition of at least having one child for which the
        # transition is performed is not satisfied so return False
        return False
    return True
Exemplo n.º 5
0
    def isVerifiable(self):
        """Checks it the current analysis can be verified. This is, its not a
        cancelled analysis and has no dependenant analyses not yet verified
        :return: True or False
        """
        # Check if the analysis is active
        workflow = getToolByName(self, "portal_workflow")
        objstate = workflow.getInfoFor(self, 'cancellation_state', 'active')
        if objstate == "cancelled":
            return False

        # Check if the analysis state is to_be_verified
        review_state = workflow.getInfoFor(self, "review_state")
        if review_state != 'to_be_verified':
            return False

        # If the analysis has at least one dependency that hasn't been verified
        # yet and because of its current state cannot be verified, then return
        # false. The idea is that an analysis that requires from results of
        # other analyses cannot be verified unless all its dependencies have
        # already been verified or are in a suitable state for doing so.
        for d in self.getDependencies():
            if not d.isVerifiable() \
                    and not wasTransitionPerformed(d, 'verify'):
                return False

        # All checks passed
        return True
Exemplo n.º 6
0
    def _is_frozen(self, brain_or_object):
        """Check if the passed in object is frozen

        :param obj: Analysis or AR Brain/Object
        :returns: True if the object is frozen
        """
        obj = api.get_object(brain_or_object)
        active = api.is_active(obj)
        verified = wasTransitionPerformed(obj, 'verify')
        return not active or verified
Exemplo n.º 7
0
    def _is_frozen(self, brain_or_object):
        """Check if the passed in object is frozen

        :param obj: Analysis or AR Brain/Object
        :returns: True if the object is frozen
        """
        obj = api.get_object(brain_or_object)
        active = api.is_active(obj)
        verified = wasTransitionPerformed(obj, 'verify')
        return not active or verified
Exemplo n.º 8
0
def was_transition_performed(analyses, transition_id):
    """Returns whether all analyses were transitioned or not
    """
    if not analyses:
        return False
    if not isinstance(analyses, list):
        return was_transition_performed([analyses], transition_id)
    for analysis in analyses:
        if not wf.wasTransitionPerformed(analysis, transition_id):
            return False
    return True
Exemplo n.º 9
0
    def workflow_script_submit(self):
        """
        Method triggered after a 'submit' transition for the current analysis
        is performed. Responsible of triggering cascade actions such as
        transitioning dependent analyses, transitioning worksheets, etc
        depending on the current analysis and other analyses that belong to the
        same Analysis Request or Worksheet.
        This function is called automatically by
        bika.lims.workfow.AfterTransitionEventHandler
        """
        # The analyses that depends on this analysis to calculate their results
        # must be transitioned too, otherwise the user will be forced to submit
        # them individually. Note that the automatic transition of dependents
        # must only take place if all their dependencies have been submitted
        # already.
        for dependent in self.getDependents():
            # If this submit transition has already been done for this
            # dependent analysis within the current request, continue.
            if skip(dependent, 'submit', peek=True):
                continue

            # TODO Workflow. All below and inside this loop should be moved to
            # a guard_submit_transition inside analysis

            # If this dependent has already been submitted, omit
            if dependent.getSubmittedBy():
                continue

            # The dependent cannot be transitioned if doesn't have result
            if not dependent.getResult():
                continue

            # If the calculation associated to the dependent analysis requires
            # the manual introduction of interim fields, do not transition the
            # dependent automatically, force the user to do it manually.
            calculation = dependent.getCalculation()
            if calculation and calculation.getInterimFields():
                continue

            # All dependencies from this dependent analysis are ok?
            deps = dependent.getDependencies()
            dsub = [
                dep for dep in deps if wasTransitionPerformed(dep, 'submit')
            ]
            if len(deps) == len(dsub):
                # The statuses of all dependencies of this dependent are ok
                # (at least, all of them have been submitted already)
                doActionFor(dependent, 'submit')

        # Do all the reflex rules process
        self._reflex_rule_process('submit')

        # Delegate the transition of Worksheet to base class AbstractAnalysis
        super(AbstractRoutineAnalysis, self).workflow_script_submit()
Exemplo n.º 10
0
 def _cascade_promote_transition(self, actionid, targetstate):
     """ Performs the transition for the actionid passed in to its children
     (Analyses). If all sibling partitions are in the targe state, promotes
     the transition to its parent Sample
     """
     # If all sibling partitions are received, promote Sample. Sample
     # transition will, in turn, transition the Analysis Requests
     sample = self.aq_parent
     parts = sample.objectValues("SamplePartition")
     recep = [sp for sp in parts if wasTransitionPerformed(sp, targetstate)]
     if len(parts) == len(recep):
         doActionFor(sample, actionid)
Exemplo n.º 11
0
 def __call__(self, context, mode, field, default):
     state = default if default else 'visible'
     if field.getName() == 'DateReceived':
         # In states earlier than `sample_received`, the field is uneditable
         if not wf.wasTransitionPerformed(context, "receive"):
             return mode == 'edit' and 'invisible' or 'visible'
         # The edition of Sample Date Received is only permitted if no
         # results have been submitted yet
         pending_states = ["unassigned", "assigned"]
         for analysis in context.getAnalyses():
             if analysis.review_state not in pending_states:
                 return mode == 'edit' and 'invisible' or 'visible'
     return state
Exemplo n.º 12
0
    def is_analysis_edition_allowed(self, analysis_brain):
        """Returns if the analysis passed in can be edited by the current user

        :param analysis_brain: Brain that represents an analysis
        :return: True if the user can edit the analysis, otherwise False
        """

        # TODO: Workflow. This function will be replaced by
        # `isTransitionAllowed(submit)` as soon as all this logic gets moved
        # into analysis' submit guard.... Very soon
        if not self.context_active:
            # The current context must be active. We cannot edit analyses from
            # inside a deactivated Analysis Request, for instance
            return False

        if analysis_brain.review_state == 'retracted':
            # Retracted analyses cannot be edited
            return False

        analysis_obj = None
        analysis_keyword = analysis_brain.getKeyword
        if analysis_keyword not in self._keywords_poc_map:
            # Store the point of capture for this analysis keyword in cache, so
            # waking up analyses with same keyword will not be longer required
            analysis_obj = api.get_object(analysis_brain)
            analysis_poc = analysis_obj.getPointOfCapture()
            self._keywords_poc_map[analysis_keyword] = analysis_poc

        poc = self._keywords_poc_map[analysis_keyword]
        if poc == 'field':
            # This analysis must be captured on field, during sampling.
            if not self.has_permission(EditFieldResults):
                # Current user cannot edit field analyses.
                return False

        elif not self.has_permission(EditResults):
            # The Point of Capture is 'lab' and the current user cannot edit
            # lab analyses.
            return False

        analysis_obj = analysis_obj or api.get_object(analysis_brain)
        if wasTransitionPerformed(analysis_obj, 'submit'):
            # Analysis has been already submitted. This analysis cannot be
            # edited anymore.
            return False

        # Is the instrument out of date?
        # The user can assign a result to the analysis if it does not have any
        # instrument assigned or the instrument assigned is valid.
        return self.is_analysis_instrument_valid(analysis_brain)
Exemplo n.º 13
0
    def _cascade_promote_transition(self, actionid, targetstate):
        """ Performs the transition for the actionid passed in to its children
        (Analyses). If all sibling partitions are in the targe state, promotes
        the transition to its parent Sample
        """
        # Transition our analyses
        for analysis in self.getAnalyses():
            doActionFor(analysis, actionid)

        # If all sibling partitions are received, promote Sample. Sample
        # transition will, in turn, transition the Analysis Requests
        sample = self.aq_parent
        parts = sample.objectValues("SamplePartition")
        recep = [sp for sp in parts if wasTransitionPerformed(sp, targetstate)]
        if len(parts) == len(recep):
            doActionFor(sample, actionid)
Exemplo n.º 14
0
def is_transition_allowed_or_performed(analyses, transition_ids):
    """Return whether all analyses can be transitioned or all them were
    transitioned.
    """
    if not analyses:
        return True
    if not isinstance(analyses, list):
        return is_transition_allowed_or_performed([analyses], transition_ids)
    if not isinstance(transition_ids, list):
        return is_transition_allowed_or_performed(analyses, [transition_ids])

    for transition_id in transition_ids:
        for analysis in analyses:
            if not wf.isTransitionAllowed(analysis, transition_id):
                if not wf.wasTransitionPerformed(analysis, transition_id):
                    return False
    return True
Exemplo n.º 15
0
def verify(obj):
    """Returns True if 'verify' transition can be applied to the Analysis
    Request passed in. This is, returns true if all the analyses that contains
    have already been verified. Those analyses that are in an inactive state
    (cancelled, inactive) are dismissed, but at least one analysis must be in
    an active state (and verified), otherwise always return False. If the
    Analysis Request is in inactive state (cancelled/inactive), returns False
    Note this guard depends entirely on the current status of the children
    :returns: true or false
    """
    if not isBasicTransitionAllowed(obj):
        return False

    analyses = obj.getAnalyses(full_objects=True)
    invalid = 0
    for an in analyses:
        # The analysis has already been verified?
        if wasTransitionPerformed(an, 'verify'):
            continue

        # Maybe the analysis is in an 'inactive' state?
        if not isActive(an):
            invalid += 1
            continue

        # Maybe the analysis has been rejected or retracted?
        dettached = ['rejected', 'retracted', 'attachments_due']
        status = getCurrentState(an)
        if status in dettached:
            invalid += 1
            continue

        # At this point we can assume this analysis is an a valid state and
        # could potentially be verified, but the Analysis Request can only be
        # verified if all the analyses have been transitioned to verified
        return False

    # Be sure that at least there is one analysis in an active state, it
    # doesn't make sense to verify an Analysis Request if all the analyses that
    # contains are rejected or cancelled!
    return len(analyses) - invalid > 0
Exemplo n.º 16
0
def verify(obj):
    """Returns True if 'verify' transition can be applied to the Analysis
    Request passed in. This is, returns true if all the analyses that contains
    have already been verified. Those analyses that are in an inactive state
    (cancelled, inactive) are dismissed, but at least one analysis must be in
    an active state (and verified), otherwise always return False. If the
    Analysis Request is in inactive state (cancelled/inactive), returns False
    Note this guard depends entirely on the current status of the children
    :returns: true or false
    """
    if not isBasicTransitionAllowed(obj):
        return False

    analyses = obj.getAnalyses(full_objects=True)
    invalid = 0
    for an in analyses:
        # The analysis has already been verified?
        if wasTransitionPerformed(an, 'verify'):
            continue

        # Maybe the analysis is in an 'inactive' state?
        if not isActive(an):
            invalid += 1
            continue

        # Maybe the analysis has been rejected or retracted?
        dettached = ['rejected', 'retracted', 'attachments_due']
        status = getCurrentState(an)
        if status in dettached:
            invalid += 1
            continue

        # At this point we can assume this analysis is an a valid state and
        # could potentially be verified, but the Analysis Request can only be
        # verified if all the analyses have been transitioned to verified
        return False

    # Be sure that at least there is one analysis in an active state, it
    # doesn't make sense to verify an Analysis Request if all the analyses that
    # contains are rejected or cancelled!
    return len(analyses) - invalid > 0
Exemplo n.º 17
0
def guard_submit(context):
    if not IAnalysisRequest.providedBy(context):
        # Note that this guard is only used for bika_ar_workflow!
        return True

    logger.info("*** Custom Guard: submit **")
    if not isBasicTransitionAllowed(context):
        return False

    invalid = 0
    analyses = context.getAnalyses()
    for an in analyses:
        if an.review_state == 'to_be_verified':
            continue

        # The analysis has already been verified?
        an = api.get_object(an)
        if wasTransitionPerformed(an, 'submit'):
            continue

        # Maybe the analysis is in an 'inactive' state?
        if not isActive(an):
            invalid += 1
            continue

        # Maybe the analysis has been rejected or retracted?
        dettached = ['rejected', 'retracted', 'attachments_due']
        status = getCurrentState(an)
        if status in dettached:
            invalid += 1
            continue

        # At this point we can assume this analysis is an a valid state and
        # the AR could potentially be submitted, but the Analysis Request can
        # only be submitted if all the analyses have been submitted already
        return False

    # Be sure that at least there is one analysis in an active state, it
    # doesn't make sense to submit an Analysis Request if all the analyses that
    # contains are rejected or cancelled!
    return len(analyses) - invalid > 0
Exemplo n.º 18
0
def _children_are_ready(obj, transition_id, dettached_states=None):
    """Returns true if the children of the object passed in (worksheet) have
    been all transitioned in accordance with the 'transition_id' passed in. If
    dettached_states is provided, children with those states are dismissed, so
    they will not be taken into account in the evaluation. Nevertheless, at
    least one child with for which the transition_id performed is required for
    this function to return true (if all children are in dettached states, it
    always return False).
    """
    analyses = obj.getAnalyses()
    invalid = 0
    for an in analyses:
        # The analysis has already been transitioned?
        if wasTransitionPerformed(an, transition_id):
            continue

        # Maybe the analysis is in an 'inactive' state?
        if not isActive(an):
            invalid += 1
            continue

        # Maybe the analysis is in a dettached state?
        if dettached_states:
            status = getCurrentState(an)
            if status in dettached_states:
                invalid += 1
                continue

        # At this point we can assume this analysis is an a valid state and
        # could potentially be transitioned, but the Worksheet can only be
        # transitioned if all the analyses have been transitioned previously
        return False

    # Be sure that at least there is one analysis in an active state, it
    # doesn't make sense to transition a Worksheet if all the analyses that
    # contains are not valid
    return len(analyses) - invalid > 0
Exemplo n.º 19
0
    def workflow_script_submit(self):
        """
        Method triggered after a 'submit' transition for the current analysis
        is performed. Responsible of triggering cascade actions such as
        transitioning dependent analyses, transitioning worksheets, etc
        depending on the current analysis and other analyses that belong to the
        same Analysis Request or Worksheet.
        This function is called automatically by
        bika.lims.workfow.AfterTransitionEventHandler
        """
        # The analyses that depends on this analysis to calculate their results
        # must be transitioned too, otherwise the user will be forced to submit
        # them individually. Note that the automatic transition of dependents
        # must only take place if all their dependencies have been submitted
        # already.
        for dependent in self.getDependents():
            # If this submit transition has already been done for this
            # dependent analysis within the current request, continue.
            if skip(dependent, 'submit', peek=True):
                continue

            # TODO Workflow. All below and inside this loop should be moved to
            # a guard_submit_transition inside analysis

            # If this dependent has already been submitted, omit
            if dependent.getSubmittedBy():
                continue

            # The dependent cannot be transitioned if doesn't have result
            if not dependent.getResult():
                continue

            # If the calculation associated to the dependent analysis requires
            # the manual introduction of interim fields, do not transition the
            # dependent automatically, force the user to do it manually.
            calculation = dependent.getCalculation()
            if calculation and calculation.getInterimFields():
                continue

            # All dependencies from this dependent analysis are ok?
            deps = dependent.getDependencies()
            dsub = [dep for dep in deps if wasTransitionPerformed(dep, 'submit')]
            if len(deps) == len(dsub):
                # The statuses of all dependencies of this dependent are ok
                # (at least, all of them have been submitted already)
                doActionFor(dependent, 'submit')

        # Do all the reflex rules process
        self._reflex_rule_process('submit')

        # If all analyses from the Analysis Request to which this Analysis
        # belongs have been submitted, then promote the action to the parent
        # Analysis Request
        ar = self.getRequest()
        ans = [an.getObject() for an in ar.getAnalyses()]
        anssub = [an for an in ans if wasTransitionPerformed(an, 'submit')]
        if len(ans) == len(anssub):
            doActionFor(ar, 'submit')

        # Delegate the transition of Worksheet to base class AbstractAnalysis
        super(AbstractRoutineAnalysis, self).workflow_script_submit()
Exemplo n.º 20
0
 def is_received(self):
     """Checks if the AR is received
     """
     return wasTransitionPerformed(self.context, "receive")
Exemplo n.º 21
0
 def is_submitted(self, obj):
     """Check if the "submit" transition was performed
     """
     return wasTransitionPerformed(obj, "submit")
Exemplo n.º 22
0
 def is_verified(self):
     """Checks if the AR is verified
     """
     return wasTransitionPerformed(self.context, "verify")
Exemplo n.º 23
0
    def workflow_action_save_analyses_button(self):
        form = self.request.form
        workflow = getToolByName(self.context, 'portal_workflow')
        bsc = self.context.bika_setup_catalog
        action, came_from = WorkflowAction._get_form_workflow_action(self)
        # AR Manage Analyses: save Analyses
        ar = self.context
        sample = ar.getSample()
        objects = WorkflowAction._get_selected_items(self)
        if not objects:
            message = _("No analyses have been selected")
            self.context.plone_utils.addPortalMessage(message, 'info')
            self.destination_url = self.context.absolute_url() + "/analyses"
            self.request.response.redirect(self.destination_url)
            return
        Analyses = objects.keys()
        prices = form.get("Price", [None])[0]

        # Hidden analyses?
        # https://jira.bikalabs.com/browse/LIMS-1324
        outs = []
        hiddenans = form.get('Hidden', {})
        for uid in Analyses:
            hidden = hiddenans.get(uid, '')
            hidden = True if hidden == 'on' else False
            outs.append({'uid': uid, 'hidden': hidden})
        ar.setAnalysisServicesSettings(outs)

        specs = {}
        if form.get("min", None):
            for service_uid in Analyses:
                service = objects[service_uid]
                keyword = service.getKeyword()
                specs[service_uid] = {
                    "min": form["min"][0][service_uid],
                    "max": form["max"][0][service_uid],
                    "warn_min": form["warn_min"][0][service_uid],
                    "warn_max": form["warn_max"][0][service_uid],
                    "keyword": keyword,
                    "uid": service_uid,
                }
        else:
            for service_uid in Analyses:
                service = objects[service_uid]
                keyword = service.getKeyword()
                specs[service_uid] = ResultsRangeDict(keyword=keyword,
                                                      uid=service_uid)
        new = ar.setAnalyses(Analyses, prices=prices, specs=specs.values())
        # link analyses and partitions
        # If Bika Setup > Analyses > 'Display individual sample
        # partitions' is checked, no Partitions available.
        # https://github.com/bikalabs/Bika-LIMS/issues/1030
        if 'Partition' in form:
            for service_uid, service in objects.items():
                part_id = form['Partition'][0][service_uid]
                part = sample[part_id]
                analysis = ar[service.getKeyword()]
                analysis.setSamplePartition(part)
                analysis.reindexObject()
                partans = part.getAnalyses()
                partans.append(analysis)
                part.setAnalyses(partans)
                part.reindexObject()

        if new:
            ar_state = getCurrentState(ar)
            if wasTransitionPerformed(ar, 'to_be_verified'):
                # Apply to AR only; we don't want this transition to cascade.
                ar.REQUEST['workflow_skiplist'].append("retract all analyses")
                workflow.doActionFor(ar, 'retract')
                ar.REQUEST['workflow_skiplist'].remove("retract all analyses")
                ar_state = getCurrentState(ar)
            for analysis in new:
                changeWorkflowState(analysis, 'bika_analysis_workflow',
                                    ar_state)

        message = PMF("Changes saved.")
        self.context.plone_utils.addPortalMessage(message, 'info')
        self.destination_url = self.context.absolute_url()
        self.request.response.redirect(self.destination_url)
Exemplo n.º 24
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'])

        # If the analysis request has been received and hasn't been yet
        # verified yet, redirect the user to manage_results view, but only if
        # the user has privileges to Edit(Field)Results, cause otherwise she/he
        # will receive an InsufficientPrivileges error!
        if (self.request.PATH_TRANSLATED.endswith(self.context.id)
                and check_permission(EditResults, self.context)
                and check_permission(EditFieldResults, self.context)
                and wasTransitionPerformed(self.context, 'receive')
                and not wasTransitionPerformed(self.context, 'verify')):
            # Redirect to manage results view if not cancelled
            if api.get_workflow_status_of(ar, 'cancellation_state') != \
                    "cancelled":
                manage_results_url = "/".join(
                    [self.context.absolute_url(), 'manage_results'])
                self.request.response.redirect(manage_results_url)
                return

        # 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(),
                    getRequestUID=self.context.UID())
                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()
        # 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 resultsinterpretation 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:
                message = "General Retract Done.  Submit this AR manually."
                self.addMessage(message, '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.getId()
            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.getId()})
            self.addMessage(message, 'info')
        self.renderMessages()
        return self.template()
Exemplo n.º 25
0
    def folderitems(self, full_objects=False, classic=True):
        self.categories = []

        analyses = self.context.getAnalyses(full_objects=True)
        self.analyses = dict([(a.getServiceUID(), a) for a in analyses])
        self.selected = self.analyses.keys()
        self.show_categories = \
            self.context.bika_setup.getCategoriseAnalysisServices()
        self.expand_all_categories = False

        wf = getToolByName(self.context, 'portal_workflow')
        items = BikaListingView.folderitems(self)

        parts = self.context.getSample().objectValues('SamplePartition')
        partitions = [{
            'ResultValue': o.Title(),
            'ResultText': o.getId()
        } for o in parts
                      if wf.getInfoFor(o, 'cancellation_state', '') == 'active'
                      ]

        for item in items:
            if 'obj' not in item:
                continue
            obj = item['obj']

            cat = obj.getCategoryTitle()
            item['category'] = cat
            if cat not in self.categories:
                self.categories.append(cat)

            item['selected'] = item['uid'] in self.selected
            item['class']['Title'] = 'service_title'
            row_data = dict()
            calculation = obj.getCalculation()
            item['Calculation'] = calculation and calculation.Title()

            locale = locales.getLocale('en')
            currency = self.context.bika_setup.getCurrency()
            symbol = locale.numbers.currencies[currency].symbol
            item['before']['Price'] = symbol
            item['Price'] = obj.getPrice()
            item['class']['Price'] = 'nowrap'
            item['allow_edit'] = list()
            if item['selected']:
                item['allow_edit'] = [
                    'Partition', 'min', 'max', 'warn_min', 'warn_max'
                ]
                if not logged_in_client(self.context):
                    item['allow_edit'].append('Price')

            item['required'].append('Partition')
            item['choices']['Partition'] = partitions

            if obj.UID() in self.analyses:
                analysis = self.analyses[obj.UID()]

                row_data['disabled'] = wasTransitionPerformed(
                    analysis, 'submit')

                part = analysis.getSamplePartition()
                part = part and part or obj
                item['Partition'] = part.Title()
                spec = self.get_spec_from_ar(self.context,
                                             analysis.getKeyword())
                item["min"] = spec.get("min", '')
                item["max"] = spec.get("max", '')
                item["warn_min"] = spec.get("warn_min", "")
                item["warn_max"] = spec.get("warn_max", "")
                item['Price'] = analysis.getPrice()
            else:
                item['Partition'] = ''
                item["min"] = ''
                item["max"] = ''
                item["warn_min"] = ""
                item["warn_max"] = ""

            # js checks in row_data if an analysis may not be editable.
            item['row_data'] = json.dumps(row_data)
            after_icons = ''
            if obj.getAccredited():
                after_icons += "<img\
                src='%s/++resource++bika.lims.images/accredited.png'\
                title='%s'>" % (self.portal_url, t(_("Accredited")))
            if obj.getAttachmentOption() == 'r':
                after_icons += "<img\
                src='%s/++resource++bika.lims.images/attach_reqd.png'\
                title='%s'>" % (self.portal_url, t(_("Attachment required")))
            if obj.getAttachmentOption() == 'n':
                after_icons += "<img\
                src='%s/++resource++bika.lims.images/attach_no.png'\
                title='%s'>" % (self.portal_url,
                                t(_('Attachment not permitted')))
            if after_icons:
                item['after']['Title'] = after_icons

            # Display analyses for this Analysis Service in results?
            ser = self.context.getAnalysisServiceSettings(obj.UID())
            item['allow_edit'].append('Hidden')
            item['Hidden'] = ser.get('hidden', obj.getHidden())
            item['Unit'] = obj.getUnit()

        self.categories.sort()
        return items
Exemplo n.º 26
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'])

        # If the analysis request has been received and hasn't been yet
        # verified yet, redirect the user to manage_results view, but only if
        # the user has privileges to Edit(Field)Results, cause otherwise she/he
        # will receive an InsufficientPrivileges error!
        if (self.request.PATH_TRANSLATED.endswith(self.context.id) and
            check_permission(EditResults, self.context) and
            check_permission(EditFieldResults, self.context) and
            wasTransitionPerformed(self.context, 'receive') and
            not wasTransitionPerformed(self.context, 'verify')):
            # Redirect to manage results view
            manage_results_url = self.context.absolute_url() + '/manage_results'
            self.request.response.redirect(manage_results_url)
            return

        # 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(),
                                 getRequestUID=self.context.UID())
                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()
        # 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 resultsinterpretation 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:
                message = "General Retract Done.  Submit this AR manually."
                self.addMessage(message, '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.getId()
            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.getId()})
            self.addMessage(message, 'info')
        self.renderMessages()
        return self.template()