def after_recover(sample): """Unassigns the sample from its storage container and "recover". It also transitions the sample to its previous state before it was stored """ container = _api.get_storage_sample(api.get_uid(sample)) if container: container.remove_object(sample) else: logger.warn("Container for Sample {} not found".format(sample.getId())) # Transition the sample to the state before it was stored previous_state = get_previous_state(sample) or "sample_due" changeWorkflowState(sample, "bika_ar_workflow", previous_state) # Notify the sample has ben modified modified(sample) # Reindex the sample sample.reindexObject() # If the sample is a partition, try to promote to the primary primary = sample.getParentAnalysisRequest() if not primary: return # Recover primary sample if all its partitions have been recovered parts = primary.getDescendants() # Partitions in some statuses won't be considered. skip = ['stored'] parts = filter(lambda part: api.get_review_status(part) in skip, parts) if not parts: # There are no partitions left, transition the primary do_action_for(primary, "recover")
def get_object_by_uid(self, uid): """Get the object by UID """ logger.debug("get_object_by_uid::UID={}".format(uid)) obj = api.get_object_by_uid(uid, None) if obj is None: logger.warn("!! No object found for UID #{} !!") return obj
def add_object(self, object_brain_uid): """Adds an object to the first available position. """ position = self.get_first_empty_position() if not position: logger.warn("Cannot add object. No empty positions available") return False return self.add_object_at(object_brain_uid, position[0], position[1])
def after_recover(sample): """Unassigns the sample from its storage container and "recover" """ container = _api.get_storage_sample(api.get_uid(sample)) if not container: logger.warn("Container for Sample {} not found".format(sample.getId())) return False return container.remove_object(sample)
def StorageContentRemovedEventHandler(container, event): """Removes the object from parent's layout (if the parent is a container) """ parent = api.get_parent(container) if not IStorageLayoutContainer.providedBy(parent): return if not parent.remove_object(container): logger.warn("Cannot remove the container '{}' from '{}'".format( container.getId(), parent.getId()))
def after_recover(sample): """Unassigns the sample from its storage container and "recover". It also transitions the sample to its previous state before it was stored """ container = _api.get_storage_sample(api.get_uid(sample)) if container: container.remove_object(sample) else: logger.warn("Container for Sample {} not found".format(sample.getId())) # Transition the sample to the state before it was stored previous_state = get_previous_state(sample, "stored") or "sample_received" changeWorkflowState(sample, "bika_ar_workflow", previous_state)
def __call__(self): form = self.request.form # Form submit toggle form_submitted = form.get("submitted", False) form_store = form.get("button_store", False) form_cancel = form.get("button_cancel", False) # Get the container container = self.get_container() if not container: return self.redirect(message=_s("No items selected"), level="warning") if not IStorageSamplesContainer.providedBy(container): logger.warn("Not a samples container: {}").format(repr(container)) self.redirect(redirect_url=self.get_next_url()) # If container is full, there is no way to add more samples there if container.is_full(): message = _("Cannot store samples. Samples container {} is full") return self.redirect(message=message.format(api.get_id(container)), level="warning") # Handle store if form_submitted and form_store: alpha_position = form.get("position") sample_uid = form.get("sample_uid") if not alpha_position or not api.is_uid(sample_uid): message = _("No position or not valid sample selected") return self.redirect(message=message) sample = api.get_object(sample_uid) logger.info("Storing sample {} in {} at {}".format( api.get_id(sample), api.get_id(container), alpha_position)) # Store position = container.alpha_to_position(alpha_position) if container.add_object_at(sample, position[0], position[1]): message = _("Stored sample {} at position {}").format( api.get_id(sample), alpha_position) if container.is_full(): return self.redirect(redirect_url=self.get_next_url()) return self.redirect(redirect_url=self.get_fallback_url(), message=message) # Handle cancel if form_submitted and form_cancel: return self.redirect(message=_("Sample storing canceled")) return self.template()
def can_add_object(self, object_brain_uid, row, column): """Returns whether the object can be added to the position """ # Is the position valid? if not self.is_valid_position(row, column): logger.warn("Position ({}, {}) not valid for '{}'" .format(row, column, self.getId())) return False # Is a valid object or a valid uid? uid = api.get_uid(object_brain_uid) if not uid: return False # If position taken, the addition is not allowed if self.get_uid_at(row, column): logger.warn("Position ({}, {}) from '{}' is already taken" .format(row, column, self.getId())) return False # If the container already contains the object, do nothing if self.has_object(object_brain_uid): object_id = api.get_object(object_brain_uid).getId() logger.warn("Container '{}' contains the object '{}' already" .format(self.getId(), object_id)) return False # Check if this type of object suits well with this container obj = api.get_object(object_brain_uid) if not self.is_object_allowed(obj): logger.warn("Container '{}' does not allow the object '{}'" .format(self.getId(), obj.getId())) return False return True
def update_workflow(portal, workflow_id, settings): """Injects 'store' and 'recover' transitions into workflow """ logger.info("Updating workflow '{}' ...".format(workflow_id)) wf_tool = api.get_tool("portal_workflow") workflow = wf_tool.getWorkflowById(workflow_id) if not workflow: logger.warn("Workflow '{}' not found [SKIP]".format(workflow_id)) states = settings.get("states", {}) for state_id, values in states.items(): update_workflow_state(workflow, state_id, values) transitions = settings.get("transitions", {}) for transition_id, values in transitions.items(): update_workflow_transition(workflow, transition_id, values)
def StorageContentModifiedEventHandler(container, event): """Adds the object to the parent's layout (if the parent is a container) We use a ObjectModifiedEvent from zope.lifecycleevent here instead of ObjectAddedEvent or InitializedEvent because: a) ObjectAddedEvent from zope.lifecycleevent is fired as soon as the object is temporarily created, when fields do not have any value set. Since we need the values from "PositionsLayout" field for the parent to get updated in accordance, we cannot use this event. b) InitializedEvent from Products.Archetypes is called as soon as the object is created (with values) after the edit form submission, but this event is not called by senaite.core's api. Hence, this cannot be used because the event will not be fired if the object is created manually unless we do a explicit call to processForm() on creation (which is not always the case). Caveats: Note that we assume the object is at least created by using Plone's default edit form or by using senaite.core's api, but if the object is created manually (e.g. using _createObjectByType), this event will not be fired. """ parent = api.get_parent(container) if not IStorageLayoutContainer.providedBy(parent): # Parent doesn't care about the changes in his children return if parent.has_object(container): if not parent.update_object(container): logger.warn("Cannot update the container '{}' from '{}'".format( container.getId(), parent.getId())) else: # The object is added at the first available position, if any if not parent.add_object(container): logger.warn("Cannot add the container '{}' into '{}'".format( container.getId(), parent.getId()))