def __call__(self): form = self.request.form plone.protect.CheckAuthenticator(form) action, came_from = WorkflowAction._get_form_workflow_action(self) if action == 'submit': objects = WorkflowAction._get_selected_items(self) if not objects: message = self.context.translate( _b("No analyses have been selected")) self.context.plone_utils.addPortalMessage(message, 'info') self.destination_url = self.context.absolute_url() + \ "/batchbook" self.request.response.redirect(self.destination_url) return for ar_uid, ar in objects.items(): for analysis in ar.getAnalyses(full_objects=True): kw = analysis.getKeyword() values = form.get(kw) analysis.setResult(values[0][ar_uid]) if values[0][ar_uid]: doActionFor(analysis, 'submit') message = self.context.translate(_p("Changes saved.")) self.context.plone_utils.addPortalMessage(message, 'info') self.request.response.redirect(self.request.get('URL')) return else: WorkflowAction.__call__(self)
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 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 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 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 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 retractInvalidAnalyses(self): """ Retract the analyses with validation pending status for which the instrument used failed a QC Test. """ toretract = {} instruments = {} refs = [] rc = getToolByName(self.context, REFERENCE_CATALOG) selected = WorkflowAction._get_selected_items(self) for uid in selected.iterkeys(): # We need to do this instead of using the dict values # directly because all these analyses have been saved before # and don't know if they already had an instrument assigned an = rc.lookupObject(uid) if an.portal_type == 'ReferenceAnalysis': refs.append(an) instrument = an.getInstrument() if instrument and instrument.UID() not in instruments: instruments[instrument.UID()] = instrument for instr in instruments.itervalues(): analyses = instr.getAnalysesToRetract() for a in analyses: if a.UID() not in toretract: toretract[a.UID] = a retracted = [] for analysis in toretract.itervalues(): try: # add a remark to this analysis failedtxt = ulocalized_time(DateTime(), long_format=0) failedtxt = '%s: %s' % (failedtxt, _("Instrument failed reference test")) analysis.setRemarks(failedtxt) # retract the analysis doActionFor(analysis, 'retract') retracted.append(analysis) except: # Already retracted as a dependant from a previous one? pass if len(retracted) > 0: # Create the Retracted Analyses List rep = AnalysesRetractedListReport(self.context, self.request, self.portal_url, 'Retracted analyses', retracted) # Attach the pdf to the ReferenceAnalysis (accessible # from Instrument's Internal Calibration Tests list pdf = rep.toPdf() for ref in refs: ref.setRetractedAnalysesPdfReport(pdf) # Send the email try: rep.sendEmail() except: pass # TODO: mostra una finestra amb els resultats publicats d'AS # que han utilitzat l'instrument des de la seva última # calibració vàlida, amb els emails, telèfons dels # contactes associats per a una intervenció manual pass
def submit(self): """ Saves the form """ form = self.request.form remarks = form.get('Remarks', [{}])[0] results = form.get('Result',[{}])[0] retested = form.get('retested', {}) methods = form.get('Method', [{}])[0] instruments = form.get('Instrument', [{}])[0] analysts = self.request.form.get('Analyst', [{}])[0] uncertainties = self.request.form.get('Uncertainty', [{}])[0] dlimits = self.request.form.get('DetectionLimit', [{}])[0] selected = WorkflowAction._get_selected_items(self) workflow = getToolByName(self.context, 'portal_workflow') rc = getToolByName(self.context, REFERENCE_CATALOG) sm = getSecurityManager() hasInterims = {} # XXX combine data from multiple bika listing tables. item_data = {} if 'item_data' in form: if type(form['item_data']) == list: for i_d in form['item_data']: for i, d in json.loads(i_d).items(): item_data[i] = d else: item_data = json.loads(form['item_data']) # Iterate for each selected analysis and save its data as needed for uid, analysis in selected.items(): allow_edit = sm.checkPermission(EditResults, analysis) analysis_active = isActive(analysis) # Need to save remarks? if uid in remarks and allow_edit and analysis_active: analysis.setRemarks(remarks[uid]) # Retested? if uid in retested and allow_edit and analysis_active: analysis.setRetested(retested[uid]) # Need to save the instrument? if uid in instruments and analysis_active: # TODO: Add SetAnalysisInstrument permission # allow_setinstrument = sm.checkPermission(SetAnalysisInstrument) allow_setinstrument = True # ---8<----- if allow_setinstrument == True: # The current analysis allows the instrument regards # to its analysis service and method? if (instruments[uid]==''): previnstr = analysis.getInstrument() if previnstr: previnstr.removeAnalysis(analysis) analysis.setInstrument(None); elif analysis.isInstrumentAllowed(instruments[uid]): previnstr = analysis.getInstrument() if previnstr: previnstr.removeAnalysis(analysis) analysis.setInstrument(instruments[uid]) instrument = analysis.getInstrument() instrument.addAnalysis(analysis) if analysis.meta_type == 'ReferenceAnalysis': instrument.setDisposeUntilNextCalibrationTest(False) # Need to save the method? if uid in methods and analysis_active: # TODO: Add SetAnalysisMethod permission # allow_setmethod = sm.checkPermission(SetAnalysisMethod) allow_setmethod = True # ---8<----- if allow_setmethod == True and analysis.isMethodAllowed(methods[uid]): analysis.setMethod(methods[uid]) # Need to save the analyst? if uid in analysts and analysis_active: analysis.setAnalyst(analysts[uid]); # Need to save the uncertainty? if uid in uncertainties and analysis_active: analysis.setUncertainty(uncertainties[uid]) # Need to save the detection limit? if analysis_active and uid in dlimits and dlimits[uid]: analysis.setDetectionLimitOperand(dlimits[uid]) # Need to save results? if uid in results and results[uid] and allow_edit \ and analysis_active: interims = item_data.get(uid, []) analysis.setInterimFields(interims) analysis.setResult(results[uid]) analysis.reindexObject() can_submit = True deps = analysis.getDependencies() \ if hasattr(analysis, 'getDependencies') else [] for dependency in deps: if workflow.getInfoFor(dependency, 'review_state') in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received'): can_submit = False break if can_submit: # doActionFor transitions the analysis to verif pending, # so must only be done when results are submitted. doActionFor(analysis, 'submit') # Maybe some analyses need to be retracted due to a QC failure # Done here because don't know if the last selected analysis is # a valid QC for the instrument used in previous analyses. # If we add this logic in subscribers.analyses, there's the # possibility to retract analyses before the QC being reached. self.retractInvalidAnalyses() message = PMF("Changes saved.") self.context.plone_utils.addPortalMessage(message, 'info') self.destination_url = self.request.get_header("referer", self.context.absolute_url()) self.request.response.redirect(self.destination_url)
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): # 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 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): # 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): # 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 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 workflow_action_submit(self): uids = self.get_selected_uids() if not uids: message = _('No items selected.') self.context.plone_utils.addPortalMessage(message, 'info') self.request.response.redirect(self.context.absolute_url()) return if not is_active(self.context): message = _('Item is inactive.') self.context.plone_utils.addPortalMessage(message, 'info') self.request.response.redirect(self.context.absolute_url()) return form = self.request.form remarks = form.get('Remarks', [{}])[0] results = form.get('Result', [{}])[0] retested = form.get('retested', {}) methods = form.get('Method', [{}])[0] instruments = form.get('Instrument', [{}])[0] analysts = self.request.form.get('Analyst', [{}])[0] uncertainties = self.request.form.get('Uncertainty', [{}])[0] dlimits = self.request.form.get('DetectionLimit', [{}])[0] # XXX combine data from multiple bika listing tables. # TODO: Is this necessary? 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']) # Store affected Analysis Requests affected_ars = set() # Store affected Worksheets affected_ws = set() # Store invalid instruments-ref.analyses invalid_instrument_refs = dict() # We manually query by all analyses uids at once here instead of using # _get_selected_items from the base class, cause that function fetches # the objects by uid, but sequentially one by one query = dict(UID=uids) for brain in api.search(query, CATALOG_ANALYSIS_LISTING): uid = api.get_uid(brain) analysis = api.get_object(brain) # If not active, do nothing if not is_active(brain): continue # Need to save remarks? if uid in remarks: analysis.setRemarks(remarks[uid]) # Retested? if uid in retested: analysis.setRetested(retested[uid]) # Need to save the instrument? if uid in instruments: instrument = instruments[uid] or None analysis.setInstrument(instrument) if instrument and IReferenceAnalysis.providedBy(analysis): if is_out_of_range(analysis): # This reference analysis is out of range, so we have # to retract all analyses assigned to this same # instrument that are awaiting for verification if uid not in invalid_instrument_refs: invalid_instrument_refs[uid] = set() invalid_instrument_refs[uid].add(analysis) else: # The reference result is valid, so make the instrument # available again for further analyses instrument.setDisposeUntilNextCalibrationTest(False) # Need to save the method? if uid in methods: method = methods[uid] or None analysis.setMethod(method) # Need to save the analyst? if uid in analysts: analysis.setAnalyst(analysts[uid]) # Need to save the uncertainty? if uid in uncertainties: analysis.setUncertainty(uncertainties[uid]) # Need to save the detection limit? if uid in dlimits and dlimits[uid]: analysis.setDetectionLimitOperand(dlimits[uid]) # Need to save results? submitted = False if uid in results and results[uid]: interims = item_data.get(uid, []) analysis.setInterimFields(interims) analysis.setResult(results[uid]) # Can the analysis be submitted? # An analysis can only be submitted if all its dependencies # are valid and have been submitted already can_submit = True invalid_states = [ 'to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received' ] for dependency in analysis.getDependencies(): if in_state(dependency, invalid_states): can_submit = False break if can_submit: # doActionFor transitions the analysis to verif pending, # so must only be done when results are submitted. doActionFor(analysis, 'submit') submitted = True if IRequestAnalysis.providedBy(analysis): # Store the AR uids to be reindexed later. affected_ars.add(brain.getParentUID) if brain.worksheetanalysis_review_state == 'assigned': worksheet_uid = analysis.getWorksheetUID() if worksheet_uid: affected_ws.add(worksheet_uid) if not submitted: # Analysis has not been submitted, so we need to reindex the # object manually, to update catalog's metadata. analysis.reindexObject() # If a reference analysis with an out-of-range result and instrument # assigned has been submitted, retract then routine analyses that are # awaiting for verification and with same instrument associated retracted = list() for invalid_instrument_uid in invalid_instrument_refs.keys(): query = dict( getInstrumentUID=invalid_instrument_uid, portal_type=['Analysis', 'DuplicateAnalysis'], review_state='to_be_verified', cancellation_state='active', ) brains = api.search(query, CATALOG_ANALYSIS_LISTING) for brain in brains: analysis = api.get_object(brain) failed_msg = '{0}: {1}'.format( ulocalized_time(DateTime(), long_format=1), _("Instrument failed reference test")) an_remarks = analysis.getRemarks() analysis.setRemarks('. '.join([an_remarks, failed_msg])) doActionFor(analysis, 'retract') retracted.append(analysis) # If some analyses have been retracted because instrument failed a # reference test, then generate a pdf report if retracted: # Create the Retracted Analyses List report = AnalysesRetractedListReport(self.context, self.request, self.portal_url, 'Retracted analyses', retracted) # Attach the pdf to all ReferenceAnalysis that failed (accessible # from Instrument's Internal Calibration Tests list pdf = report.toPdf() for ref in invalid_instrument_refs.values(): ref.setRetractedAnalysesPdfReport(pdf) # Send the email try: report.sendEmail() except: pass # Finally, when we are done processing all applicable analyses, we must # attempt to initiate the submit transition on the ARs and Worksheets # the processed analyses belong to. # We stick only to affected_ars, and affected_ws # Reindex the Analysis Requests for which at least one Analysis has # been submitted. We do this here because one AR can contain multiple # Analyses, so better to just reindex the AR once instead of each time. # AR Catalog contains some metadata that that rely on the Analyses an # Analysis Request contains. if affected_ars: query = dict(UID=list(affected_ars), portal_type="AnalysisRequest") for ar_brain in api.search(query, CATALOG_ANALYSIS_REQUEST_LISTING): if ar_brain.review_state == 'to_be_verified': continue ar = api.get_object(ar_brain) if isTransitionAllowed(ar, "submit"): doActionFor(ar, "submit") else: ar.reindexObject() if affected_ws: query = dict(UID=list(affected_ws), portal_type="Worksheet") for ws_brain in api.search(query, CATALOG_WORKSHEET_LISTING): if ws_brain.review_state == 'to_be_verified': continue ws = api.get_object(ws_brain) if isTransitionAllowed(ws, "submit"): doActionFor(ws, "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)
def submit(self): """ Saves the form """ form = self.request.form remarks = form.get('Remarks', [{}])[0] results = form.get('Result', [{}])[0] retested = form.get('retested', {}) methods = form.get('Method', [{}])[0] instruments = form.get('Instrument', [{}])[0] analysts = self.request.form.get('Analyst', [{}])[0] uncertainties = self.request.form.get('Uncertainty', [{}])[0] dlimits = self.request.form.get('DetectionLimit', [{}])[0] selected = WorkflowAction._get_selected_items(self) workflow = getToolByName(self.context, 'portal_workflow') rc = getToolByName(self.context, REFERENCE_CATALOG) sm = getSecurityManager() hasInterims = {} # XXX combine data from multiple bika listing tables. item_data = {} if 'item_data' in form: if type(form['item_data']) == list: for i_d in form['item_data']: for i, d in json.loads(i_d).items(): item_data[i] = d else: item_data = json.loads(form['item_data']) # Iterate for each selected analysis and save its data as needed for uid, analysis in selected.items(): allow_edit = sm.checkPermission(EditResults, analysis) analysis_active = isActive(analysis) # Need to save remarks? if uid in remarks and allow_edit and analysis_active: analysis.setRemarks(remarks[uid]) # Retested? if uid in retested and allow_edit and analysis_active: analysis.setRetested(retested[uid]) # Need to save the instrument? if uid in instruments and analysis_active: # TODO: Add SetAnalysisInstrument permission # allow_setinstrument = sm.checkPermission(SetAnalysisInstrument) allow_setinstrument = True # ---8<----- if allow_setinstrument == True: # The current analysis allows the instrument regards # to its analysis service and method? if (instruments[uid] == ''): previnstr = analysis.getInstrument() if previnstr: previnstr.removeAnalysis(analysis) analysis.setInstrument(None) elif analysis.isInstrumentAllowed(instruments[uid]): previnstr = analysis.getInstrument() if previnstr: previnstr.removeAnalysis(analysis) analysis.setInstrument(instruments[uid]) instrument = analysis.getInstrument() instrument.addAnalysis(analysis) if analysis.meta_type == 'ReferenceAnalysis': instrument.setDisposeUntilNextCalibrationTest( False) # Need to save the method? if uid in methods and analysis_active: # TODO: Add SetAnalysisMethod permission # allow_setmethod = sm.checkPermission(SetAnalysisMethod) allow_setmethod = True # ---8<----- if allow_setmethod == True and analysis.isMethodAllowed( methods[uid]): analysis.setMethod(methods[uid]) # Need to save the analyst? if uid in analysts and analysis_active: analysis.setAnalyst(analysts[uid]) # Need to save the uncertainty? if uid in uncertainties and analysis_active: analysis.setUncertainty(uncertainties[uid]) # Need to save the detection limit? if analysis_active and uid in dlimits and dlimits[uid]: analysis.setDetectionLimitOperand(dlimits[uid]) # Need to save results? if uid in results and results[uid] and allow_edit \ and analysis_active: interims = item_data.get(uid, []) analysis.setInterimFields(interims) analysis.setResult(results[uid]) analysis.reindexObject() can_submit = True deps = analysis.getDependencies() \ if hasattr(analysis, 'getDependencies') else [] for dependency in deps: if workflow.getInfoFor(dependency, 'review_state') in \ ('to_be_sampled', 'to_be_preserved', 'sample_due', 'sample_received'): can_submit = False break if can_submit: # doActionFor transitions the analysis to verif pending, # so must only be done when results are submitted. doActionFor(analysis, 'submit') # Maybe some analyses need to be retracted due to a QC failure # Done here because don't know if the last selected analysis is # a valid QC for the instrument used in previous analyses. # If we add this logic in subscribers.analyses, there's the # possibility to retract analyses before the QC being reached. self.retractInvalidAnalyses() message = PMF("Changes saved.") self.context.plone_utils.addPortalMessage(message, 'info') self.destination_url = self.request.get_header( "referer", self.context.absolute_url()) self.request.response.redirect(self.destination_url)
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) self.context = aq_inner(self.context) workflow = getToolByName(self.context, 'portal_workflow') checkPermission = self.context.portal_membership.checkPermission context = self.context context_url = context.absolute_url() # use came_from to decide which UI action was clicked. # "workflow_action" is the action name specified in the # portal_workflow transition url. came_from = "workflow_action" action = form.get(came_from, '') if not action and not form.get('bika_listing_filter_bar_submit', ''): # workflow_action_button is the action name specified in # the bika_listing_view table buttons. came_from = "workflow_action_button" action = form.get('workflow_action_id', '') if not action: if self.destination_url == "": self.destination_url = self.request.get_header("referer", context_url) self.request.response.redirect(self.destination_url) return if action == "sample": objects = AnalysisRequestWorkflowAction._get_selected_items(self) transitioned = {'to_be_preserved': [], 'sample_due': []} dsfn = 'getDateSampled' for obj_uid, obj in objects.items(): if obj.portal_type == "AnalysisRequest": ar = obj sample = obj.getSample() else: # If it is a Sample, then fieldname is DateSampled dsfn = 'DateSampled' sample = obj ar = sample.aq_parent # can't transition inactive items if workflow.getInfoFor(sample, 'inactive_state', '') == 'inactive': continue # grab this object's Sampler and DateSampled from the form # (if the columns are available and edit controls exist) if 'getSampler' in form: try: Sampler = form['getSampler'][0][obj_uid].strip() except KeyError: continue Sampler = Sampler and Sampler or '' sample.setSampler(Sampler) sample.reindexObject() if dsfn in form: try: DateSampled = form[dsfn][0][obj_uid].strip() except KeyError: continue DateSampled = DateSampled and DateTime(DateSampled) or '' sample.setDateSampled(DateSampled) sample.reindexObject() # write them to the sample if not sample.getSampler(): # Make the message more specific if the reason for not # transitioning is that no Sampler has been selected message = _('Sampler is required for the Sampling transition of ${sample}', mapping={'sample': sample.Title()}) self.context.plone_utils.addPortalMessage(message, 'info') continue if not sample.getDateSampled(): continue Sampler = sample.getSampler() DateSampled = sample.getDateSampled() sample.reindexObject() ars = sample.getAnalysisRequests() # Analyses and AnalysisRequets have calculated fields # that are indexed; re-index all these objects. for ar in ars: ar.reindexObject() analyses = sample.getAnalyses({'review_state': 'to_be_sampled'}) for a in analyses: a.getObject().reindexObject() # transition the object if both values are present if Sampler and DateSampled: workflow.doActionFor(sample, action) new_state = workflow.getInfoFor(sample, 'review_state') doActionFor(ar, action) transitioned[new_state].append(sample.Title()) message = None for state in transitioned: tlist = transitioned[state] if len(tlist) > 1: if state == 'to_be_preserved': message = _('${items} are waiting for preservation.', mapping={'items': ', '.join(tlist)}) else: message = _('${items} are waiting to be received.', mapping={'items': ', '.join(tlist)}) self.context.plone_utils.addPortalMessage(message, 'info') elif len(tlist) == 1: if state == 'to_be_preserved': message = _('${item} is waiting for preservation.', mapping={'item': ', '.join(tlist)}) else: message = _('${item} is waiting to be received.', mapping={'item': ', '.join(tlist)}) self.context.plone_utils.addPortalMessage(message, 'info') if not message: message = _('No changes made.') self.context.plone_utils.addPortalMessage(message, 'info') self.destination_url = self.request.get_header("referer", context_url) self.request.response.redirect(self.destination_url) elif action == "preserve": objects = AnalysisRequestWorkflowAction._get_selected_items(self) transitioned = {} not_transitioned = [] Preserver = str() DatePreserved = str() for obj_uid, obj in objects.items(): if obj.portal_type == "AnalysisRequest": ar = obj sample = obj.getSample() else: sample = obj ar = sample.aq_parent # can't transition inactive items if workflow.getInfoFor(sample, 'inactive_state', '') == 'inactive': continue if not checkPermission(PreserveSample, sample): continue # grab this object's Preserver and DatePreserved from the form # (if the columns are available and edit controls exist) if 'getPreserver' in form and 'getDatePreserved' in form: try: Preserver = form['getPreserver'][0][obj_uid].strip() DatePreserved = form['getDatePreserved'][0][obj_uid].strip() except KeyError: continue Preserver = Preserver and Preserver or '' DatePreserved = DatePreserved and DateTime(DatePreserved) or '' else: continue for sp in sample.objectValues("SamplePartition"): if workflow.getInfoFor(sp, 'review_state') == 'to_be_preserved': sp.setDatePreserved(DatePreserved) sp.setPreserver(Preserver) for sp in sample.objectValues("SamplePartition"): if workflow.getInfoFor(sp, 'review_state') == 'to_be_preserved': if Preserver and DatePreserved: doActionFor(sp, action) transitioned[sp.aq_parent.Title()] = sp.Title() else: not_transitioned.append(sp) if len(transitioned.keys()) > 1: message = _('${items}: partitions are waiting to be received.', mapping={'items': ', '.join(transitioned.keys())}) else: message = _('${item}: ${part} is waiting to be received.', mapping={'item': ', '.join(transitioned.keys()), 'part': ', '.join(transitioned.values()), }) self.context.plone_utils.addPortalMessage(message, 'info') # And then the sample itself if Preserver and DatePreserved and not not_transitioned: doActionFor(sample, action) # message = _('${item} is waiting to be received.', # mapping = {'item': sample.Title()}) # message = t(message) # self.context.plone_utils.addPortalMessage(message, 'info') self.destination_url = self.request.get_header( "referer", self.context.absolute_url()) self.request.response.redirect(self.destination_url) elif action in ('prepublish', 'publish', 'republish'): # We pass a list of AR objects to Publish. # it returns a list of AR IDs which were actually published. objects = AnalysisRequestWorkflowAction._get_selected_items(self) its = [] for uid, obj in objects.items(): if isActive(obj): its.append(uid) its = ",".join(its) q = "/publish?items=" + its self.destination_url = self.context.absolute_url() + q self.request.response.redirect(self.destination_url) else: AnalysisRequestWorkflowAction.__call__(self)
def __call__(self): form = self.request.form plone.protect.CheckAuthenticator(form) workflow = getToolByName(self.context, 'portal_workflow') pc = getToolByName(self.context, 'portal_catalog') rc = getToolByName(self.context, REFERENCE_CATALOG) translate = self.context.translation_service.translate checkPermission = self.context.portal_membership.checkPermission # use came_from to decide which UI action was clicked. # "workflow_action" is the action name specified in the # portal_workflow transition url. came_from = "workflow_action" action = form.get(came_from, '') if not action: # workflow_action_button is the action name specified in # the bika_listing_view table buttons. came_from = "workflow_action_button" action = form.get('workflow_action_id', '') if not action: if self.destination_url == "": self.destination_url = self.request.get_header( "referer", self.context.absolute_url()) self.request.response.redirect(self.destination_url) return if action == "sampled": objects = AnalysisRequestWorkflowAction._get_selected_items(self) transitioned = {'to_be_preserved': [], 'sample_due': []} for obj_uid, obj in objects.items(): if obj.portal_type == "AnalysisRequest": ar = obj sample = obj.getSample() else: sample = obj ar = sample.aq_parent # can't transition inactive items if workflow.getInfoFor(sample, 'inactive_state', '') == 'inactive': continue if not checkPermission(SampleSample, sample): continue # grab this object's Sampler and DateSampled from the form # (if the columns are available and edit controls exist) if 'getSampler' in form and 'getDateSampled' in form: try: Sampler = form['getSampler'][0][obj_uid].strip() DateSampled = form['getDateSampled'][0][obj_uid].strip( ) except KeyError: continue Sampler = Sampler and Sampler or '' DateSampled = DateSampled and DateTime(DateSampled) or '' else: continue # write them to the sample sample.setSampler(Sampler) sample.setDateSampled(DateSampled) # transition the object if both values are present if Sampler and DateSampled: workflow.doActionFor(sample, 'sampled') new_state = workflow.getInfoFor(sample, 'review_state') transitioned[new_state].append(sample.Title()) doActionFor(ar, 'sampled') message = None for state in transitioned: t = transitioned[state] if len(t) > 1: if state == 'to_be_preserved': message = _('${items} are waiting for preservation.', mapping={'items': ', '.join(t)}) else: message = _('${items} are waiting to be received.', mapping={'items': ', '.join(t)}) message = self.context.translate(message) self.context.plone_utils.addPortalMessage(message, 'info') elif len(t) == 1: if state == 'to_be_preserved': message = _('${item} is waiting for preservation.', mapping={'item': ', '.join(t)}) else: message = _('${item} is waiting to be received.', mapping={'item': ', '.join(t)}) message = self.context.translate(message) self.context.plone_utils.addPortalMessage(message, 'info') if not message: message = _('No changes made.') message = self.context.translate(message) self.context.plone_utils.addPortalMessage(message, 'info') self.destination_url = self.request.get_header( "referer", self.context.absolute_url()) self.request.response.redirect(self.destination_url) elif action == "preserved": objects = AnalysisRequestWorkflowAction._get_selected_items(self) transitioned = {} not_transitioned = [] for obj_uid, obj in objects.items(): if obj.portal_type == "AnalysisRequest": ar = obj sample = obj.getSample() else: sample = obj ar = sample.aq_parent # can't transition inactive items if workflow.getInfoFor(sample, 'inactive_state', '') == 'inactive': continue if not checkPermission(PreserveSample, sample): continue # grab this object's Preserver and DatePreserved from the form # (if the columns are available and edit controls exist) if 'getPreserver' in form and 'getDatePreserved' in form: try: Preserver = form['getPreserver'][0][obj_uid].strip() DatePreserved = form['getDatePreserved'][0][ obj_uid].strip() except KeyError: continue Preserver = Preserver and Preserver or '' DatePreserved = DatePreserved and DateTime( DatePreserved) or '' else: continue for sp in sample.objectValues("SamplePartition"): if workflow.getInfoFor( sp, 'review_state') == 'to_be_preserved': sp.setDatePreserved(DatePreserved) sp.setPreserver(Preserver) for sp in sample.objectValues("SamplePartition"): if workflow.getInfoFor( sp, 'review_state') == 'to_be_preserved': if Preserver and DatePreserved: doActionFor(sp, 'preserved') transitioned[sp.aq_parent.Title()] = sp.Title() else: not_transitioned.append(sp) if len(transitioned.keys()) > 1: message = _('${items}: partitions are waiting to be received.', mapping={'items': ', '.join(transitioned.keys())}) else: message = _('${item}: ${part} is waiting to be received.', mapping={ 'item': ', '.join(transitioned.keys()), 'part': ', '.join(transitioned.values()), }) message = self.context.translate(message) self.context.plone_utils.addPortalMessage(message, 'info') # And then the sample itself if Preserver and DatePreserved and not not_transitioned: doActionFor(sample, 'preserved') #message = _('${item} is waiting to be received.', # mapping = {'item': sample.Title()}) #message = self.context.translate(message) #self.context.plone_utils.addPortalMessage(message, 'info') self.destination_url = self.request.get_header( "referer", self.context.absolute_url()) self.request.response.redirect(self.destination_url) elif action in ('prepublish', 'publish', 'republish'): # We pass a list of AR objects to Publish. # it returns a list of AR IDs which were actually published. ARs_to_publish = [] transitioned = [] if 'paths' in form: for path in form['paths']: item_id = path.split("/")[-1] item_path = path.replace("/" + item_id, '') ar = pc(id=item_id, path={ 'query': item_path, 'depth': 1 })[0].getObject() # can't publish inactive items if not( 'bika_inactive_workflow' in workflow.getChainFor(ar) and \ workflow.getInfoFor(ar, 'inactive_state', '') == 'inactive'): ar.setDatePublished(DateTime()) ARs_to_publish.append(ar) transitioned = Publish(self.context, self.request, action, ARs_to_publish)() if len(transitioned) > 1: message = _('${items} were published.', mapping={'items': ', '.join(transitioned)}) elif len(transitioned) == 1: message = _('${item} published.', mapping={'item': ', '.join(transitioned)}) else: message = _('No items were published') message = translate(message) self.context.plone_utils.addPortalMessage(message, 'info') self.destination_url = self.request.get_header( "referer", self.context.absolute_url()) self.request.response.redirect(self.destination_url) else: AnalysisRequestWorkflowAction.__call__(self)
def __call__(self): form = self.request.form plone.protect.CheckAuthenticator(form) self.context = aq_inner(self.context) workflow = getToolByName(self.context, 'portal_workflow') bc = getToolByName(self.context, 'bika_catalog') rc = getToolByName(self.context, REFERENCE_CATALOG) translate = self.context.translate checkPermission = self.context.portal_membership.checkPermission # use came_from to decide which UI action was clicked. # "workflow_action" is the action name specified in the # portal_workflow transition url. came_from = "workflow_action" action = form.get(came_from, '') if not action: # workflow_action_button is the action name specified in # the bika_listing_view table buttons. came_from = "workflow_action_button" action = form.get('workflow_action_id', '') if not action: if self.destination_url == "": self.destination_url = self.request.get_header("referer", self.context.absolute_url()) self.request.response.redirect(self.destination_url) return if action == "sample": objects = AnalysisRequestWorkflowAction._get_selected_items(self) transitioned = {'to_be_preserved':[], 'sample_due':[]} for obj_uid, obj in objects.items(): if obj.portal_type == "AnalysisRequest": ar = obj sample = obj.getSample() else: sample = obj ar = sample.aq_parent # can't transition inactive items if workflow.getInfoFor(sample, 'inactive_state', '') == 'inactive': continue # grab this object's Sampler and DateSampled from the form # (if the columns are available and edit controls exist) if 'getSampler' in form and 'getDateSampled' in form: try: Sampler = form['getSampler'][0][obj_uid].strip() DateSampled = form['getDateSampled'][0][obj_uid].strip() except KeyError: continue Sampler = Sampler and Sampler or '' DateSampled = DateSampled and DateTime(DateSampled) or '' else: continue # write them to the sample sample.setSampler(Sampler) sample.setDateSampled(DateSampled) sample.reindexObject() ars = sample.getAnalysisRequests() # Analyses and AnalysisRequets have calculated fields # that are indexed; re-index all these objects. for ar in ars: ar.reindexObject() analyses = sample.getAnalyses({'review_state':'to_be_sampled'}) for a in analyses: a.getObject().reindexObject() # transition the object if both values are present if Sampler and DateSampled: workflow.doActionFor(sample, action) new_state = workflow.getInfoFor(sample, 'review_state') doActionFor(ar, action) transitioned[new_state].append(sample.Title()) message = None for state in transitioned: tlist = transitioned[state] if len(tlist) > 1: if state == 'to_be_preserved': message = _('${items} are waiting for preservation.', mapping = {'items': ', '.join(tlist)}) else: message = _('${items} are waiting to be received.', mapping = {'items': ', '.join(tlist)}) self.context.plone_utils.addPortalMessage(message, 'info') elif len(tlist) == 1: if state == 'to_be_preserved': message = _('${item} is waiting for preservation.', mapping = {'item': ', '.join(tlist)}) else: message = _('${item} is waiting to be received.', mapping = {'item': ', '.join(tlist)}) self.context.plone_utils.addPortalMessage(message, 'info') if not message: message = _('No changes made.') self.context.plone_utils.addPortalMessage(message, 'info') self.destination_url = self.request.get_header("referer", self.context.absolute_url()) self.request.response.redirect(self.destination_url) elif action == "preserve": objects = AnalysisRequestWorkflowAction._get_selected_items(self) transitioned = {} not_transitioned = [] for obj_uid, obj in objects.items(): if obj.portal_type == "AnalysisRequest": ar = obj sample = obj.getSample() else: sample = obj ar = sample.aq_parent # can't transition inactive items if workflow.getInfoFor(sample, 'inactive_state', '') == 'inactive': continue if not checkPermission(PreserveSample, sample): continue # grab this object's Preserver and DatePreserved from the form # (if the columns are available and edit controls exist) if 'getPreserver' in form and 'getDatePreserved' in form: try: Preserver = form['getPreserver'][0][obj_uid].strip() DatePreserved = form['getDatePreserved'][0][obj_uid].strip() except KeyError: continue Preserver = Preserver and Preserver or '' DatePreserved = DatePreserved and DateTime(DatePreserved) or '' else: continue for sp in sample.objectValues("SamplePartition"): if workflow.getInfoFor(sp, 'review_state') == 'to_be_preserved': sp.setDatePreserved(DatePreserved) sp.setPreserver(Preserver) for sp in sample.objectValues("SamplePartition"): if workflow.getInfoFor(sp, 'review_state') == 'to_be_preserved': if Preserver and DatePreserved: doActionFor(sp, action) transitioned[sp.aq_parent.Title()] = sp.Title() else: not_transitioned.append(sp) if len(transitioned.keys()) > 1: message = _('${items}: partitions are waiting to be received.', mapping = {'items': ', '.join(transitioned.keys())}) else: message = _('${item}: ${part} is waiting to be received.', mapping = {'item': ', '.join(transitioned.keys()), 'part': ', '.join(transitioned.values()),}) self.context.plone_utils.addPortalMessage(message, 'info') # And then the sample itself if Preserver and DatePreserved and not not_transitioned: doActionFor(sample, action) #message = _('${item} is waiting to be received.', # mapping = {'item': sample.Title()}) #message = t(message) #self.context.plone_utils.addPortalMessage(message, 'info') self.destination_url = self.request.get_header( "referer", self.context.absolute_url()) self.request.response.redirect(self.destination_url) elif action in ('prepublish', 'publish', 'republish'): # We pass a list of AR objects to Publish. # it returns a list of AR IDs which were actually published. objects = AnalysisRequestWorkflowAction._get_selected_items(self) ARs_to_publish = [] transitioned = [] for obj_uid, obj in objects.items(): if isActive(obj): obj.setDatePublished(DateTime()) ARs_to_publish.append(obj) transitioned = self.doPublish(self.context, self.request, action, ARs_to_publish)() if len(transitioned) > 1: message = _('${items} were published.', mapping = {'items': ', '.join(transitioned)}) elif len(transitioned) == 1: message = _('${item} published.', mapping = {'item': ', '.join(transitioned)}) else: message = _('No items were published') self.context.plone_utils.addPortalMessage(message, 'info') self.destination_url = self.request.get_header("referer", self.context.absolute_url()) self.request.response.redirect(self.destination_url) else: AnalysisRequestWorkflowAction.__call__(self)
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)