def ObjectInitializedEventHandler(instance, event): wf_tool = getToolByName(instance, 'portal_workflow') ar = instance.getRequest() ar_state = wf_tool.getInfoFor(ar, 'review_state') ar_ws_state = wf_tool.getInfoFor(ar, 'worksheetanalysis_review_state') # Set the state of the analysis depending on the state of the AR. if ar_state in ('sample_registered', 'to_be_sampled', 'sampled', 'to_be_preserved', 'sample_due', 'sample_received'): changeWorkflowState(instance, "bika_analysis_workflow", ar_state) elif ar_state in ('to_be_verified'): # Apply to AR only; we don't want this transition to cascade. if 'workflow_skiplist' not in ar.REQUEST: ar.REQUEST['workflow_skiplist'] = [] ar.REQUEST['workflow_skiplist'].append("retract all analyses") wf_tool.doActionFor(ar, 'retract') ar.REQUEST['workflow_skiplist'].remove("retract all analyses") if ar_ws_state == 'assigned': # TODO workflow: analysis request can be 'assigned'? wf_tool.doActionFor(ar, 'unassign') skip(ar, 'unassign', unskip=True) return
def ObjectInitializedEventHandler(instance, event): # This is the easiest place to assign IRoutineAnalysis, # since other anlaysis types subclass Analysis. # (noLongerProvides cannot un-provide interfaces on the class itself) if instance.portal_type == 'Analysis': alsoProvides(instance, IRoutineAnalysis) wf_tool = getToolByName(instance, 'portal_workflow') ar = instance.aq_parent ar_state = wf_tool.getInfoFor(ar, 'review_state') ar_ws_state = wf_tool.getInfoFor(ar, 'worksheetanalysis_review_state') # Set the state of the analysis depending on the state of the AR. if ar_state in ('sample_registered', 'to_be_sampled', 'sampled', 'to_be_preserved', 'sample_due', 'sample_received'): changeWorkflowState(instance, "bika_analysis_workflow", ar_state) elif ar_state in ('to_be_verified'): # Apply to AR only; we don't want this transition to cascade. if 'workflow_skiplist' not in ar.REQUEST: ar.REQUEST['workflow_skiplist'] = [] ar.REQUEST['workflow_skiplist'].append("retract all analyses") wf_tool.doActionFor(ar, 'retract') ar.REQUEST['workflow_skiplist'].remove("retract all analyses") if ar_ws_state == 'assigned': wf_tool.doActionFor(ar, 'unassign') skip(ar, 'unassign', unskip=True) instance.updateDueDate() return
def workflow_script_submit(self): if skip(self, "submit"): return workflow = getToolByName(self, "portal_workflow") self.reindexObject(idxs=["review_state", ]) # If all analyses on the worksheet have been submitted, # then submit the worksheet. ws = self.getBackReferences('WorksheetAnalysis') ws = ws[0] # if the worksheet analyst is not assigned, the worksheet can't be transitioned. if ws.getAnalyst() and not skip(ws, "submit", peek=True): all_submitted = True for a in ws.getAnalyses(): if workflow.getInfoFor(a, 'review_state') in \ ('sample_due', 'sample_received', 'assigned',): all_submitted = False break if all_submitted: workflow.doActionFor(ws, 'submit') # If no problem with attachments, do 'attach' action. can_attach = True if not self.getAttachment(): service = self.getService() if service.getAttachmentOption() == 'r': can_attach = False if can_attach: workflow.doActionFor(self, 'attach')
def workflow_script_verify(self): if skip(self, "verify"): return workflow = getToolByName(self, "portal_workflow") self.reindexObject(idxs=["review_state"]) # If all other analyses on the worksheet are verified, # then verify the worksheet. ws = self.getBackReferences("WorksheetAnalysis") if ws and len(ws) > 0: ws = ws[0] ws_state = workflow.getInfoFor(ws, "review_state") if ws_state == "to_be_verified" and not skip(ws, "verify", peek=True): all_verified = True for a in ws.getAnalyses(): if workflow.getInfoFor(a, "review_state") in ( "sample_due", "sample_received", "attachment_due", "to_be_verified", "assigned", ): all_verified = False break if all_verified: if not "verify all analyses" in self.REQUEST["workflow_skiplist"]: self.REQUEST["workflow_skiplist"].append("verify all analyses") workflow.doActionFor(ws, "verify")
def workflow_script_attach(self): if skip(self, "attach"): return workflow = getToolByName(self, "portal_workflow") self.reindexObject(idxs=["review_state"]) # If all analyses on the worksheet have been attached, # then attach the worksheet. ws = self.getBackReferences("WorksheetAnalysis") ws = ws[0] ws_state = workflow.getInfoFor(ws, "review_state") if ws_state == "attachment_due" and not skip(ws, "attach", peek=True): can_attach = True for a in ws.getAnalyses(): if workflow.getInfoFor(a, "review_state") in ( "to_be_sampled", "to_be_preserved", "sample_due", "sample_received", "attachment_due", "assigned", ): can_attach = False break if can_attach: workflow.doActionFor(ws, "attach") return
def ObjectInitializedEventHandler(instance, event): # This handler fires for DuplicateAnalysis because # DuplicateAnalysis also provides IAnalysis. # DuplicateAnalysis doesn't have analysis_workflow. if instance.portal_type == "DuplicateAnalysis": return workflow = getToolByName(instance, 'portal_workflow') ar = instance.aq_parent ar_state = workflow.getInfoFor(ar, 'review_state') ar_ws_state = workflow.getInfoFor(ar, 'worksheetanalysis_review_state') # Set the state of the analysis depending on the state of the AR. if ar_state in ('sample_registered', 'to_be_sampled', 'sampled', 'to_be_preserved', 'sample_due', 'sample_received'): changeWorkflowState(instance, "bika_analysis_workflow", ar_state) elif ar_state in ('to_be_verified'): # Apply to AR only; we don't want this transition to cascade. if 'workflow_skiplist' not in ar.REQUEST: ar.REQUEST['workflow_skiplist'] = [] ar.REQUEST['workflow_skiplist'].append("retract all analyses") workflow.doActionFor(ar, 'retract') ar.REQUEST['workflow_skiplist'].remove("retract all analyses") if ar_ws_state == 'assigned': workflow.doActionFor(ar, 'unassign') skip(ar, 'unassign', unskip=True) instance.updateDueDate() return
def workflow_script_verify(self): if skip(self, "verify"): return workflow = getToolByName(self, 'portal_workflow') # If all other analyses on the worksheet are verified, # then verify the worksheet. ws = self.getBackReferences('WorksheetAnalysis') if ws and len(ws) > 0: ws = ws[0] ws_state = workflow.getInfoFor(ws, 'review_state') if ws_state == 'to_be_verified' and not skip(ws, "verify", peek=True): all_verified = True for a in ws.getAnalyses(): if workflow.getInfoFor(a, 'review_state') in \ ('sample_due', 'sample_received', 'attachment_due', 'to_be_verified', 'assigned'): all_verified = False break if all_verified: if "verify all analyses" \ not in self.REQUEST['workflow_skiplist']: self.REQUEST["workflow_skiplist"].append( "verify all analyses") workflow.doActionFor(ws, "verify") self.reindexObject()
def workflow_script_verify(self): if skip(self, "verify"): return workflow = getToolByName(self, 'portal_workflow') self.reindexObject(idxs=[ "review_state", ]) # If all other analyses on the worksheet are verified, # then verify the worksheet. ws = self.getBackReferences('WorksheetAnalysis') ws = ws[0] ws_state = workflow.getInfoFor(ws, 'review_state') if ws_state == 'to_be_verified' and not skip(ws, "verify", peek=True): all_verified = True for a in ws.getAnalyses(): if workflow.getInfoFor(a, 'review_state') in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received', 'attachment_due', 'to_be_verified', 'assigned'): all_verified = False break if all_verified: if not "verify all analyses" in self.REQUEST[ 'workflow_skiplist']: self.REQUEST["workflow_skiplist"].append( "verify all analyses") workflow.doActionFor(ws, "verify")
def __call__(self): form = self.request.form plone.protect.CheckAuthenticator(form) workflow = getToolByName(self.context, 'portal_workflow') if self.destination_url == "": self.destination_url = self.request.get_header( "referer", self.context.absolute_url()) action, came_from = self._get_form_workflow_action() # transition the context object (plone edit bar dropdown) if came_from == "workflow_action": obj = self.context # the only actions allowed on inactive/cancelled # items are "reinstate" and "activate" if not isActive(obj) and action not in ('reinstate', 'activate'): message = self.context.translate(_('Item is inactive.')) self.context.plone_utils.addPortalMessage(message, 'info') self.request.response.redirect(self.destination_url) return if not skip(obj, action, peek=True): allowed_transitions = [] for t in workflow.getTransitionsFor(obj): allowed_transitions.append(t['id']) if action in allowed_transitions: workflow.doActionFor(obj, action) self.request.response.redirect(self.destination_url) return # transition selected items from the bika_listing/Table. transitioned = [] selected_items = self._get_selected_items() for uid, item in selected_items.items(): # the only actions allowed on inactive/cancelled # items are "reinstate" and "activate" if not isActive(item) and action not in ('reinstate', 'activate'): continue if not skip(item, action, peek=True): allowed_transitions = [] for t in workflow.getTransitionsFor(item): allowed_transitions.append(t['id']) if action in allowed_transitions: doActionFor(item, action) transitioned.append(item.Title()) if len(transitioned) > 0: message = self.context.translate(PMF('Changes saved.')) self.context.plone_utils.addPortalMessage(message, 'info') # automatic label printing if action == 'receive' and 'receive' in self.portal.bika_setup.getAutoPrintLabels( ): q = "/sticker?size=%s&items=" % ( self.portal.bika_setup.getAutoLabelSize()) # selected_items is a list of UIDs (stickers for AR_add use IDs) q += ",".join([i.getId() for i in selected_items.values()]) self.request.response.redirect(self.context.absolute_url() + q) else: self.request.response.redirect(self.destination_url)
def __call__(self): form = self.request.form plone.protect.CheckAuthenticator(form) workflow = getToolByName(self.context, 'portal_workflow') if self.destination_url == "": self.destination_url = self.request.get_header("referer", self.context.absolute_url()) action, came_from = self._get_form_workflow_action() # transition the context object (plone edit bar dropdown) if came_from == "workflow_action": obj = self.context # the only actions allowed on inactive/cancelled # items are "reinstate" and "activate" if not isActive(obj) and action not in ('reinstate', 'activate'): message = self.context.translate(_('Item is inactive.')) self.context.plone_utils.addPortalMessage(message, 'info') self.request.response.redirect(self.destination_url) return if not skip(obj, action, peek=True): allowed_transitions = [] for t in workflow.getTransitionsFor(obj): allowed_transitions.append(t['id']) if action in allowed_transitions: workflow.doActionFor(obj, action) self.request.response.redirect(self.destination_url) return # transition selected items from the bika_listing/Table. transitioned = [] selected_items = self._get_selected_items() for uid, item in selected_items.items(): # the only actions allowed on inactive/cancelled # items are "reinstate" and "activate" if not isActive(item) and action not in ('reinstate', 'activate'): continue if not skip(item, action, peek=True): allowed_transitions = [] for t in workflow.getTransitionsFor(item): allowed_transitions.append(t['id']) if action in allowed_transitions: doActionFor(item, action) transitioned.append(item.Title()) if len(transitioned) > 0: message = self.context.translate(PMF('Changes saved.')) self.context.plone_utils.addPortalMessage(message, 'info') # automatic label printing if action == 'receive' and 'receive' in self.context.bika_setup.getAutoPrintLabels(): q = "/sticker?size=%s&items=" % (self.context.bika_setup.getAutoLabelSize()) # selected_items is a list of UIDs (stickers for AR_add use IDs) q += ",".join([i.getId() for i in selected_items.values()]) self.request.response.redirect(self.context.absolute_url() + q) else: self.request.response.redirect(self.destination_url)
def workflow_script_unassign(self): if skip(self, "unassign"): return workflow = getToolByName(self, 'portal_workflow') self.reindexObject(idxs=["review_state", ]) rc = getToolByName(self, REFERENCE_CATALOG) wsUID = self.REQUEST['context_uid'] ws = rc.lookupObject(wsUID) # May need to promote the Worksheet's review_state # if all other analyses are at a higher state than this one was. # (or maybe retract it if there are no analyses left) # Note: duplicates, controls and blanks have 'assigned' as a review_state. can_submit = True can_attach = True can_verify = True ws_empty = False analyses = ws.getAnalyses() # We flag this worksheet as empty if there is ONE UNASSIGNED # analysis left: worksheet.removeAnalysis() hasn't removed it from # the layout yet at this stage. if len(analyses) == 1 \ and workflow.getInfoFor(analyses[0], 'review_state') == 'unassigned': ws_empty = True for a in analyses: a_state = workflow.getInfoFor(a, 'review_state') if a_state in \ ('assigned', 'sample_due', 'sample_received',): can_submit = False else: if not ws.getAnalyst(): can_submit = False if a_state in \ ('assigned', 'sample_due', 'sample_received', 'attachment_due',): can_attach = False if a_state in \ ('assigned', 'sample_due', 'sample_received', 'attachment_due', 'to_be_verified',): can_verify = False if not ws_empty: # Note: WS adds itself to the skiplist so we have to take it off again # to allow multiple promotions (maybe by more than one self). if can_submit and workflow.getInfoFor(ws, 'review_state') == 'open': workflow.doActionFor(ws, 'submit') skip(ws, 'submit', unskip=True) if can_attach and workflow.getInfoFor(ws, 'review_state') == 'attachment_due': workflow.doActionFor(ws, 'attach') skip(ws, 'attach', unskip=True) if can_verify and workflow.getInfoFor(ws, 'review_state') == 'to_be_verified': self.REQUEST["workflow_skiplist"].append('verify all analyses') workflow.doActionFor(ws, 'verify') skip(ws, 'verify', unskip=True) else: if workflow.getInfoFor(ws, 'review_state') != 'open': workflow.doActionFor(ws, 'retract') skip(ws, 'retract', unskip=True)
def workflow_script_retract(self): if skip(self, "retract"): return workflow = getToolByName(self, 'portal_workflow') self.reindexObject(idxs=["review_state", ]) # Escalate action to the Worksheet. ws = self.getBackReferences('WorksheetAnalysis') ws = ws[0] if not skip(ws, "retract", peek=True): if workflow.getInfoFor(ws, 'review_state') == 'open': skip(ws, "retract") else: if not "retract all analyses" in self.REQUEST['workflow_skiplist']: self.REQUEST["workflow_skiplist"].append("retract all analyses") workflow.doActionFor(ws, 'retract')
def submitTransition(self, action, came_from, items): """ Performs the action's transition for the specified items Returns (numtransitions, destination), where: - numtransitions: the number of objects successfully transitioned. If no objects have been successfully transitioned, gets 0 value - destination: the destination url to be loaded immediately """ dest = None transitioned = [] workflow = getToolByName(self.context, 'portal_workflow') # transition selected items from the bika_listing/Table. for item in items: # the only actions allowed on inactive/cancelled # items are "reinstate" and "activate" if not isActive(item) and action not in ('reinstate', 'activate'): continue if not skip(item, action, peek=True): allowed_transitions = [it['id'] for it in \ workflow.getTransitionsFor(item)] if action in allowed_transitions: success, message = doActionFor(item, action) if success: transitioned.append(item.id) else: self.context.plone_utils.addPortalMessage(message, 'error') # automatic label printing if transitioned and action == 'receive' \ and 'receive' in self.portal.bika_setup.getAutoPrintStickers(): q = "/sticker?template=%s&items=" % (self.portal.bika_setup.getAutoStickerTemplate()) # selected_items is a list of UIDs (stickers for AR_add use IDs) q += ",".join(transitioned) dest = self.context.absolute_url() + q return len(transitioned), dest
def AfterTransitionEventHandler(instance, event): # creation doesn't have a 'transition' if not event.transition: return action_id = event.transition.id if skip(instance, action_id): return workflow = getToolByName(instance, 'portal_workflow') if action_id == "attach": instance.reindexObject(idxs = ["review_state", ]) # Don't cascade. Shouldn't be attaching WSs for now (if ever). return elif action_id == "submit": # Don't cascade. Shouldn't be submitting WSs directly for now, # except edge cases where all analyses are already submitted, # but instance was held back until an analyst was assigned. instance.reindexObject(idxs = ["review_state", ]) can_attach = True for a in instance.getAnalyses(): if workflow.getInfoFor(a, 'review_state') in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received', 'attachment_due', 'assigned',): # Note: referenceanalyses and duplicateanalyses can still have review_state = "assigned". can_attach = False break if can_attach: doActionFor(instance, 'attach') elif action_id == "retract": instance.reindexObject(idxs = ["review_state", ]) if not "retract all analyses" in instance.REQUEST['workflow_skiplist']: # retract all analyses in this instance. # (NB: don't retract if it's verified) analyses = instance.getAnalyses() for analysis in analyses: if workflow.getInfoFor(analysis, 'review_state', '') not in ('attachment_due', 'to_be_verified',): continue doActionFor(analysis, 'retract') elif action_id == "verify": instance.reindexObject(idxs = ["review_state", ]) if not "verify all analyses" in instance.REQUEST['workflow_skiplist']: # verify all analyses in this instance. analyses = instance.getSortedAnalyses() for analysis in analyses: if workflow.getInfoFor(analysis, 'review_state', '') != 'to_be_verified': continue doActionFor(analysis, "verify") if "verify all analyses" in instance.REQUEST['workflow_skiplist']: instance.REQUEST['workflow_skiplist'].remove("verify all analyses") return
def ObjectInitializedEventHandler(instance, event): # This handler fires for DuplicateAnalysis because # DuplicateAnalysis also provides IAnalysis. # DuplicateAnalysis doesn't have analysis_workflow. if instance.portal_type == "DuplicateAnalysis": return # 'receive' instance if AR is received. # Adding a new instance to an AR retracts the AR to 'sample_received' # AR may have to be unassigned too ar = instance.aq_parent ar_UID = ar.UID() wf = getToolByName(instance, 'portal_workflow') ar_state = wf.getInfoFor(ar, 'review_state') ar_ws_state = wf.getInfoFor(ar, 'worksheetanalysis_review_state') if ar_state not in ('sample_registered', 'sampled', 'to_be_sampled', 'to_be_preserved', 'sample_due'): try: wf.doActionFor(instance, 'receive') except WorkflowException: pass # Note: AR adds itself to the skiplist so we have to take it off again # to allow possible promotions if other analyses are deleted. if ar_state not in ('sample_registered', 'sampled', 'to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received'): if not instance.REQUEST.has_key('workflow_skiplist'): instance.REQUEST['workflow_skiplist'] = [ 'retract all analyses', ] else: instance.REQUEST["workflow_skiplist"].append( 'retract all analyses') doActionFor(ar, 'retract') skip(ar, 'retract', unskip=True) if ar_ws_state == 'assigned': wf.doActionFor(ar, 'unassign') skip(ar, 'unassign', unskip=True) return
def ObjectInitializedEventHandler(instance, event): # This handler fires for DuplicateAnalysis because # DuplicateAnalysis also provides IAnalysis. # DuplicateAnalysis doesn't have analysis_workflow. if instance.portal_type == "DuplicateAnalysis": return # 'receive' instance if AR is received. # Adding a new instance to an AR retracts the AR to 'sample_received' # AR may have to be unassigned too ar = instance.aq_parent ar_UID = ar.UID() wf = getToolByName(instance, 'portal_workflow') ar_state = wf.getInfoFor(ar, 'review_state') ar_ws_state = wf.getInfoFor(ar, 'worksheetanalysis_review_state') if ar_state not in ('sample_registered', 'sampled', 'to_be_sampled', 'to_be_preserved', 'sample_due'): try: wf.doActionFor(instance, 'receive') except WorkflowException: pass # Note: AR adds itself to the skiplist so we have to take it off again # to allow possible promotions if other analyses are deleted. if ar_state not in ('sample_registered', 'sampled', 'to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received'): if not instance.REQUEST.has_key('workflow_skiplist'): instance.REQUEST['workflow_skiplist'] = ['retract all analyses', ] else: instance.REQUEST["workflow_skiplist"].append('retract all analyses') doActionFor(ar, 'retract') skip(ar, 'retract', unskip=True) if ar_ws_state == 'assigned': wf.doActionFor(ar, 'unassign') skip(ar, 'unassign', unskip=True) return
def workflow_script_attach(self): if skip(self, "attach"): return workflow = getToolByName(self, 'portal_workflow') self.reindexObject(idxs=["review_state", ]) # If all analyses on the worksheet have been attached, # then attach the worksheet. ws = self.getBackReferences('WorksheetAnalysis') ws = ws[0] ws_state = workflow.getInfoFor(ws, 'review_state') if ws_state == 'attachment_due' and not skip(ws, "attach", peek=True): can_attach = True for a in ws.getAnalyses(): if workflow.getInfoFor(a, 'review_state') in \ ('sample_due', 'sample_received', 'attachment_due', 'assigned',): can_attach = False break if can_attach: workflow.doActionFor(ws, 'attach')
def AfterTransitionEventHandler(instance, event): # creation doesn't have a 'transition' if not event.transition: return debug_mode = App.config.getConfiguration().debug_mode if not debug_mode: return if not skip(instance, event.transition.id, peek=True): logger.debug("Started transition %s on %s" % (event.transition.id, instance))
def ObjectInitializedEventHandler(instance, event): # This handler fires for DuplicateAnalysis because # DuplicateAnalysis also provides IAnalysis. # DuplicateAnalysis doesn't have analysis_workflow. if instance.portal_type == "DuplicateAnalysis": return if instance.portal_type == 'Analysis': alsoProvides(instance, IRoutineAnalysis) workflow = getToolByName(instance, 'portal_workflow') ar = instance.aq_parent ar_state = workflow.getInfoFor(ar, 'review_state') ar_ws_state = workflow.getInfoFor(ar, 'worksheetanalysis_review_state') # Set the state of the analysis depending on the state of the AR. if ar_state in ('sample_registered', 'to_be_sampled', 'sampled', 'to_be_preserved', 'sample_due', 'sample_received'): changeWorkflowState(instance, "bika_analysis_workflow", ar_state) elif ar_state in ('to_be_verified'): # Apply to AR only; we don't want this transition to cascade. if 'workflow_skiplist' not in ar.REQUEST: ar.REQUEST['workflow_skiplist'] = [] ar.REQUEST['workflow_skiplist'].append("retract all analyses") workflow.doActionFor(ar, 'retract') ar.REQUEST['workflow_skiplist'].remove("retract all analyses") if ar_ws_state == 'assigned': workflow.doActionFor(ar, 'unassign') skip(ar, 'unassign', unskip=True) instance.updateDueDate() return
def workflow_script_assign(self): if skip(self, "assign"): return workflow = getToolByName(self, "portal_workflow") self.reindexObject(idxs=["review_state"]) rc = getToolByName(self, REFERENCE_CATALOG) wsUID = self.REQUEST["context_uid"] ws = rc.lookupObject(wsUID) # retract the worksheet to 'open' ws_state = workflow.getInfoFor(ws, "review_state") if ws_state != "open": if "workflow_skiplist" not in self.REQUEST: self.REQUEST["workflow_skiplist"] = ["retract all analyses"] else: self.REQUEST["workflow_skiplist"].append("retract all analyses") workflow.doActionFor(ws, "retract")
def workflow_script_assign(self): if skip(self, "assign"): return workflow = getToolByName(self, 'portal_workflow') self.reindexObject(idxs=["review_state", ]) rc = getToolByName(self, REFERENCE_CATALOG) wsUID = self.REQUEST['context_uid'] ws = rc.lookupObject(wsUID) # retract the worksheet to 'open' ws_state = workflow.getInfoFor(ws, 'review_state') if ws_state != 'open': if 'workflow_skiplist' not in self.REQUEST: self.REQUEST['workflow_skiplist'] = ['retract all analyses', ] else: self.REQUEST["workflow_skiplist"].append('retract all analyses') workflow.doActionFor(ws, 'retract')
def AfterTransitionEventHandler(instance, event): # creation doesn't have a 'transition' if not event.transition: return action_id = event.transition.id if skip(instance, action_id): return workflow = getToolByName(instance, 'portal_workflow') membership_tool = getToolByName(instance, 'portal_membership') member = membership_tool.getAuthenticatedMember() sample = instance.aq_parent sample_state = workflow.getInfoFor(sample, 'review_state') if action_id == "sample": # Transition our analyses analyses = instance.getBackReferences('AnalysisSamplePartition') for analysis in analyses: doActionFor(analysis, action_id) # if all our siblings are now up to date, promote sample and ARs. parts = sample.objectValues("SamplePartition") if parts: lower_states = ['to_be_sampled',] escalate = True for part in parts: pstate = workflow.getInfoFor(part, 'review_state') if pstate in lower_states: escalate = False if escalate: doActionFor(sample, action_id) for ar in sample.getAnalysisRequests(): doActionFor(ar, action_id) elif action_id == "to_be_preserved": # Transition our analyses analyses = instance.getBackReferences('AnalysisSamplePartition') for analysis in analyses: doActionFor(analysis, action_id) # if all our siblings are now up to date, promote sample and ARs. parts = sample.objectValues("SamplePartition") if parts: lower_states = ['to_be_sampled', 'to_be_preserved',] escalate = True for part in parts: if workflow.getInfoFor(part, 'review_state') in lower_states: escalate = False if escalate: doActionFor(sample, action_id) for ar in sample.getAnalysisRequests(): doActionFor(ar, action_id) elif action_id == "sample_due": # Transition our analyses analyses = instance.getBackReferences('AnalysisSamplePartition') for analysis in analyses: doActionFor(analysis, action_id) # if all our siblings are now up to date, promote sample and ARs. parts = sample.objectValues("SamplePartition") if parts: lower_states = ['to_be_preserved',] escalate = True for part in parts: pstate = workflow.getInfoFor(part, 'review_state') if pstate in lower_states: escalate = False if escalate: doActionFor(sample, action_id) for ar in sample.getAnalysisRequests(): doActionFor(ar, action_id) elif action_id == "preserve": # Transition our analyses analyses = instance.getBackReferences('AnalysisSamplePartition') if analyses: for analysis in analyses: doActionFor(analysis, action_id) # if all our siblings are now up to date, promote sample and ARs. parts = sample.objectValues("SamplePartition") if parts: lower_states = ['to_be_sampled', 'to_be_preserved', ] escalate = True for part in parts: if workflow.getInfoFor(part, 'review_state') in lower_states: escalate = False if escalate: doActionFor(sample, action_id) for ar in sample.getAnalysisRequests(): doActionFor(ar, action_id) elif action_id == "receive": if sample.getSamplingDate() > DateTime(): raise WorkflowException instance.setDateReceived(DateTime()) instance.reindexObject(idxs = ["getDateReceived", ]) # Transition our analyses analyses = instance.getBackReferences('AnalysisSamplePartition') for analysis in analyses: doActionFor(analysis, action_id) # if all sibling partitions are received, promote sample if not skip(sample, action_id, peek=True): due = [sp for sp in sample.objectValues("SamplePartition") if workflow.getInfoFor(sp, 'review_state') == 'sample_due'] if sample_state == 'sample_due' and not due: doActionFor(sample, 'receive') elif action_id == "expire": instance.setDateExpired(DateTime()) instance.reindexObject(idxs = ["review_state", "getDateExpired", ]) #--------------------- # Secondary workflows: #--------------------- elif action_id == "reinstate": instance.reindexObject(idxs = ["cancellation_state", ]) sample_c_state = workflow.getInfoFor(sample, 'cancellation_state') # if all sibling partitions are active, activate sample if not skip(sample, action_id, peek=True): cancelled = [sp for sp in sample.objectValues("SamplePartition") if workflow.getInfoFor(sp, 'cancellation_state') == 'cancelled'] if sample_c_state == 'cancelled' and not cancelled: workflow.doActionFor(sample, 'reinstate') elif action_id == "cancel": instance.reindexObject(idxs = ["cancellation_state", ]) sample_c_state = workflow.getInfoFor(sample, 'cancellation_state') # if all sibling partitions are cancelled, cancel sample if not skip(sample, action_id, peek=True): active = [sp for sp in sample.objectValues("SamplePartition") if workflow.getInfoFor(sp, 'cancellation_state') == 'active'] if sample_c_state == 'active' and not active: workflow.doActionFor(sample, 'cancel') return
def __call__(self): form = self.request.form CheckAuthenticator(form) workflow = getToolByName(self.context, 'portal_workflow') rc = getToolByName(self.context, REFERENCE_CATALOG) bsc = getToolByName(self.context, 'bika_setup_catalog') bac = getToolByName(self.context, 'bika_analysis_catalog') action, came_from = WorkflowAction._get_form_workflow_action(self) if action == 'submit': # Submit the form. Saves the results, methods, etc. self.submit() ## assign elif action == 'assign': if not self.context.checkUserManage(): self.request.response.redirect(self.context.absolute_url()) return analysis_uids = form.get("uids", []) if analysis_uids: # We retrieve the analyses from the database sorted by AR ID # ascending, so the positions of the ARs inside the WS are # consistent with the order of the ARs catalog = get_tool(CATALOG_ANALYSIS_LISTING) brains = catalog({'UID': analysis_uids, 'sort_on': 'getRequestID'}) # Now, we need the analyses within a request ID to be sorted by # sortkey (sortable_title index), so it will appear in the same # order as they appear in Analyses list from AR view curr_arid = None curr_brains = [] sorted_brains = [] for brain in brains: arid = brain.getRequestID if curr_arid != arid: # Sort the brains we've collected until now, that belong to # the same Analysis Request curr_brains.sort(key=attrgetter('getPrioritySortkey')) sorted_brains.extend(curr_brains) curr_arid = arid curr_brains = [] # Now we are inside the same AR curr_brains.append(brain) continue # Sort the last set of brains we've collected curr_brains.sort(key=attrgetter('getPrioritySortkey')) sorted_brains.extend(curr_brains) # Add analyses in the worksheet for brain in sorted_brains: analysis = brain.getObject() self.context.addAnalysis(analysis) self.destination_url = self.context.absolute_url() self.request.response.redirect(self.destination_url) ## unassign elif action == 'unassign': if not self.context.checkUserManage(): self.request.response.redirect(self.context.absolute_url()) return selected_analyses = WorkflowAction._get_selected_items(self) selected_analysis_uids = selected_analyses.keys() for analysis_uid in selected_analysis_uids: try: analysis = bac(UID=analysis_uid)[0].getObject() except IndexError: # Duplicate analyses are removed when their analyses # get removed, so indexerror is expected. continue if skip(analysis, action, peek=True): continue self.context.removeAnalysis(analysis) 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) ## verify elif action == 'verify': # default bika_listing.py/WorkflowAction, but then go to view screen. self.destination_url = self.context.absolute_url() return self.workflow_action_default(action='verify', came_from=came_from) else: # default bika_listing.py/WorkflowAction for other transitions WorkflowAction.__call__(self)
def __call__(self): form = self.request.form plone.protect.CheckAuthenticator(form) rc = getToolByName(self.context, REFERENCE_CATALOG) bsc = getToolByName(self.context, 'bika_setup_catalog') bac = getToolByName(self.context, 'bika_analysis_catalog') action, came_from = WorkflowAction._get_form_workflow_action(self) if action == 'submit': # Submit the form. Saves the results, methods, etc. self.submit() ## assign elif action == 'assign': if not self.context.checkUserManage(): self.request.response.redirect(self.context.absolute_url()) return analyses = WorkflowAction._get_selected_items(self).values() if analyses: analyses = sorted(analyses, key=methodcaller('getRequestID')) for analysis in analyses: if self.isAddable(analysis): self.context.addAnalysis(analysis) self.destination_url = self.context.absolute_url() self.request.response.redirect(self.destination_url) ## unassign elif action == 'unassign': if not self.context.checkUserManage(): self.request.response.redirect(self.context.absolute_url()) return selected_analyses = WorkflowAction._get_selected_items(self) selected_analysis_uids = selected_analyses.keys() for analysis_uid in selected_analysis_uids: try: analysis = bac(UID=analysis_uid)[0].getObject() except IndexError: # Duplicate analyses are removed when their analyses # get removed, so indexerror is expected. continue if skip(analysis, action, peek=True): continue self.context.removeAnalysis(analysis) 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) ## verify elif action == 'verify': # default bika_listing.py/WorkflowAction, but then go to view screen. self.destination_url = self.context.absolute_url() return self.workflow_action_default(action='verify', came_from=came_from) else: # default bika_listing.py/WorkflowAction for other transitions WorkflowAction.__call__(self)
def AfterTransitionEventHandler(instance, event): # Note: Don't have dependencies or dependents, not on an AR #---------------------------------------------------------- # creation doesn't have a 'transition' if not event.transition: return action_id = event.transition.id if skip(instance, action_id): return if action_id == "attach": wf = getToolByName(instance, 'portal_workflow') instance.reindexObject(idxs = ["review_state", ]) # If all analyses on the worksheet have been attached, # then attach the worksheet. ws = instance.getBackReferences('WorksheetAnalysis') ws = ws[0] ws_state = wf.getInfoFor(ws, 'review_state') if ws_state == 'attachment_due' and not skip(ws, action_id, peek=True): can_attach = True for a in ws.getAnalyses(): if wf.getInfoFor(a, 'review_state') in \ ('sample_due', 'sample_received', 'attachment_due', 'assigned',): can_attach = False break if can_attach: wf.doActionFor(ws, 'attach') return #------------------------------------------------------ # End of "attach" code, back to your basic nightmare... #------------------------------------------------------ wf = getToolByName(instance, 'portal_workflow') if action_id == "submit": instance.reindexObject(idxs = ["review_state", ]) # If all analyses on the worksheet have been submitted, # then submit the worksheet. ws = instance.getBackReferences('WorksheetAnalysis') ws = ws[0] # if the worksheet analyst is not assigned, the worksheet can't be transitioned. if ws.getAnalyst() and not skip(ws, action_id, peek=True): all_submitted = True for a in ws.getAnalyses(): if wf.getInfoFor(a, 'review_state') in \ ('sample_due', 'sample_received', 'assigned',): all_submitted = False break if all_submitted: wf.doActionFor(ws, 'submit') # If no problem with attachments, do 'attach' action for this instance. can_attach = True if not instance.getAttachment(): service = instance.getService() if service.getAttachmentOption() == 'r': can_attach = False if can_attach: wf.doActionFor(instance, 'attach') elif action_id == "retract": instance.reindexObject(idxs = ["review_state", ]) # Escalate action to the Worksheet. ws = instance.getBackReferences('WorksheetAnalysis') ws = ws[0] if not skip(ws, action_id, peek=True): if wf.getInfoFor(ws, 'review_state') == 'open': skip(ws, action_id) else: if not "retract all analyses" in instance.REQUEST['workflow_skiplist']: instance.REQUEST["workflow_skiplist"].append("retract all analyses") wf.doActionFor(ws, 'retract') elif action_id == "verify": instance.reindexObject(idxs = ["review_state", ]) # If all other analyses on the worksheet are verified, # then verify the worksheet. ws = instance.getBackReferences('WorksheetAnalysis') ws = ws[0] ws_state = wf.getInfoFor(ws, 'review_state') if ws_state == 'to_be_verified' and not skip(ws, action_id, peek=True): all_verified = True for a in ws.getAnalyses(): if wf.getInfoFor(a, 'review_state') in \ ('sample_due', 'sample_received', 'attachment_due', 'to_be_verified', 'assigned'): all_verified = False break if all_verified: if not "verify all analyses" in instance.REQUEST['workflow_skiplist']: instance.REQUEST["workflow_skiplist"].append("verify all analyses") wf.doActionFor(ws, "verify") elif action_id == "assign": instance.reindexObject(idxs = ["review_state", ]) rc = getToolByName(instance, REFERENCE_CATALOG) wsUID = instance.REQUEST['context_uid'] ws = rc.lookupObject(wsUID) # retract the worksheet to 'open' ws_state = wf.getInfoFor(ws, 'review_state') if ws_state != 'open': if not instance.REQUEST.has_key('workflow_skiplist'): instance.REQUEST['workflow_skiplist'] = ['retract all analyses', ] else: instance.REQUEST["workflow_skiplist"].append('retract all analyses') wf.doActionFor(ws, 'retract') elif action_id == "unassign": instance.reindexObject(idxs = ["review_state", ]) rc = getToolByName(instance, REFERENCE_CATALOG) wsUID = instance.REQUEST['context_uid'] ws = rc.lookupObject(wsUID) # May need to promote the Worksheet's review_state # if all other analyses are at a higher state than this one was. # (or maybe retract it if there are no analyses left) # Note: duplicates, controls and blanks have 'assigned' as a review_state. can_submit = True can_attach = True can_verify = True ws_empty = True for a in ws.getAnalyses(): ws_empty = False a_state = wf.getInfoFor(a, 'review_state') if a_state in \ ('assigned', 'sample_due', 'sample_received',): can_submit = False else: if not ws.getAnalyst(): can_submit = False if a_state in \ ('assigned', 'sample_due', 'sample_received', 'attachment_due',): can_attach = False if a_state in \ ('assigned', 'sample_due', 'sample_received', 'attachment_due', 'to_be_verified',): can_verify = False if not ws_empty: # Note: WS adds itself to the skiplist so we have to take it off again # to allow multiple promotions (maybe by more than one instance). if can_submit and wf.getInfoFor(ws, 'review_state') == 'open': wf.doActionFor(ws, 'submit') unskip(ws, 'submit', unskip=True) if can_attach and wf.getInfoFor(ws, 'review_state') == 'attachment_due': wf.doActionFor(ws, 'attach') unskip(ws, 'attach', unskip=True) if can_verify and wf.getInfoFor(ws, 'review_state') == 'to_be_verified': instance.REQUEST["workflow_skiplist"].append('verify all analyses') wf.doActionFor(ws, 'verify') unskip(ws, 'verify', unskip=True) else: if wf.getInfoFor(ws, 'review_state') != 'open': wf.doActionFor(ws, 'retract') unskip(ws, 'retract', unskip=True) return
def __call__(self): form = self.request.form plone.protect.CheckAuthenticator(form) workflow = getToolByName(self.context, 'portal_workflow') rc = getToolByName(self.context, REFERENCE_CATALOG) bsc = getToolByName(self.context, 'bika_setup_catalog') bac = getToolByName(self.context, 'bika_analysis_catalog') action, came_from = WorkflowAction._get_form_workflow_action(self) if action == 'submit': # Submit the form. Saves the results, methods, etc. self.submit() ## assign elif action == 'assign': if not self.context.checkUserManage(): self.request.response.redirect(self.context.absolute_url()) return selected_analyses = WorkflowAction._get_selected_items(self) selected_analysis_uids = selected_analyses.keys() if selected_analyses: for uid in selected_analysis_uids: analysis = rc.lookupObject(uid) # Double-check the state first if (workflow.getInfoFor(analysis, 'worksheetanalysis_review_state') == 'unassigned' and workflow.getInfoFor(analysis, 'review_state') == 'sample_received' and workflow.getInfoFor(analysis, 'cancellation_state') == 'active'): self.context.addAnalysis(analysis) self.destination_url = self.context.absolute_url() self.request.response.redirect(self.destination_url) ## unassign elif action == 'unassign': if not self.context.checkUserManage(): self.request.response.redirect(self.context.absolute_url()) return selected_analyses = WorkflowAction._get_selected_items(self) selected_analysis_uids = selected_analyses.keys() for analysis_uid in selected_analysis_uids: try: analysis = bac(UID=analysis_uid)[0].getObject() except IndexError: # Duplicate analyses are removed when their analyses # get removed, so indexerror is expected. continue if skip(analysis, action, peek=True): continue self.context.removeAnalysis(analysis) 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) ## verify elif action == 'verify': # default bika_listing.py/WorkflowAction, but then go to view screen. self.destination_url = self.context.absolute_url() return self.workflow_action_default(action='verify', came_from='edit') else: # default bika_listing.py/WorkflowAction for other transitions WorkflowAction.__call__(self)
def ObjectRemovedEventHandler(instance, event): # TODO Workflow - Review all this function and normalize # May need to promote the AR's review_state # if all other analyses are at a higher state than this one was. workflow = getToolByName(instance, 'portal_workflow') ar = instance.getRequest() can_submit = True can_attach = True can_verify = True can_publish = True # We add this manually here, because during admin/ZMI removal, # it may possibly not be added by the workflow code. if not 'workflow_skiplist' in instance.REQUEST: instance.REQUEST['workflow_skiplist'] = [] for a in ar.getAnalyses(): a_state = a.review_state if a_state in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received',): can_submit = False if a_state in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received', 'attachment_due',): can_attach = False if a_state in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received', 'attachment_due', 'to_be_verified',): can_verify = False if a_state in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received', 'attachment_due', 'to_be_verified', 'verified',): can_publish = False # Note: AR adds itself to the skiplist so we have to take it off again # to allow multiple promotions (maybe by more than one deleted instance). if can_submit and workflow.getInfoFor(ar, 'review_state') == 'sample_received': try: workflow.doActionFor(ar, 'submit') except WorkflowException: pass skip(ar, 'submit', unskip=True) if can_attach and workflow.getInfoFor(ar, 'review_state') == 'attachment_due': try: workflow.doActionFor(ar, 'attach') except WorkflowException: pass skip(ar, 'attach', unskip=True) if can_verify and workflow.getInfoFor(ar, 'review_state') == 'to_be_verified': instance.REQUEST["workflow_skiplist"].append('verify all analyses') try: workflow.doActionFor(ar, 'verify') except WorkflowException: pass skip(ar, 'verify', unskip=True) if can_publish and workflow.getInfoFor(ar, 'review_state') == 'verified': instance.REQUEST["workflow_skiplist"].append('publish all analyses') try: workflow.doActionFor(ar, 'publish') except WorkflowException: pass skip(ar, 'publish', unskip=True) return
def AfterTransitionEventHandler(instance, event): # creation doesn't have a 'transition' if not event.transition: return action_id = event.transition.id if skip(instance, action_id): return workflow = getToolByName(instance, 'portal_workflow') if action_id == "attach": instance.reindexObject(idxs = ["review_state", ]) # Don't cascade. Shouldn't be attaching ARs for now (if ever). return elif action_id == "sampled": # transition our sample sample = instance.getSample() if not skip(sample, action_id, peek=True): workflow.doActionFor(sample, action_id) elif action_id == "to_be_preserved": pass elif action_id == "sample_due": pass elif action_id == "preserved": # transition our sample sample = instance.getSample() if not skip(sample, action_id, peek=True): workflow.doActionFor(sample, action_id) elif action_id == "receive": instance.setDateReceived(DateTime()) instance.reindexObject(idxs = ["review_state", "getDateReceived", ]) # receive the AR's sample sample = instance.getSample() if not skip(sample, action_id, peek=True): # unless this is a secondary AR if workflow.getInfoFor(sample, 'review_state') == 'sample_due': workflow.doActionFor(sample, 'receive') # receive all analyses in this AR. analyses = instance.getAnalyses(review_state = 'sample_due') for analysis in analyses: if not skip(analysis, action_id): workflow.doActionFor(analysis.getObject(), 'receive') elif action_id == "submit": instance.reindexObject(idxs = ["review_state", ]) # Don't cascade. Shouldn't be submitting ARs directly for now. elif action_id == "retract": instance.reindexObject(idxs = ["review_state", ]) if not "retract all analyses" in instance.REQUEST['workflow_skiplist']: # retract all analyses in this AR. # (NB: don't retract if it's verified) analyses = instance.getAnalyses(review_state = ('attachment_due', 'to_be_verified',)) for analysis in analyses: doActionFor(analysis.getObject(), 'retract') elif action_id == "verify": instance.reindexObject(idxs = ["review_state", ]) if not "verify all analyses" in instance.REQUEST['workflow_skiplist']: # verify all analyses in this AR. analyses = instance.getAnalyses(review_state = 'to_be_verified') for analysis in analyses: doActionFor(analysis.getObject(), "verify") elif action_id == "publish": instance.reindexObject(idxs = ["review_state", "getDatePublished", ]) if not "publish all analyses" in instance.REQUEST['workflow_skiplist']: # publish all analyses in this AR. (except not requested ones) analyses = instance.getAnalyses(review_state = 'verified') for analysis in analyses: doActionFor(analysis.getObject(), "publish") #--------------------- # Secondary workflows: #--------------------- elif action_id == "reinstate": instance.reindexObject(idxs = ["cancellation_state", ]) # activate all analyses in this AR. analyses = instance.getAnalyses(cancellation_state = 'cancelled') for analysis in analyses: doActionFor(analysis.getObject(), 'reinstate') elif action_id == "cancel": instance.reindexObject(idxs = ["cancellation_state", ]) # deactivate all analyses in this AR. analyses = instance.getAnalyses(cancellation_state = 'active') for analysis in analyses: doActionFor(analysis.getObject(), 'cancel') return
def ObjectRemovedEventHandler(instance, event): # May need to promote the AR's review_state # if all other analyses are at a higher state than this one was. workflow = getToolByName(instance, 'portal_workflow') ar = instance.aq_parent can_submit = True can_attach = True can_verify = True can_publish = True # We add this manually here, because during admin/ZMI removal, # it may possibly not be added by the workflow code. if not 'workflow_skiplist' in instance.REQUEST: instance.REQUEST['workflow_skiplist'] = [] for a in ar.getAnalyses(): a_state = a.review_state if a_state in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received',): can_submit = False if a_state in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received', 'attachment_due',): can_attach = False if a_state in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received', 'attachment_due', 'to_be_verified',): can_verify = False if a_state in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received', 'attachment_due', 'to_be_verified', 'verified',): can_publish = False # Note: AR adds itself to the skiplist so we have to take it off again # to allow multiple promotions (maybe by more than one deleted instance). if can_submit and workflow.getInfoFor(ar, 'review_state') == 'sample_received': try: workflow.doActionFor(ar, 'submit') except WorkflowException: pass skip(ar, 'submit', unskip=True) if can_attach and workflow.getInfoFor(ar, 'review_state') == 'attachment_due': try: workflow.doActionFor(ar, 'attach') except WorkflowException: pass skip(ar, 'attach', unskip=True) if can_verify and workflow.getInfoFor(ar, 'review_state') == 'to_be_verified': instance.REQUEST["workflow_skiplist"].append('verify all analyses') try: workflow.doActionFor(ar, 'verify') except WorkflowException: pass skip(ar, 'verify', unskip=True) if can_publish and workflow.getInfoFor(ar, 'review_state') == 'verified': instance.REQUEST["workflow_skiplist"].append('publish all analyses') try: workflow.doActionFor(ar, 'publish') except WorkflowException: pass skip(ar, 'publish', unskip=True) ar_ws_state = workflow.getInfoFor(ar, 'worksheetanalysis_review_state') if ar_ws_state == 'unassigned': if not ar.getAnalyses(worksheetanalysis_review_state = 'unassigned'): if ar.getAnalyses(worksheetanalysis_review_state = 'assigned'): try: workflow.doActionFor(ar, 'assign') except WorkflowException: pass skip(ar, 'assign', unskip=True) return
def __call__(self): form = self.request.form CheckAuthenticator(form) workflow = getToolByName(self.context, 'portal_workflow') rc = getToolByName(self.context, REFERENCE_CATALOG) bsc = getToolByName(self.context, 'bika_setup_catalog') bac = getToolByName(self.context, 'bika_analysis_catalog') action, came_from = WorkflowAction._get_form_workflow_action(self) if action == 'submit': # Submit the form. Saves the results, methods, etc. self.submit() ## assign elif action == 'assign': if not self.context.checkUserManage(): self.request.response.redirect(self.context.absolute_url()) return analysis_uids = form.get("uids", []) if analysis_uids: # We retrieve the analyses from the database sorted by AR ID # ascending, so the positions of the ARs inside the WS are # consistent with the order of the ARs catalog = get_tool(CATALOG_ANALYSIS_LISTING) brains = catalog({ 'UID': analysis_uids, 'sort_on': 'getRequestID' }) # Now, we need the analyses within a request ID to be sorted by # sortkey (sortable_title index), so it will appear in the same # order as they appear in Analyses list from AR view curr_arid = None curr_brains = [] sorted_brains = [] for brain in brains: arid = brain.getRequestID if curr_arid != arid: # Sort the brains we've collected until now, that belong to # the same Analysis Request curr_brains.sort(key=attrgetter('getPrioritySortkey')) sorted_brains.extend(curr_brains) curr_arid = arid curr_brains = [] # Now we are inside the same AR curr_brains.append(brain) continue # Sort the last set of brains we've collected curr_brains.sort(key=attrgetter('getPrioritySortkey')) sorted_brains.extend(curr_brains) # Add analyses in the worksheet for brain in sorted_brains: analysis = brain.getObject() self.context.addAnalysis(analysis) self.destination_url = self.context.absolute_url() self.request.response.redirect(self.destination_url) ## unassign elif action == 'unassign': if not self.context.checkUserManage(): self.request.response.redirect(self.context.absolute_url()) return selected_analyses = WorkflowAction._get_selected_items(self) selected_analysis_uids = selected_analyses.keys() for analysis_uid in selected_analysis_uids: try: analysis = bac(UID=analysis_uid)[0].getObject() except IndexError: # Duplicate analyses are removed when their analyses # get removed, so indexerror is expected. continue if skip(analysis, action, peek=True): continue self.context.removeAnalysis(analysis) 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) ## verify elif action == 'verify': # default bika_listing.py/WorkflowAction, but then go to view screen. self.destination_url = self.context.absolute_url() return self.workflow_action_default(action='verify', came_from=came_from) else: # default bika_listing.py/WorkflowAction for other transitions WorkflowAction.__call__(self)
def AfterTransitionEventHandler(instance, event): # creation doesn't have a 'transition' if not event.transition: return action_id = event.transition.id if skip(instance, action_id): return workflow = getToolByName(instance, 'portal_workflow') if action_id == "attach": instance.reindexObject(idxs=[ "review_state", ]) # Don't cascade. Shouldn't be attaching WSs for now (if ever). return elif action_id == "submit": # Don't cascade. Shouldn't be submitting WSs directly for now, # except edge cases where all analyses are already submitted, # but instance was held back until an analyst was assigned. instance.reindexObject(idxs=[ "review_state", ]) can_attach = True for a in instance.getAnalyses(): if workflow.getInfoFor(a, 'review_state') in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received', 'attachment_due', 'assigned',): # Note: referenceanalyses and duplicateanalyses can still have review_state = "assigned". can_attach = False break if can_attach: doActionFor(instance, 'attach') elif action_id == "retract": instance.reindexObject(idxs=[ "review_state", ]) if not "retract all analyses" in instance.REQUEST['workflow_skiplist']: # retract all analyses in this instance. # (NB: don't retract if it's verified) analyses = instance.getAnalyses() for analysis in analyses: if workflow.getInfoFor(analysis, 'review_state', '') not in ( 'attachment_due', 'to_be_verified', ): continue doActionFor(analysis, 'retract') elif action_id == "verify": instance.reindexObject(idxs=[ "review_state", ]) if not "verify all analyses" in instance.REQUEST['workflow_skiplist']: # verify all analyses in this instance. analyses = instance.getAnalyses() for analysis in analyses: if workflow.getInfoFor(analysis, 'review_state', '') != 'to_be_verified': continue doActionFor(analysis, "verify") return
def AfterTransitionEventHandler(instance, event): # DuplicateAnalysis doesn't have analysis_workflow. if instance.portal_type == "DuplicateAnalysis": return # creation doesn't have a 'transition' if not event.transition: return action_id = event.transition.id if skip(instance, action_id): return service = instance.getService() wf = getToolByName(instance, 'portal_workflow') ar = instance.aq_parent part = instance.getSamplePartition() if action_id == "attach": instance.reindexObject(idxs = ["review_state", ]) # Dependencies are already at least 'to_be_verified', ignore them. #---------------------------------------------------------------- # Check our dependents: # If it is 'attachment_due' # And it's attachments are OK # And all it's dependencies are at least 'to_be_verified' # Then: 'attach' it.: dependents = instance.getDependents() for dependent in dependents: if not skip(dependent, 'attach', peek=True): can_attach = True if wf.getInfoFor(dependent, 'review_state') != 'attachment_due': can_attach = False else: if not dependent.getAttachment(): service = dependent.getService() if service.getAttachmentOption() == 'r': can_attach = False if can_attach: dependencies = dependent.getDependencies() for dependency in dependencies: if wf.getInfoFor(dependency, 'review_state') in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received', 'attachment_due',): can_attach = False break if can_attach: doActionFor(dependent, 'attach') # If all analyses in this AR have been attached # escalate the action to the parent AR ar_state = wf.getInfoFor(ar, 'review_state') if ar_state == 'attachment_due' and not skip(ar, 'attach', peek=True): can_attach = True for a in ar.getAnalyses(): if a.review_state in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received', 'attachment_due',): can_attach = False break if can_attach: wf.doActionFor(ar, 'attach') # If assigned to a worksheet and all analyses on the worksheet have been attached, # then attach the worksheet. ws = instance.getBackReferences('WorksheetAnalysis') if ws: ws = ws[0] ws_state = wf.getInfoFor(ws, 'review_state') if ws_state == 'attachment_due' and not skip(ws, action_id, peek=True): can_attach = True for a in ws.getAnalyses(): if wf.getInfoFor(a, 'review_state') in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received', 'attachment_due', 'assigned',): # Note: referenceanalyses and duplicateanalyses can still have review_state = "assigned". can_attach = False break if can_attach: wf.doActionFor(ws, 'attach') return #------------------------------------------------------ # End of "attach" code, back to your basic nightmare... #------------------------------------------------------ elif action_id == "receive": instance.updateDueDate() instance.reindexObject() elif action_id == "submit": instance.reindexObject(idxs = ["review_state", ]) # Dependencies are submitted already, ignore them. #------------------------------------------------- # Submit our dependents # Need to check for result and status of dependencies first dependents = instance.getDependents() for dependent in dependents: if not skip(dependent, action_id, peek=True): can_submit = True if not dependent.getResult(): can_submit = False else: interim_fields = False service = dependent.getService() calculation = service.getCalculation() if calculation: interim_fields = calculation.getInterimFields() if interim_fields: can_submit = False if can_submit: dependencies = dependent.getDependencies() for dependency in dependencies: if wf.getInfoFor(dependency, 'review_state') in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received',): can_submit = False if can_submit: wf.doActionFor(dependent, 'submit') # If all analyses in this AR have been submitted # escalate the action to the parent AR if not skip(ar, action_id, peek=True): all_submitted = True for a in ar.getAnalyses(): if a.review_state in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received',): all_submitted = False break if all_submitted: wf.doActionFor(ar, 'submit') # If assigned to a worksheet and all analyses on the worksheet have been submitted, # then submit the worksheet. ws = instance.getBackReferences('WorksheetAnalysis') if ws: ws = ws[0] # if the worksheet analyst is not assigned, the worksheet can't be transitioned. if ws.getAnalyst() and not skip(ws, action_id, peek=True): all_submitted = True for a in ws.getAnalyses(): if wf.getInfoFor(a, 'review_state') in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received', 'assigned',): # Note: referenceanalyses and duplicateanalyses can still have review_state = "assigned". all_submitted = False break if all_submitted: wf.doActionFor(ws, 'submit') # If no problem with attachments, do 'attach' action for this instance. can_attach = True if not instance.getAttachment(): service = instance.getService() if service.getAttachmentOption() == 'r': can_attach = False if can_attach: dependencies = instance.getDependencies() for dependency in dependencies: if wf.getInfoFor(dependency, 'review_state') in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received', 'attachment_due',): can_attach = False if can_attach: wf.doActionFor(instance, 'attach') elif action_id == "retract": instance.reindexObject(idxs = ["review_state", ]) # retract our dependencies if not "retract all dependencies" in instance.REQUEST['workflow_skiplist']: for dependency in instance.getDependencies(): if not skip(dependency, action_id, peek=True): if wf.getInfoFor(dependency, 'review_state') in ('attachment_due', 'to_be_verified',): # (NB: don't retract if it's verified) wf.doActionFor(dependency, 'retract') # Retract our dependents for dep in instance.getDependents(): if not skip(dep, action_id, peek=True): if wf.getInfoFor(dep, 'review_state') != 'sample_received': instance.REQUEST["workflow_skiplist"].append("retract all dependencies") wf.doActionFor(dep, 'retract') instance.REQUEST["workflow_skiplist"].remove("retract all dependencies") # Escalate action to the parent AR if not skip(ar, action_id, peek=True): if wf.getInfoFor(ar, 'review_state') == 'sample_received': skip(ar, action_id) else: if not "retract all analyses" in instance.REQUEST['workflow_skiplist']: instance.REQUEST["workflow_skiplist"].append("retract all analyses") wf.doActionFor(ar, 'retract') # Escalate action to the Worksheet (if it's on one). ws = instance.getBackReferences('WorksheetAnalysis') if ws: ws = ws[0] if not skip(ws, action_id, peek=True): if wf.getInfoFor(ws, 'review_state') == 'open': skip(ws, 'retract') else: if not "retract all analyses" in instance.REQUEST['workflow_skiplist']: instance.REQUEST["workflow_skiplist"].append("retract all analyses") wf.doActionFor(ws, 'retract') elif action_id == "verify": instance.reindexObject(idxs = ["review_state", ]) # Don't verify our dependencies, they're done (or will be done by AR). #--------------------------------------------------------------------- # Check for dependents, ensure all their dependencies # have been verified, and submit/verify them for dependent in instance.getDependents(): if not skip(dependent, action_id, peek=True): if dependent.getResult(): review_state = wf.getInfoFor(dependent, 'review_state') interim_fields = False service = dependent.getService() calculation = service.getCalculation() if calculation: interim_fields = calculation.getInterimFields() dependencies = dependent.getDependencies() if interim_fields: if review_state == 'sample_received': can_submit = True for dependency in dependencies: if wf.getInfoFor(dependency, 'review_state') in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received', 'attachment_due', 'to_be_verified'): can_submit = False break if can_submit: wf.doActionFor(dependent, 'submit') else: if review_state == 'to_be_verified': can_verify = True for dependency in dependencies: if wf.getInfoFor(dependency, 'review_state') in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received', 'attachment_due', 'to_be_verified'): can_verify = False break if can_verify: wf.doActionFor(dependent, 'verify') # If all analyses in this AR are verified # escalate the action to the parent AR if not skip(ar, action_id, peek=True): all_verified = True for a in ar.getAnalyses(): if a.review_state in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received', 'attachment_due', 'to_be_verified'): all_verified = False break if all_verified: if not "verify all analyses" in instance.REQUEST['workflow_skiplist']: instance.REQUEST["workflow_skiplist"].append("verify all analyses") wf.doActionFor(ar, "verify") # If this is on a worksheet and all it's other analyses are verified, # then verify the worksheet. ws = instance.getBackReferences('WorksheetAnalysis') if ws: ws = ws[0] ws_state = wf.getInfoFor(ws, 'review_state') if ws_state == 'to_be_verified' and not skip(ws, action_id, peek=True): all_verified = True for a in ws.getAnalyses(): if wf.getInfoFor(a, 'review_state') in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received', 'attachment_due', 'to_be_verified', 'assigned'): # Note: referenceanalyses and duplicateanalyses can # still have review_state = "assigned". all_verified = False break if all_verified: if not "verify all analyses" in instance.REQUEST['workflow_skiplist']: instance.REQUEST["workflow_skiplist"].append("verify all analyses") wf.doActionFor(ws, "verify") elif action_id == "publish": endtime = DateTime() instance.setDateAnalysisPublished(endtime) starttime = instance.aq_parent.getDateReceived() starttime = starttime or instance.created() service = instance.getService() maxtime = service.getMaxTimeAllowed() # set the instance duration value to default values # in case of no calendars or max hours if maxtime: duration = (endtime - starttime) * 24 * 60 maxtime_delta = int(maxtime.get('hours', 0)) * 86400 maxtime_delta += int(maxtime.get('hours', 0)) * 3600 maxtime_delta += int(maxtime.get('minutes', 0)) * 60 earliness = duration - maxtime_delta else: earliness = 0 duration = 0 instance.setDuration(duration) instance.setEarliness(earliness) instance.reindexObject() #--------------------- # Secondary workflows: #--------------------- elif action_id == "cancel": instance.reindexObject(idxs = ["worksheetanalysis_review_state", ]) # If it is assigned to a worksheet, unassign it. if wf.getInfoFor(instance, 'worksheetanalysis_review_state') == 'assigned': ws = instance.getBackReferences("WorksheetAnalysis")[0] skip(instance, action_id, unskip=True) ws.removeAnalysis(instance) elif action_id == "assign": instance.reindexObject(idxs = ["worksheetanalysis_review_state", ]) rc = getToolByName(instance, REFERENCE_CATALOG) wsUID = instance.REQUEST['context_uid'] ws = rc.lookupObject(wsUID) # retract the worksheet to 'open' ws_state = wf.getInfoFor(ws, 'review_state') if ws_state != 'open': if not instance.REQUEST.has_key('workflow_skiplist'): instance.REQUEST['workflow_skiplist'] = ['retract all analyses', ] else: instance.REQUEST["workflow_skiplist"].append('retract all analyses') wf.doActionFor(ws, 'retract') # If all analyses in this AR have been assigned, # escalate the action to the parent AR if not skip(ar, action_id, peek=True): if not ar.getAnalyses(worksheetanalysis_review_state = 'unassigned'): wf.doActionFor(ar, 'assign') elif action_id == "unassign": instance.reindexObject(idxs = ["worksheetanalysis_review_state", ]) rc = getToolByName(instance, REFERENCE_CATALOG) wsUID = instance.REQUEST['context_uid'] ws = rc.lookupObject(wsUID) # Escalate the action to the parent AR if it is assigned # Note: AR adds itself to the skiplist so we have to take it off again # to allow multiple promotions/demotions (maybe by more than one instance). if wf.getInfoFor(ar, 'worksheetanalysis_review_state') == 'assigned': wf.doActionFor(ar, 'unassign') skip(ar, action_id, unskip=True) # If it has been duplicated on the worksheet, delete the duplicates. dups = instance.getBackReferences("DuplicateAnalysisAnalysis") for dup in dups: ws.removeAnalysis(dup) # May need to promote the Worksheet's review_state # if all other analyses are at a higher state than this one was. # (or maybe retract it if there are no analyses left) # Note: duplicates, controls and blanks have 'assigned' as a review_state. can_submit = True can_attach = True can_verify = True ws_empty = True for a in ws.getAnalyses(): ws_empty = False a_state = wf.getInfoFor(a, 'review_state') if a_state in \ ('to_be_sampled', 'to_be_preserved', 'assigned', 'sample_due', 'sample_received',): can_submit = False else: if not ws.getAnalyst(): can_submit = False if a_state in \ ('to_be_sampled', 'to_be_preserved', 'assigned', 'sample_due', 'sample_received', 'attachment_due',): can_attach = False if a_state in \ ('to_be_sampled', 'to_be_preserved', 'assigned', 'sample_due', 'sample_received', 'attachment_due', 'to_be_verified',): can_verify = False if not ws_empty: # Note: WS adds itself to the skiplist so we have to take it off again # to allow multiple promotions (maybe by more than one instance). if can_submit and wf.getInfoFor(ws, 'review_state') == 'open': wf.doActionFor(ws, 'submit') skip(ws, action_id, unskip=True) if can_attach and wf.getInfoFor(ws, 'review_state') == 'attachment_due': wf.doActionFor(ws, 'attach') skip(ws, action_id, unskip=True) if can_verify and wf.getInfoFor(ws, 'review_state') == 'to_be_verified': instance.REQUEST["workflow_skiplist"].append('verify all analyses') wf.doActionFor(ws, 'verify') skip(ws, action_id, unskip=True) else: if wf.getInfoFor(ws, 'review_state') != 'open': wf.doActionFor(ws, 'retract') skip(ws, 'retract', unskip=True) return
def ObjectRemovedEventHandler(instance, event): # This handler fires for DuplicateAnalysis because # DuplicateAnalysis also provides IAnalysis. # DuplicateAnalysis doesn't have analysis_workflow. if instance.portal_type == "DuplicateAnalysis": return # May need to promote the AR's review_state # if all other analyses are at a higher state than this one was. wf = getToolByName(instance, 'portal_workflow') ar = instance.aq_parent ar_UID = ar.UID() can_submit = True can_attach = True can_verify = True can_publish = True for a in ar.getAnalyses(): a_state = a.review_state if a_state in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received',): can_submit = False if a_state in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received', 'attachment_due',): can_attach = False if a_state in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received', 'attachment_due', 'to_be_verified',): can_verify = False if a_state in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received', 'attachment_due', 'to_be_verified', 'verified',): can_publish = False # Note: AR adds itself to the skiplist so we have to take it off again # to allow multiple promotions (maybe by more than one deleted instance). if can_submit and wf.getInfoFor(ar, 'review_state') == 'sample_received': wf.doActionFor(ar, 'submit') skip(ar, 'submit', unskip=True) if can_attach and wf.getInfoFor(ar, 'review_state') == 'attachment_due': wf.doActionFor(ar, 'attach') skip(ar, 'attach', unskip=True) if can_verify and wf.getInfoFor(ar, 'review_state') == 'to_be_verified': instance.REQUEST["workflow_skiplist"].append('verify all analyses') wf.doActionFor(ar, 'verify') skip(ar, 'verify', unskip=True) if can_publish and wf.getInfoFor(ar, 'review_state') == 'verified': instance.REQUEST["workflow_skiplist"].append('publish all analyses') wf.doActionFor(ar, 'publish') skip(ar, 'publish', unskip=True) ar_ws_state = wf.getInfoFor(ar, 'worksheetanalysis_review_state') if ar_ws_state == 'unassigned': if not ar.getAnalyses(worksheetanalysis_review_state = 'unassigned'): if ar.getAnalyses(worksheetanalysis_review_state = 'assigned'): wf.doActionFor(ar, 'assign') skip(ar, 'assign', unskip=True) return
def AfterTransitionEventHandler(instance, event): # creation doesn't have a 'transition' if not event.transition: return action_id = event.transition.id if skip(instance, action_id): return workflow = getToolByName(instance, 'portal_workflow') membership_tool = getToolByName(instance, 'portal_membership') member = membership_tool.getAuthenticatedMember() sample = instance.aq_parent sample_state = workflow.getInfoFor(sample, 'review_state') if action_id == "sample": # Transition our analyses analyses = instance.getBackReferences('AnalysisSamplePartition') for analysis in analyses: doActionFor(analysis, action_id) # if all our siblings are now up to date, promote sample and ARs. parts = sample.objectValues("SamplePartition") if parts: lower_states = [ 'to_be_sampled', ] escalate = True for part in parts: pstate = workflow.getInfoFor(part, 'review_state') if pstate in lower_states: escalate = False if escalate: doActionFor(sample, action_id) for ar in sample.getAnalysisRequests(): doActionFor(ar, action_id) elif action_id == "to_be_preserved": # Transition our analyses analyses = instance.getBackReferences('AnalysisSamplePartition') for analysis in analyses: doActionFor(analysis, action_id) # if all our siblings are now up to date, promote sample and ARs. parts = sample.objectValues("SamplePartition") if parts: lower_states = [ 'to_be_sampled', 'to_be_preserved', ] escalate = True for part in parts: if workflow.getInfoFor(part, 'review_state') in lower_states: escalate = False if escalate: doActionFor(sample, action_id) for ar in sample.getAnalysisRequests(): doActionFor(ar, action_id) elif action_id == "sample_due": # Transition our analyses analyses = instance.getBackReferences('AnalysisSamplePartition') for analysis in analyses: doActionFor(analysis, action_id) # if all our siblings are now up to date, promote sample and ARs. parts = sample.objectValues("SamplePartition") if parts: lower_states = [ 'to_be_preserved', ] escalate = True for part in parts: pstate = workflow.getInfoFor(part, 'review_state') if pstate in lower_states: escalate = False if escalate: doActionFor(sample, action_id) for ar in sample.getAnalysisRequests(): doActionFor(ar, action_id) elif action_id == "preserve": # Transition our analyses analyses = instance.getBackReferences('AnalysisSamplePartition') if analyses: for analysis in analyses: doActionFor(analysis, action_id) # if all our siblings are now up to date, promote sample and ARs. parts = sample.objectValues("SamplePartition") if parts: lower_states = [ 'to_be_sampled', 'to_be_preserved', ] escalate = True for part in parts: if workflow.getInfoFor(part, 'review_state') in lower_states: escalate = False if escalate: doActionFor(sample, action_id) for ar in sample.getAnalysisRequests(): doActionFor(ar, action_id) elif action_id == "receive": if sample.getSamplingDate() > DateTime(): raise WorkflowException instance.setDateReceived(DateTime()) instance.reindexObject(idxs=[ "getDateReceived", ]) # Transition our analyses analyses = instance.getBackReferences('AnalysisSamplePartition') for analysis in analyses: doActionFor(analysis, action_id) # if all sibling partitions are received, promote sample if not skip(sample, action_id, peek=True): due = [ sp for sp in sample.objectValues("SamplePartition") if workflow.getInfoFor(sp, 'review_state') == 'sample_due' ] if sample_state == 'sample_due' and not due: doActionFor(sample, 'receive') elif action_id == "expire": instance.setDateExpired(DateTime()) instance.reindexObject(idxs=[ "review_state", "getDateExpired", ]) #--------------------- # Secondary workflows: #--------------------- elif action_id == "reinstate": instance.reindexObject(idxs=[ "cancellation_state", ]) sample_c_state = workflow.getInfoFor(sample, 'cancellation_state') # if all sibling partitions are active, activate sample if not skip(sample, action_id, peek=True): cancelled = [ sp for sp in sample.objectValues("SamplePartition") if workflow.getInfoFor(sp, 'cancellation_state') == 'cancelled' ] if sample_c_state == 'cancelled' and not cancelled: workflow.doActionFor(sample, 'reinstate') elif action_id == "cancel": instance.reindexObject(idxs=[ "cancellation_state", ]) sample_c_state = workflow.getInfoFor(sample, 'cancellation_state') # if all sibling partitions are cancelled, cancel sample if not skip(sample, action_id, peek=True): active = [ sp for sp in sample.objectValues("SamplePartition") if workflow.getInfoFor(sp, 'cancellation_state') == 'active' ] if sample_c_state == 'active' and not active: workflow.doActionFor(sample, 'cancel') return
def __call__(self): form = self.request.form plone.protect.CheckAuthenticator(form) workflow = getToolByName(self.context, 'portal_workflow') rc = getToolByName(self.context, REFERENCE_CATALOG) bsc = getToolByName(self.context, 'bika_setup_catalog') bac = getToolByName(self.context, 'bika_analysis_catalog') action, came_from = WorkflowAction._get_form_workflow_action(self) if action == 'submit': # Submit the form. Saves the results, methods, etc. self.submit() ## assign elif action == 'assign': if not self.context.checkUserManage(): self.request.response.redirect(self.context.absolute_url()) return selected_analyses = WorkflowAction._get_selected_items(self) selected_analysis_uids = selected_analyses.keys() if selected_analyses: for uid in selected_analysis_uids: analysis = rc.lookupObject(uid) # Double-check the state first if (workflow.getInfoFor(analysis, 'worksheetanalysis_review_state') == 'unassigned' and workflow.getInfoFor( analysis, 'review_state') == 'sample_received' and workflow.getInfoFor( analysis, 'cancellation_state') == 'active'): self.context.addAnalysis(analysis) self.destination_url = self.context.absolute_url() self.request.response.redirect(self.destination_url) ## unassign elif action == 'unassign': if not self.context.checkUserManage(): self.request.response.redirect(self.context.absolute_url()) return selected_analyses = WorkflowAction._get_selected_items(self) selected_analysis_uids = selected_analyses.keys() for analysis_uid in selected_analysis_uids: try: analysis = bac(UID=analysis_uid)[0].getObject() except IndexError: # Duplicate analyses are removed when their analyses # get removed, so indexerror is expected. continue if skip(analysis, action, peek=True): continue self.context.removeAnalysis(analysis) 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) ## verify elif action == 'verify': # default bika_listing.py/WorkflowAction, but then go to view screen. self.destination_url = self.context.absolute_url() return self.workflow_action_default(action='verify', came_from=came_from) else: # default bika_listing.py/WorkflowAction for other transitions WorkflowAction.__call__(self)
def AfterTransitionEventHandler(instance, event): # creation doesn't have a 'transition' if not event.transition: return action_id = event.transition.id if skip(instance, action_id): return workflow = getToolByName(instance, 'portal_workflow') membership_tool = getToolByName(instance, 'portal_membership') member = membership_tool.getAuthenticatedMember() parts = instance.objectValues('SamplePartition') if action_id == "sample": # This action can happen in the Sample UI. So we transition all # instance partitions that are still 'to_be_sampled' tbs = [ sp for sp in parts if workflow.getInfoFor(sp, 'review_state') == 'to_be_sampled' ] for sp in tbs: doActionFor(sp, action_id) # All associated AnalysisRequests are also transitioned for ar in instance.getAnalysisRequests(): doActionFor(ar, action_id) ar.reindexObject() elif action_id == "to_be_preserved": # Transition our children tbs = [ sp for sp in parts if workflow.getInfoFor(sp, 'review_state') == 'to_be_preserved' ] for sp in tbs: doActionFor(sp, action_id) # All associated AnalysisRequests are also transitioned for ar in instance.getAnalysisRequests(): doActionFor(ar, action_id) ar.reindexObject() elif action_id == "sample_due": # All associated AnalysisRequests are also transitioned for ar in instance.getAnalysisRequests(): doActionFor(ar, action_id) ar.reindexObject() elif action_id == "preserve": # This action can happen in the Sample UI. So we transition all # instance partitions that are still 'to_be_preserved' tbs = [ sp for sp in parts if workflow.getInfoFor(sp, 'review_state') == 'to_be_preserved' ] for sp in tbs: doActionFor(sp, action_id) # All associated AnalysisRequests are also transitioned for ar in instance.getAnalysisRequests(): doActionFor(ar, action_id) ar.reindexObject() elif action_id == "receive": instance.setDateReceived(DateTime()) instance.reindexObject(idxs=["review_state", "getDateReceived"]) # Receive all instance partitions that are still 'sample_due' sample_due = [ sp for sp in parts if workflow.getInfoFor(sp, 'review_state') == 'sample_due' ] for sp in sample_due: workflow.doActionFor(sp, 'receive') # when a instance is received, all associated # AnalysisRequests are also transitioned for ar in instance.getAnalysisRequests(): doActionFor(ar, "receive") elif action_id == "expire": instance.setDateExpired(DateTime()) instance.reindexObject(idxs=[ "review_state", "getDateExpired", ]) #--------------------- # Secondary workflows: #--------------------- elif action_id == "reinstate": instance.reindexObject(idxs=[ "cancellation_state", ]) # Re-instate all instance partitions for sp in [ sp for sp in parts if workflow.getInfoFor(sp, 'cancellation_state') == 'cancelled' ]: workflow.doActionFor(sp, 'reinstate') # reinstate all ARs for this instance. ars = instance.getAnalysisRequests() for ar in ars: if not skip(ar, action_id, peek=True): ar_state = workflow.getInfoFor(ar, 'cancellation_state') if ar_state == 'cancelled': workflow.doActionFor(ar, 'reinstate') elif action_id == "cancel": instance.reindexObject(idxs=[ "cancellation_state", ]) # Cancel all partitions for sp in [ sp for sp in parts if workflow.getInfoFor(sp, 'cancellation_state') == 'active' ]: workflow.doActionFor(sp, 'cancel') # cancel all ARs for this instance. ars = instance.getAnalysisRequests() for ar in ars: if not skip(ar, action_id, peek=True): ar_state = workflow.getInfoFor(ar, 'cancellation_state') if ar_state == 'active': workflow.doActionFor(ar, 'cancel') return
def AfterTransitionEventHandler(instance, event): # Note: Don't have dependencies or dependents, not on an AR #---------------------------------------------------------- # creation doesn't have a 'transition' if not event.transition: return action_id = event.transition.id if skip(instance, action_id): return wf = getToolByName(instance, 'portal_workflow') if action_id == "attach": instance.reindexObject(idxs = ["review_state", ]) # If all analyses on the worksheet have been attached, # then attach the worksheet. ws = instance.getBackReferences('WorksheetAnalysis') ws = ws[0] ws_state = wf.getInfoFor(ws, 'review_state') if ws_state == 'attachment_due' and not skip(ws, action_id, peek=True): can_attach = True for a in ws.getAnalyses(): if wf.getInfoFor(a, 'review_state') in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received', 'attachment_due', 'assigned',): can_attach = False break if can_attach: wf.doActionFor(ws, 'attach') return #------------------------------------------------------ # End of "attach" code, back to your basic nightmare... #------------------------------------------------------ elif action_id == "submit": instance.reindexObject(idxs = ["review_state", ]) # If all analyses on the worksheet have been submitted, # then submit the worksheet. ws = instance.getBackReferences('WorksheetAnalysis') ws = ws[0] # if the worksheet analyst is not assigned, the worksheet can't be transitioned. if ws.getAnalyst() and not skip(ws, action_id, peek=True): all_submitted = True for a in ws.getAnalyses(): if wf.getInfoFor(a, 'review_state') in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received', 'assigned',): all_submitted = False break if all_submitted: wf.doActionFor(ws, 'submit') # If no problem with attachments, do 'attach' action for this instance. can_attach = True if not instance.getAttachment(): service = instance.getService() if service.getAttachmentOption() == 'r': can_attach = False if can_attach: wf.doActionFor(instance, 'attach') elif action_id == "retract": instance.reindexObject(idxs = ["review_state", ]) # Escalate action to the Worksheet. ws = instance.getBackReferences('WorksheetAnalysis') ws = ws[0] if skip(ws, action_id, peek=True): if wf.getInfoFor(ws, 'review_state') == 'open': skip(ws, action_id) else: if not "retract all analyses" in instance.REQUEST['workflow_skiplist']: instance.REQUEST["workflow_skiplist"].append("retract all analyses") wf.doActionFor(ws, 'retract') elif action_id == "verify": instance.reindexObject(idxs = ["review_state", ]) # If all other analyses on the worksheet are verified, # then verify the worksheet. ws = instance.getBackReferences('WorksheetAnalysis') ws = ws[0] ws_state = wf.getInfoFor(ws, 'review_state') if ws_state == 'to_be_verified' and not skip(ws, action_id, peek=True): all_verified = True for a in ws.getAnalyses(): if wf.getInfoFor(a, 'review_state') in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received', 'attachment_due', 'to_be_verified', 'assigned'): all_verified = False break if all_verified: if not "verify all analyses" in instance.REQUEST['workflow_skiplist']: instance.REQUEST["workflow_skiplist"].append("verify all analyses") wf.doActionFor(ws, "verify") elif action_id == "assign": instance.reindexObject(idxs = ["review_state", ]) rc = getToolByName(instance, REFERENCE_CATALOG) wsUID = instance.REQUEST['context_uid'] ws = rc.lookupObject(wsUID) # retract the worksheet to 'open' ws_state = wf.getInfoFor(ws, 'review_state') if ws_state != 'open': if not instance.REQUEST.has_key('workflow_skiplist'): instance.REQUEST['workflow_skiplist'] = ['retract all analyses', ] else: instance.REQUEST["workflow_skiplist"].append('retract all analyses') wf.doActionFor(ws, 'retract') elif action_id == "unassign": instance.reindexObject(idxs = ["review_state", ]) rc = getToolByName(instance, REFERENCE_CATALOG) wsUID = instance.REQUEST['context_uid'] ws = rc.lookupObject(wsUID) # May need to promote the Worksheet's review_state # if all other analyses are at a higher state than this one was. # (or maybe retract it if there are no analyses left) # Note: duplicates, controls and blanks have 'assigned' as a review_state. can_submit = True can_attach = True can_verify = True ws_empty = True for a in ws.getAnalyses(): ws_empty = False a_state = wf.getInfoFor(a, 'review_state') if a_state in \ ('assigned', 'sample_due', 'sample_received',): can_submit = False else: if not ws.getAnalyst(): can_submit = False if a_state in \ ('assigned', 'sample_due', 'sample_received', 'attachment_due',): can_attach = False if a_state in \ ('assigned', 'sample_due', 'sample_received', 'attachment_due', 'to_be_verified',): can_verify = False if not ws_empty: # Note: WS adds itself to the skiplist so we have to take it off again # to allow multiple promotions (maybe by more than one instance). if can_submit and wf.getInfoFor(ws, 'review_state') == 'open': wf.doActionFor(ws, 'submit') skip(ws, 'submit', unskip=True) if can_attach and wf.getInfoFor(ws, 'review_state') == 'attachment_due': wf.doActionFor(ws, 'attach') skip(ws, 'attach', unskip=True) if can_verify and wf.getInfoFor(ws, 'review_state') == 'to_be_verified': instance.REQUEST["workflow_skiplist"].append('verify all analyses') wf.doActionFor(ws, 'verify') skip(ws, 'verify', unskip=True) else: if wf.getInfoFor(ws, 'review_state') != 'open': wf.doActionFor(ws, 'retract') skip(ws, 'retract', unskip=True) return
def ObjectRemovedEventHandler(instance, event): # This handler fires for DuplicateAnalysis because # DuplicateAnalysis also provides IAnalysis. # DuplicateAnalysis doesn't have analysis_workflow. if instance.portal_type == "DuplicateAnalysis": return # May need to promote the AR's review_state # if all other analyses are at a higher state than this one was. workflow = getToolByName(instance, 'portal_workflow') ar = instance.aq_parent can_submit = True can_attach = True can_verify = True can_publish = True # We add this manually here, because during admin/ZMI removal, # it may possibly not be added by the workflow code. if not 'workflow_skiplist' in instance.REQUEST: instance.REQUEST['workflow_skiplist'] = [] for a in ar.getAnalyses(): a_state = a.review_state if a_state in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received',): can_submit = False if a_state in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received', 'attachment_due',): can_attach = False if a_state in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received', 'attachment_due', 'to_be_verified',): can_verify = False if a_state in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received', 'attachment_due', 'to_be_verified', 'verified',): can_publish = False # Note: AR adds itself to the skiplist so we have to take it off again # to allow multiple promotions (maybe by more than one deleted instance). if can_submit and workflow.getInfoFor(ar, 'review_state') == 'sample_received': try: workflow.doActionFor(ar, 'submit') except WorkflowException: pass skip(ar, 'submit', unskip=True) if can_attach and workflow.getInfoFor(ar, 'review_state') == 'attachment_due': try: workflow.doActionFor(ar, 'attach') except WorkflowException: pass skip(ar, 'attach', unskip=True) if can_verify and workflow.getInfoFor(ar, 'review_state') == 'to_be_verified': instance.REQUEST["workflow_skiplist"].append('verify all analyses') try: workflow.doActionFor(ar, 'verify') except WorkflowException: pass skip(ar, 'verify', unskip=True) if can_publish and workflow.getInfoFor(ar, 'review_state') == 'verified': instance.REQUEST["workflow_skiplist"].append('publish all analyses') try: workflow.doActionFor(ar, 'publish') except WorkflowException: pass skip(ar, 'publish', unskip=True) ar_ws_state = workflow.getInfoFor(ar, 'worksheetanalysis_review_state') if ar_ws_state == 'unassigned': if not ar.getAnalyses(worksheetanalysis_review_state='unassigned'): if ar.getAnalyses(worksheetanalysis_review_state='assigned'): try: workflow.doActionFor(ar, 'assign') except WorkflowException: pass skip(ar, 'assign', unskip=True) return
def AfterTransitionEventHandler(instance, event): # creation doesn't have a 'transition' if not event.transition: return action_id = event.transition.id if skip(instance, action_id): return workflow = getToolByName(instance, 'portal_workflow') if action_id == "attach": instance.reindexObject(idxs=[ "review_state", ]) # Don't cascade. Shouldn't be attaching ARs for now (if ever). return elif action_id == "sample": # transition our sample sample = instance.getSample() if not skip(sample, action_id, peek=True): workflow.doActionFor(sample, action_id) elif action_id == "to_be_preserved": pass elif action_id == "sample_due": pass elif action_id == "preserve": # transition our sample sample = instance.getSample() if not skip(sample, action_id, peek=True): workflow.doActionFor(sample, action_id) elif action_id == "receive": instance.setDateReceived(DateTime()) instance.reindexObject(idxs=[ "review_state", "getDateReceived", ]) # receive the AR's sample sample = instance.getSample() if not skip(sample, action_id, peek=True): # unless this is a secondary AR if workflow.getInfoFor(sample, 'review_state') == 'sample_due': workflow.doActionFor(sample, 'receive') # receive all analyses in this AR. analyses = instance.getAnalyses(review_state='sample_due') for analysis in analyses: if not skip(analysis, action_id): workflow.doActionFor(analysis.getObject(), 'receive') # Possibly receive the AR's batch batch = instance.getBatch() if batch: try: workflow.doActionFor(batch, action_id) except: pass elif action_id == "submit": instance.reindexObject(idxs=[ "review_state", ]) # Possibly submit the AR's batch batch = instance.getBatch() if batch: try: workflow.doActionFor(batch, action_id) except: pass elif action_id == "retract": instance.reindexObject(idxs=[ "review_state", ]) if not "retract all analyses" in instance.REQUEST['workflow_skiplist']: # retract all analyses in this AR. # (NB: don't retract if it's verified) analyses = instance.getAnalyses(review_state=( 'attachment_due', 'to_be_verified', )) for analysis in analyses: doActionFor(analysis.getObject(), 'retract') elif action_id == "verify": instance.reindexObject(idxs=[ "review_state", ]) if not "verify all analyses" in instance.REQUEST['workflow_skiplist']: # verify all analyses in this AR. analyses = instance.getAnalyses(review_state='to_be_verified') for analysis in analyses: doActionFor(analysis.getObject(), "verify") # Possibly verify the AR's batch batch = instance.getBatch() if batch: try: workflow.doActionFor(batch, action_id) except: pass elif action_id == "publish": instance.reindexObject(idxs=[ "review_state", "getDatePublished", ]) if not "publish all analyses" in instance.REQUEST['workflow_skiplist']: # publish all analyses in this AR. (except not requested ones) analyses = instance.getAnalyses(review_state='verified') for analysis in analyses: doActionFor(analysis.getObject(), "publish") #--------------------- # Secondary workflows: #--------------------- elif action_id == "reinstate": instance.reindexObject(idxs=[ "cancellation_state", ]) # activate all analyses in this AR. analyses = instance.getAnalyses(cancellation_state='cancelled') for analysis in analyses: doActionFor(analysis.getObject(), 'reinstate') elif action_id == "cancel": instance.reindexObject(idxs=[ "cancellation_state", ]) # deactivate all analyses in this AR. analyses = instance.getAnalyses(cancellation_state='active') for analysis in analyses: doActionFor(analysis.getObject(), 'cancel') return
def AfterTransitionEventHandler(instance, event): # creation doesn't have a 'transition' if not event.transition: return action_id = event.transition.id if skip(instance, action_id): return workflow = getToolByName(instance, "portal_workflow") membership_tool = getToolByName(instance, "portal_membership") member = membership_tool.getAuthenticatedMember() parts = instance.objectValues("SamplePartition") if action_id == "sample": # This action can happen in the Sample UI. So we transition all # instance partitions that are still 'to_be_sampled' tbs = [sp for sp in parts if workflow.getInfoFor(sp, "review_state") == "to_be_sampled"] for sp in tbs: doActionFor(sp, action_id) # All associated AnalysisRequests are also transitioned for ar in instance.getAnalysisRequests(): doActionFor(ar, action_id) ar.reindexObject() elif action_id == "to_be_preserved": # Transition our children tbs = [sp for sp in parts if workflow.getInfoFor(sp, "review_state") == "to_be_preserved"] for sp in tbs: doActionFor(sp, action_id) # All associated AnalysisRequests are also transitioned for ar in instance.getAnalysisRequests(): doActionFor(ar, action_id) ar.reindexObject() elif action_id == "sample_due": # All associated AnalysisRequests are also transitioned for ar in instance.getAnalysisRequests(): doActionFor(ar, action_id) ar.reindexObject() elif action_id == "preserve": # This action can happen in the Sample UI. So we transition all # instance partitions that are still 'to_be_preserved' tbs = [sp for sp in parts if workflow.getInfoFor(sp, "review_state") == "to_be_preserved"] for sp in tbs: doActionFor(sp, action_id) # All associated AnalysisRequests are also transitioned for ar in instance.getAnalysisRequests(): doActionFor(ar, action_id) ar.reindexObject() elif action_id == "receive": instance.setDateReceived(DateTime()) instance.reindexObject(idxs=["review_state", "getDateReceived"]) # Receive all instance partitions that are still 'sample_due' sample_due = [sp for sp in parts if workflow.getInfoFor(sp, "review_state") == "sample_due"] for sp in sample_due: workflow.doActionFor(sp, "receive") # when a instance is received, all associated # AnalysisRequests are also transitioned for ar in instance.getAnalysisRequests(): doActionFor(ar, "receive") elif action_id == "expire": instance.setDateExpired(DateTime()) instance.reindexObject(idxs=["review_state", "getDateExpired"]) # --------------------- # Secondary workflows: # --------------------- elif action_id == "reinstate": instance.reindexObject(idxs=["cancellation_state"]) # Re-instate all instance partitions for sp in [sp for sp in parts if workflow.getInfoFor(sp, "cancellation_state") == "cancelled"]: workflow.doActionFor(sp, "reinstate") # reinstate all ARs for this instance. ars = instance.getAnalysisRequests() for ar in ars: if not skip(ar, action_id, peek=True): ar_state = workflow.getInfoFor(ar, "cancellation_state") if ar_state == "cancelled": workflow.doActionFor(ar, "reinstate") elif action_id == "cancel": instance.reindexObject(idxs=["cancellation_state"]) # Cancel all partitions for sp in [sp for sp in parts if workflow.getInfoFor(sp, "cancellation_state") == "active"]: workflow.doActionFor(sp, "cancel") # cancel all ARs for this instance. ars = instance.getAnalysisRequests() for ar in ars: if not skip(ar, action_id, peek=True): ar_state = workflow.getInfoFor(ar, "cancellation_state") if ar_state == "active": workflow.doActionFor(ar, "cancel") return
def __call__(self): form = self.request.form plone.protect.CheckAuthenticator(form) workflow = getToolByName(self.context, 'portal_workflow') rc = getToolByName(self.context, REFERENCE_CATALOG) bsc = getToolByName(self.context, 'bika_setup_catalog') bac = getToolByName(self.context, 'bika_analysis_catalog') action, came_from = WorkflowAction._get_form_workflow_action(self) # XXX combine data from multiple bika listing tables. item_data = {} if 'item_data' in form: if type(form['item_data']) == list: for i_d in form['item_data']: for i, d in json.loads(i_d).items(): item_data[i] = d else: item_data = json.loads(form['item_data']) if action == 'submit' and self.request.form.has_key("Result"): selected_analyses = WorkflowAction._get_selected_items(self) results = {} hasInterims = {} # first save results for entire form for uid, result in self.request.form['Result'][0].items(): if uid in selected_analyses: analysis = selected_analyses[uid] else: analysis = rc.lookupObject(uid) if not analysis: # ignore result if analysis object no longer exists continue if not(getSecurityManager().checkPermission(EditResults, analysis)): # or changes no longer allowed continue if not isActive(analysis): # or it's cancelled continue results[uid] = result service = analysis.getService() interimFields = item_data[uid] if len(interimFields) > 0: hasInterims[uid] = True else: hasInterims[uid] = False unit = service.getUnit() analysis.edit( Result = result, InterimFields = interimFields, Retested = form.has_key('retested') and \ form['retested'].has_key(uid), Unit = unit and unit or '') # discover which items may be submitted submissable = [] for uid, analysis in selected_analyses.items(): if uid not in results: continue can_submit = True if hasattr(analysis, 'getDependencies'): dependencies = analysis.getDependencies() for dependency in dependencies: dep_state = workflow.getInfoFor(dependency, 'review_state') if hasInterims[uid]: if dep_state in ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received', 'attachment_due', 'to_be_verified',): can_submit = False break else: if dep_state in ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received',): can_submit = False break for dependency in dependencies: if workflow.getInfoFor(dependency, 'review_state') in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received'): can_submit = False if can_submit: submissable.append(analysis) # and then submit them. for analysis in submissable: doActionFor(analysis, 'submit') message = PMF("Changes saved.") self.context.plone_utils.addPortalMessage(message, 'info') self.destination_url = self.request.get_header("referer", self.context.absolute_url()) self.request.response.redirect(self.destination_url) ## assign elif action == 'assign': if not(getSecurityManager().checkPermission(EditWorksheet, self.context)): self.request.response.redirect(self.context.absolute_url()) return selected_analyses = WorkflowAction._get_selected_items(self) selected_analysis_uids = selected_analyses.keys() if selected_analyses: for uid in selected_analysis_uids: analysis = rc.lookupObject(uid) # Double-check the state first if (workflow.getInfoFor(analysis, 'worksheetanalysis_review_state') == 'unassigned' and workflow.getInfoFor(analysis, 'review_state') == 'sample_received' and workflow.getInfoFor(analysis, 'cancellation_state') == 'active'): self.context.addAnalysis(analysis) self.destination_url = self.context.absolute_url() self.request.response.redirect(self.destination_url) ## unassign elif action == 'unassign': if not(getSecurityManager().checkPermission(EditWorksheet, self.context)): self.request.response.redirect(self.context.absolute_url()) return selected_analyses = WorkflowAction._get_selected_items(self) selected_analysis_uids = selected_analyses.keys() for analysis_uid in selected_analysis_uids: try: analysis = bac(UID=analysis_uid)[0].getObject() except IndexError: # Duplicate analyses are removed when their analyses # get removed, so indexerror is expected. continue if skip(analysis, action, peek=True): continue self.context.removeAnalysis(analysis) self.destination_url = self.context.absolute_url() self.request.response.redirect(self.destination_url) ## verify elif action == 'verify': # default bika_listing.py/WorkflowAction, but then go to view screen. self.destination_url = self.context.absolute_url() WorkflowAction.__call__(self) else: # default bika_listing.py/WorkflowAction for other transitions WorkflowAction.__call__(self)