Esempio n. 1
0
    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
Esempio n. 2
0
 def get_minimum_size(self):
     """Returns a tuple (rows, columns) that represents the minimum size this
     container can have without removing any of the objects it contains
     """
     els = filter(self.is_taken, self.getPositionsLayout())
     rows = map(lambda el: api.to_int(el['row']), els) or [0]
     cols = map(lambda el: api.to_int(el['column']), els) or [0]
     return (max(rows) + 1, max(cols) + 1)
Esempio n. 3
0
 def get_item_at(self, row, column):
     """Returns the layout item this container contains at the given position
     """
     if not self.is_valid_position(row, column):
         return None
     for item in self.getPositionsLayout():
         if api.to_int(item["row"]) == api.to_int(row):
             if api.to_int(item["column"]) == api.to_int(column):
                 return item
     return None
Esempio n. 4
0
 def is_valid_position(self, row, column):
     """Returns whether the position defined is valid or not for this
     container's layout
     """
     col_num = api.to_int(column, default=-1)
     if col_num < 0 or col_num >= self.getColumns():
         return False
     row_num = api.to_int(row, default=-1)
     if row_num < 0 or row_num >= self.getRows():
         return False
     return True
Esempio n. 5
0
 def get_object_position(self, object_brain_uid):
     """Returns the position as a tuple (row, column) of the object. If the
     object is not found, returns None
     """
     uid = api.get_uid(object_brain_uid)
     if not uid:
         return None
     els = filter(lambda el: el.get("uid", "") == uid,
                  self.getPositionsLayout())
     if not els:
         return None
     return (api.to_int(els[0]['row']), api.to_int(els[0]['column']))
Esempio n. 6
0
    def pop(self, consumer_id):
        """Returns the next task to process, if any
        :param consumer_id: id of the consumer thread that will process the task
        :return: the task to be processed or None
        :rtype: queue.QueueTask
        """
        with self.__lock:
            # Get the queued tasks
            queued = filter(lambda t: t.status == "queued", self._tasks)
            if not queued:
                # Maybe some tasks got stuck
                self._purge()

            consumer_tasks = self._get_consumer_tasks(consumer_id)
            if consumer_tasks:
                # This consumer has tasks running already, mark those that have
                # been running for more than 10s as failed. We assume here the
                # consumer always checks there is no thread running from his
                # side before doing a pop(). Thus, we consider that when a
                # consumer does a pop(), he did not succeed with running ones.
                for consumer_task in consumer_tasks:
                    started = consumer_task.get("started")
                    if not started or started + 10 < time.time():
                        msg = "Purged on pop ({})".format(consumer_id)
                        self._fail(consumer_task, error_message=msg)
                return None

            if self.is_busy():
                # We've reached the max number of tasks to process at same time
                return None

            # Get the names and paths of tasks that are currently running
            running_names = self.get_running_task_names()
            running_paths = self.get_running_context_paths()

            # Tasks are sorted from highest to lowest priority
            for task in queued:
                # Wait some secs before a task is available for pop. We do not
                # want to start processing the task while the life-cycle of the
                # request that added the task is still alive
                delay = capi.to_int(task.get("delay"), default=0)
                if task.created + delay > time.time():
                    continue

                # Be sure there is no other consumer working in a same type of
                # task and for the same path
                if task.name in running_names:
                    # There is another consumer processing a task of same type
                    if self.strip_path(task.context_path) in running_paths:
                        # Same path, skip
                        continue

                # Update and return the task
                task.update({
                    "started": time.time(),
                    "status": "running",
                    "consumer_id": consumer_id,
                })
                return copy.deepcopy(task)
            return None
Esempio n. 7
0
    def __call__(self):
        protect.CheckAuthenticator(self.request.form)

        self.portal = api.get_portal()
        self.request.set('disable_plone.rightcolumn', 1)
        self.request.set('disable_border', 1)

        # Handle form submit
        form = self.request.form
        submitted = form.get("submitted", False)

        # nothing to do here
        if not submitted:
            return self.template()

        # Handle "Seed" action
        if form.get("seed", False):
            seeds = form.get("seeds", {})
            for key, value in seeds.items():
                value = api.to_int(value, None)
                message = ""
                if value is None:
                    message = _(
                        "Could not convert '{}' to an integer".format(value))
                elif value == 0:
                    del self.storage[key]
                    message = _("Removed key {} from storage".format(key))
                else:
                    self.set_seed(key, value)
                    message = _("Seeding key {} to {}".format(key, value))
                self.add_status_message(message, "info")

        return self.template()
Esempio n. 8
0
def get_max_retries(default=3):
    """Returns the number of retries before considering a task as failed
    """
    registry_id = "senaite.queue.max_retries"
    max_retries = api.get_registry_record(registry_id)
    max_retries = api.to_int(max_retries, default=default)
    return max_retries >= 1 and max_retries or default
Esempio n. 9
0
def get_max_seconds(default=120):
    """Returns the max number of seconds to wait for a task to finish
    """
    registry_id = "senaite.queue.max_seconds_unlock"
    max_seconds = api.get_registry_record(registry_id)
    max_seconds = api.to_int(max_seconds, default=default)
    return max_seconds >= 30 and max_seconds or default
Esempio n. 10
0
def get_min_seconds(default=3):
    """Returns the minimum number of seconds to book per task
    """
    registry_id = "senaite.queue.min_seconds_task"
    min_seconds = api.get_registry_record(registry_id)
    min_seconds = api.to_int(min_seconds, default=default)
    return min_seconds >= 1 and min_seconds or default
Esempio n. 11
0
def to_decimal(alpha_number, alphabet=ALPHABET, default=_marker):
    """Converts an alphanumeric code (e.g AB12) to an integer
    :param alpha_number: representation of an alphanumeric code
    :param alphabet: alphabet to use when alpha_number is a non-int string
    :type number: int, string, Alphanumber, float
    :type alphabet: string
    """
    num = api.to_int(alpha_number, default=None)
    if num is not None:
        return num

    alpha_number = str(alpha_number)
    regex = re.compile(r"([A-Z]+)(\d+)", re.IGNORECASE)
    matches = re.findall(regex, alpha_number)
    if not matches:
        if default is not _marker:
            return default
        raise ValueError("Not a valid alpha number: {}".format(alpha_number))

    alpha = matches[0][0]
    number = int(matches[0][1])
    max_num = 10**len(matches[0][1]) - 1
    len_alphabet = len(alphabet)
    for pos_char, alpha_char in enumerate(reversed(alpha)):
        index_char = alphabet.find(alpha_char)
        number += (index_char * max_num * len_alphabet**pos_char)

    return number
Esempio n. 12
0
    def resolve_sorting(self, query):
        """Resolves the sorting criteria for the given query
        """
        sorting = {}

        # Sort on
        sort_on = query.get("sidx", None)
        sort_on = sort_on or query.get("sort_on", None)
        sort_on = sort_on == "Title" and "sortable_title" or sort_on
        if sort_on:
            sorting["sort_on"] = sort_on

            # Sort order
            sort_order = query.get("sord", None)
            sort_order = sort_order or query.get("sort_order", None)
            if sort_order in ["desc", "reverse", "rev", "descending"]:
                sorting["sort_order"] = "descending"
            else:
                sorting["sort_order"] = "ascending"

            # Sort limit
            sort_limit = query.get("sort_limit", query.get("limit"))
            sort_limit = api.to_int(sort_limit, default=30)
            if sort_limit:
                sorting["sort_limit"] = sort_limit

        return sorting
Esempio n. 13
0
    def __init__(self, name, request, context, *arg, **kw):
        super(QueueTask, self).__init__(*arg, **kw)
        if api.is_uid(context):
            context_uid = context
            context_path = kw.get("context_path")
            if not context_path:
                raise ValueError("context_path is missing")

        elif api.is_object(context):
            context_uid = api.get_uid(context)
            context_path = api.get_path(context)

        else:
            raise TypeError("No valid context object")

        # Set defaults
        kw = kw or {}
        task_uid = str(kw.get("task_uid", tmpID()))
        uids = map(str, kw.get("uids", []))
        created = api.to_float(kw.get("created"), default=time.time())
        status = kw.get("status", None)
        min_sec = api.to_int(kw.get("min_seconds"), default=get_min_seconds())
        max_sec = api.to_int(kw.get("max_seconds"), default=get_max_seconds())
        priority = api.to_int(kw.get("priority"), default=10)
        retries = api.to_int(kw.get("retries"), default=get_max_retries())
        unique = self._is_true(kw.get("unique", False))
        chunks = api.to_int(kw.get("chunk_size"), default=get_chunk_size(name))
        username = kw.get("username", self._get_authenticated_user(request))
        err_message = kw.get("error_message", None)

        self.update({
            "task_uid": task_uid,
            "name": name,
            "context_uid": context_uid,
            "context_path": context_path,
            "uids": uids,
            "created": created,
            "status": status and str(status) or None,
            "error_message": err_message and str(err_message) or None,
            "min_seconds": min_sec,
            "max_seconds": max_sec,
            "priority": priority,
            "retries": retries,
            "unique": unique,
            "chunk_size": chunks,
            "username": str(username),
        })
Esempio n. 14
0
 def alpha_to_position(self, alpha):
     """Converts an alphanumeric value to a position
     """
     alphabet = string.ascii_uppercase
     regex = re.compile(r"([A-Z]+)(\d+)", re.IGNORECASE)
     matches = re.findall(regex, alpha)
     alpha_part = matches[0][0]
     column = api.to_int(matches[0][1]) - 1
     row = 0
     mapping = map(lambda val: alphabet.index(val), alpha_part)
     for idx in range(len(mapping)):
         row += idx * len(alphabet) + mapping[idx]
     return (row, column)
Esempio n. 15
0
    def process(self, task):
        """Transition the objects from the task
        """
        # The worksheet is the context
        worksheet = self.context

        uids = task.get("uids", [])
        slots = task.get("slots", [])

        # Sanitize the slots list and pad with empties
        slots = map(lambda s: _api.to_int(s, None) or "", slots)
        slots += [""] * abs(len(uids) - len(slots))

        # Sort analyses so those with an assigned slot are added first
        # Note numeric values get precedence over strings, empty strings here
        uids_slots = zip(uids, slots)
        uids_slots = sorted(uids_slots, key=lambda i: i[1])

        # Remove those with no valid uids
        uids_slots = filter(lambda us: _api.is_uid(us[0]), uids_slots)

        # Remove duplicate uids while keeping the order
        seen = set()
        uids_slots = filter(lambda us: not (us[0] in seen or seen.add(us[0])),
                            uids_slots)

        # Remove uids that are already in the worksheet (just in case)
        layout = filter(None, worksheet.getLayout() or [])
        existing = map(lambda r: r.get("analysis_uid"), layout)
        uids_slots = filter(lambda us: us[0] not in existing, uids_slots)

        # If there are too many objects to process, split them in chunks to
        # prevent the task to take too much time to complete
        chunks = get_chunks_for(task, items=uids_slots)

        # Process the first chunk
        for uid, slot in chunks[0]:
            # Add the analysis
            slot = slot or None
            analysis = _api.get_object_by_uid(uid)
            worksheet.addAnalysis(analysis, slot)

        # Reindex the worksheet
        worksheet.reindexObject()

        if chunks[1]:
            # Unpack the remaining analyses-slots and add them to the queue
            uids, slots = zip(*chunks[1])
            api.add_assign_task(worksheet, analyses=uids, slots=slots)
Esempio n. 16
0
def get_chunk_size(name_or_action=None):
    """Returns the number of items to process at once for the given task name
    :param name_or_action: task name or workflow action id
    :returns: the number of items from the task to process async at once
    :rtype: int
    """
    chunk_size = api.get_registry_record("senaite.queue.default")
    chunk_size = api.to_int(chunk_size, 0)
    if chunk_size <= 0:
        # Queue disabled
        return 0

    if name_or_action:
        # TODO Retrieve task-specific chunk-sizes via adapters
        pass

    if chunk_size < 0:
        chunk_size = 0

    return chunk_size
Esempio n. 17
0
 def minimum_length(self):
     """Minimum required length of the search term
     """
     min_length = self.request.get("minLength", 0)
     return api.to_int(min_length, 0)
Esempio n. 18
0
 def set_seed(self, key, value):
     """Set a number of the number generator
     """
     number_generator = getUtility(INumberGenerator)
     return number_generator.set_number(key, api.to_int(value))
Esempio n. 19
0
 def get_layout_subfield_sum(self, subfield):
     """Returns the sum of the elements stored in the layout for the subfield
     name passed in. If the value for the element is not floatable, uses 0
     """
     layout = self.getPositionsLayout()
     return sum(map(lambda el: api.to_int(el[subfield], default=0), layout))
Esempio n. 20
0
 def extract_period(val, period):
     num = re.findall(r'(\d{1,2})' + period, val) or [0]
     return api.to_int(num[0], default=0)
Esempio n. 21
0
 def num_page(self):
     """Returns the number of page to render
     """
     return api.to_int(self.request.get("page", None), default=1)
Esempio n. 22
0
 def num_rows_page(self):
     """Returns the number of rows per page to render
     """
     return api.to_int(self.request.get("rows", None), default=10)
Esempio n. 23
0
def get_default_num_samples():
    """Returns the num of Samples (Columns) to be displayed in Sample Add Form
    """
    ar_count = api.get_setup().getDefaultNumberOfARsToAdd()
    return api.to_int(ar_count, 1)