def after_retract(analysis_request): """Method triggered after a 'retract' transition for the Analysis Request passed in is performed. Cascades the transition to descendants (partitions) and analyses as well. """ do_action_to_descendants(analysis_request, "retract") do_action_to_analyses(analysis_request, "retract")
def after_cancel(analysis_request): """Method triggered after a 'cancel' transition for the Analysis Request passed in is performed. Cascades this transition to its analyses and partitions. """ do_action_to_descendants(analysis_request, "cancel") do_action_to_analyses(analysis_request, "cancel")
def after_publish(analysis_request): """Method triggered after an 'publish' transition for the Analysis Request passed in is performed. Performs the 'publish' transition Publishes the descendant partitions and all analyses associated to the analysis request as well. """ do_action_to_descendants(analysis_request, "publish") do_action_to_analyses(analysis_request, "publish")
def after_reject(analysis_request): """Method triggered after a 'reject' transition for the Analysis Request passed in is performed. Cascades the transition to descendants (partitions) and analyses as well. If "notify on rejection" setting is enabled, sends a notification to the client contact. """ do_action_to_descendants(analysis_request, "reject") do_action_to_analyses(analysis_request, "reject")
def after_receive(analysis_request): """Method triggered after "receive" transition for the Analysis Request passed in is performed """ # Mark this analysis request as IReceived alsoProvides(analysis_request, IReceived) analysis_request.setDateReceived(DateTime()) do_action_to_analyses(analysis_request, "initialize")
def after_reinstate(analysis_request): """Method triggered after a 'reinstate' transition for the Analysis Request passed in is performed. Sets its status to the last status before it was cancelled. Reinstates the descendant partitions and all the analyses associated to the analysis request as well. """ do_action_to_descendants(analysis_request, "reinstate") do_action_to_analyses(analysis_request, "reinstate") # Force the transition to previous state before the request was cancelled prev_status = get_prev_status_from_history(analysis_request, "cancelled") changeWorkflowState(analysis_request, AR_WORKFLOW_ID, prev_status, action="reinstate") analysis_request.reindexObject()
def after_reject(analysis_request): """Method triggered after a 'reject' transition for the Analysis Request passed in is performed. Cascades the transition to descendants (partitions) and analyses as well. If "notify on rejection" setting is enabled, sends a notification to the client contact. """ do_action_to_descendants(analysis_request, "reject") do_action_to_analyses(analysis_request, "reject") # TODO Workflow - AnalysisRequest - Revisit rejection notification if not analysis_request.bika_setup.getNotifyOnSampleRejection(): return ancestor = analysis_request.getParentAnalysisRequest() if ancestor and api.get_workflow_status_of(ancestor) == "rejected": # No need to notify, notification done by the ancestor return # Notify the Client about the Rejection. from bika.lims.utils.analysisrequest import notify_rejection notify_rejection(analysis_request)
def create_analysisrequest(client, request, values, analyses=None, results_ranges=None, prices=None): """Creates a new AnalysisRequest (a Sample) object :param client: The container where the Sample will be created :param request: The current Http Request object :param values: A dict, with keys as AnalaysisRequest's schema field names :param analyses: List of Services or Analyses (brains, objects, UIDs, keywords). Extends the list from values["Analyses"] :param results_ranges: List of Results Ranges. Extends the results ranges from the Specification object defined in values["Specification"] :param prices: Mapping of AnalysisService UID -> price. If not set, prices are read from the associated analysis service. """ # Don't pollute the dict param passed in values = dict(values.items()) # Resolve the Service uids of analyses to be added in the Sample. Values # passed-in might contain Profiles and also values that are not uids. Also, # additional analyses can be passed-in through either values or services service_uids = to_services_uids(values=values, services=analyses) # Remove the Analyses from values. We will add them manually values.update({"Analyses": []}) # Create the Analysis Request and submit the form ar = _createObjectByType('AnalysisRequest', client, tmpID()) ar.processForm(REQUEST=request, values=values) # Set the analyses manually ar.setAnalyses(service_uids, prices=prices, specs=results_ranges) # Handle hidden analyses from template and profiles # https://github.com/senaite/senaite.core/issues/1437 # https://github.com/senaite/senaite.core/issues/1326 apply_hidden_services(ar) # Handle rejection reasons rejection_reasons = resolve_rejection_reasons(values) ar.setRejectionReasons(rejection_reasons) # Handle secondary Analysis Request primary = ar.getPrimaryAnalysisRequest() if primary: # Mark the secondary with the `IAnalysisRequestSecondary` interface alsoProvides(ar, IAnalysisRequestSecondary) # Rename the secondary according to the ID server setup renameAfterCreation(ar) # Set dates to match with those from the primary ar.setDateSampled(primary.getDateSampled()) ar.setSamplingDate(primary.getSamplingDate()) ar.setDateReceived(primary.getDateReceived()) # Force the transition of the secondary to received and set the # description/comment in the transition accordingly. if primary.getDateReceived(): primary_id = primary.getId() comment = "Auto-received. Secondary Sample of {}".format( primary_id) changeWorkflowState(ar, AR_WORKFLOW_ID, "sample_received", action="receive", comments=comment) # Mark the secondary as received alsoProvides(ar, IReceived) # Initialize analyses do_action_to_analyses(ar, "initialize") # Notify the ar has ben modified modified(ar) # Reindex the AR ar.reindexObject() # If rejection reasons have been set, reject automatically if rejection_reasons: do_rejection(ar) # In "received" state already return ar # Try first with no sampling transition, cause it is the most common config success, message = doActionFor(ar, "no_sampling_workflow") if not success: doActionFor(ar, "to_be_sampled") # If rejection reasons have been set, reject the sample automatically if rejection_reasons: do_rejection(ar) return ar
def create_analysisrequest(client, request, values, analyses=None, partitions=None, specifications=None, prices=None): """This is meant for general use and should do everything necessary to create and initialise an AR and any other required auxilliary objects (Sample, SamplePartition, Analysis...) :param client: The container (Client) in which the ARs will be created. :param request: The current Request object. :param values: a dict, where keys are AR|Sample schema field names. :param analyses: Analysis services list. If specified, augments the values in values['Analyses']. May consist of service objects, UIDs, or Keywords. :param partitions: A list of dictionaries, if specific partitions are required. If not specified, AR's sample is created with a single partition. :param specifications: These values augment those found in values['Specifications'] :param prices: Allow different prices to be set for analyses. If not set, prices are read from the associated analysis service. """ # Don't pollute the dict param passed in values = dict(values.items()) # Create the Analysis Request ar = _createObjectByType('AnalysisRequest', client, tmpID()) # Resolve the services uids and set the analyses for this Analysis Request service_uids = get_services_uids(context=client, values=values, analyses_serv=analyses) ar.setAnalyses(service_uids, prices=prices, specs=specifications) values.update({"Analyses": service_uids}) ar.processForm(REQUEST=request, values=values) # Handle rejection reasons rejection_reasons = resolve_rejection_reasons(values) ar.setRejectionReasons(rejection_reasons) # Handle secondary Analysis Request primary = ar.getPrimaryAnalysisRequest() if primary: # Mark the secondary with the `IAnalysisRequestSecondary` interface alsoProvides(ar, IAnalysisRequestSecondary) # Rename the secondary according to the ID server setup renameAfterCreation(ar) # Set dates to match with those from the primary ar.setDateSampled(primary.getDateSampled()) ar.setSamplingDate(primary.getSamplingDate()) ar.setDateReceived(primary.getDateReceived()) # Force the transition of the secondary to received and set the # description/comment in the transition accordingly. if primary.getDateReceived(): primary_id = primary.getId() comment = "Auto-received. Secondary Sample of {}".format( primary_id) changeWorkflowState(ar, AR_WORKFLOW_ID, "sample_received", action="receive", comments=comment) # Mark the secondary as received alsoProvides(ar, IReceived) # Initialize analyses do_action_to_analyses(ar, "initialize") # Notify the ar has ben modified modified(ar) # Reindex the AR ar.reindexObject() # If rejection reasons have been set, reject automatically if rejection_reasons: doActionFor(ar, "reject") # In "received" state already return ar # Try first with no sampling transition, cause it is the most common config success, message = doActionFor(ar, "no_sampling_workflow") if not success: doActionFor(ar, "to_be_sampled") # If rejection reasons have been set, reject the sample automatically if rejection_reasons: doActionFor(ar, "reject") return ar