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
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
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
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
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
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
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()
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)
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
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)
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)
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
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
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
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
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()
def is_received(self): """Checks if the AR is received """ return wasTransitionPerformed(self.context, "receive")
def is_submitted(self, obj): """Check if the "submit" transition was performed """ return wasTransitionPerformed(obj, "submit")
def is_verified(self): """Checks if the AR is verified """ return wasTransitionPerformed(self.context, "verify")
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)
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 <<a href='mailto:%s'>%s</a>>" % (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()
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
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 <<a href='mailto:%s'>%s</a>>" % (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()