示例#1
0
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")
示例#2
0
 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
示例#3
0
 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])
示例#4
0
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)
示例#5
0
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()))
示例#6
0
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()
示例#8
0
    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
示例#9
0
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)
示例#10
0
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()))