def reset_samples_usage(self, recursive=True): """Resets the sample usage values (capacity and utilization) for this container. It looks through all children to reset the values. If recursive is set to True, the function reset the samples usage for contained containers too. """ items = filter(self.is_taken, self.getPositionsLayout()) items = map(lambda item: item.get["uid"], items) uids = filter(api.is_uid, items) if not uids: return query = dict(UID=uids) uids_usage = {} for brain in api.search(query, "uid_catalog"): obj = api.get_object(brain) if not IStorageLayoutContainer.providedBy(obj): continue if recursive: obj.reset_samples_usage(recursive=recursive) uids_usage[api.get_uid(obj)] = { "samples_utilization": obj.get_samples_utilization(), "samples_capacity": obj.get_samples_capacity() } new_items = list() for layout_item in self.getPositionsLayout(): item = layout_item.copy() usage = uids_usage.get(item.get("uid"), None) if usage: item.update(usage) new_items.append(item) self.setPositionsLayout(new_items)
def notify_parent(self): """Notifies the parent to update the information it holds about this container """ parent = api.get_parent(self) if IStorageLayoutContainer.providedBy(parent): parent.update_object(self)
def add_object_at(self, object_brain_uid, row, column): """Adds an object to the specified position. If an object already exists at the given position, return False. Otherwise, return True """ if not self.can_add_object(object_brain_uid, row, column): return False uid = api.get_uid(object_brain_uid) obj = api.get_object(object_brain_uid) # If the object does not implement StorageLayoutContainer, then we # assume the object is not a container, rather the content that needs to # be contained (e.g. a Sample), so we set capacity and utilization to 1 samples_capacity = 1 samples_utilization = 1 if IStorageLayoutContainer.providedBy(obj): # This is a container, so infer the capacity and utilization samples_capacity = obj.get_samples_capacity() samples_utilization = obj.get_samples_utilization() row = api.to_int(row) column = api.to_int(column) layout = [{'uid': uid, 'row': row, 'column': column, 'samples_capacity': samples_capacity, 'samples_utilization': samples_utilization,}] for item in self.getPositionsLayout(): if item["row"] == row and item["column"] == column: continue layout.append(item.copy()) self.setPositionsLayout(layout) self.notify_parent() return True
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 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()))
def get_layout_containers(self): """Returns the containers that belong to this facility and implement IStorageLayoutContainer """ return filter(lambda obj: IStorageLayoutContainer.providedBy(obj), self.objectValues())