def get_task(task_uid): """Resolves the task for the given task uid """ if not capi.is_uid(task_uid) or task_uid == "0": # 400 Bad Request, wrong task uid _fail(412, "Task uid empty or no valid format") task = api.get_queue().get_task(task_uid) if not task: _fail(404, "Task {}".format(task_uid)) if not capi.is_uid(task.context_uid): _fail(500, "Task's context uid is not valid") return task
def _to_service(self, thing): """Convert to Analysis Service :param thing: UID/Catalog Brain/Object/Something :returns: Analysis Service object or None """ # Convert UIDs to objects if api.is_uid(thing): thing = api.get_object_by_uid(thing, None) # Bail out if the thing is not a valid object if not api.is_object(thing): logger.warn("'{}' is not a valid object!".format(repr(thing))) return None # Ensure we have an object here and not a brain obj = api.get_object(thing) if IAnalysisService.providedBy(obj): return obj if IAnalysis.providedBy(obj): return obj.getAnalysisService() # An object, but neither an Analysis nor AnalysisService? # This should never happen. portal_type = api.get_portal_type(obj) logger.error("ARAnalysesField doesn't accept objects from {} type. " "The object will be dismissed.".format(portal_type)) return None
def _process_value(value): """Convert the value into a human readable diff string """ if not value: value = _("Not set") # handle strings elif isinstance(value, basestring): # XXX: bad data, e.g. in AS Method field if value == "None": value = _("Not set") # 0 is detected as the portal UID elif value == "0": value = "0" # handle physical paths elif value.startswith("/"): # remove the portal path to reduce noise in virtual hostings portal_path = api.get_path(api.get_portal()) value = value.replace(portal_path, "", 1) elif api.is_uid(value): value = _get_title_or_id_from_uid(value) # handle dictionaries elif isinstance(value, (dict)): value = json.dumps(sorted(value.items()), indent=1) # handle lists and tuples elif isinstance(value, (list, tuple)): value = sorted(map(_process_value, value)) value = "; ".join(value) # handle unicodes if isinstance(value, unicode): value = api.safe_unicode(value).encode("utf8") return str(value)
def _to_service(self, thing): """Convert to Analysis Service :param thing: UID/Catalog Brain/Object/Something :returns: Analysis Service object or None """ # Convert UIDs to objects if api.is_uid(thing): thing = api.get_object_by_uid(thing, None) # Bail out if the thing is not a valid object if not api.is_object(thing): logger.warn("'{}' is not a valid object!".format(repr(thing))) return None # Ensure we have an object here and not a brain obj = api.get_object(thing) if IAnalysisService.providedBy(obj): return obj if IAnalysis.providedBy(obj): return obj.getAnalysisService() # An object, but neither an Analysis nor AnalysisService? # This should never happen. msg = "ARAnalysesField doesn't accept objects from {} type. " \ "The object will be dismissed.".format(api.get_portal_type(obj)) logger.warn(msg) return None
def is_result_range_compliant(analysis): """Returns whether the result range from the analysis matches with the result range for the service counterpart defined in the Sample """ if not IRequestAnalysis.providedBy(analysis): return True if IDuplicateAnalysis.providedBy(analysis): # Does not make sense to apply compliance to a duplicate, cause its # valid range depends on the result of the original analysis return True rr = analysis.getResultsRange() service_uid = rr.get("uid", None) if not api.is_uid(service_uid): return True # Compare with Sample sample = analysis.getRequest() # If no Specification is set, assume is compliant specification = sample.getRawSpecification() if not specification: return True # Compare with the Specification that was initially set to the Sample sample_rr = sample.getResultsRange(search_by=service_uid) if not sample_rr: # This service is not defined in Sample's ResultsRange, we # assume this *does not* break the compliance return True return rr == sample_rr
def get_object_at(self, row, column): """Returns an object this container contains at the given position """ uid = self.get_uid_at(row, column) if not api.is_uid(uid): return None return api.get_object_by_uid(uid, default=None)
def _get_object(context, value): """Resolve a UID to an object. :param context: context is the object containing the field's schema. :type context: BaseContent :param value: A UID. :type value: string :return: Returns a Content object or None. :rtype: BaseContent """ if not value: return None if api.is_brain(value): return api.get_object(value) if api.is_object(value): return value if api.is_uid(value): uc = api.get_tool('uid_catalog', context=context) brains = uc(UID=value) if len(brains) == 0: # Broken Reference! logger.warn("Reference on {} with UID {} is broken!" .format(repr(context), value)) return None return brains[0].getObject() return None
def metadata_to_searchable_text(self, brain, key, value): """Parse the given metadata to text :param brain: ZCatalog Brain :param key: The name of the metadata column :param value: The raw value of the metadata column :returns: Searchable and translated unicode value or None """ if not value: return u"" if value is Missing.Value: return u"" if api.is_uid(value): return u"" if isinstance(value, (bool)): return u"" if isinstance(value, (list, tuple)): for v in value: return self.metadata_to_searchable_text(brain, key, v) if isinstance(value, (dict)): for k, v in value.items(): return self.metadata_to_searchable_text(brain, k, v) if self.is_date(value): return self.to_str_date(value) if "state" in key.lower(): return self.translate_review_state(value, api.get_portal_type(brain)) if not isinstance(value, basestring): value = str(value) return safe_unicode(value)
def guard(self, action): """Returns False if the sample is queued or contains queued analyses """ # Check if this current request life-cycle is handled by a consumer request = capi.get_request() queue_task_uid = request.get("queue_tuid", "") if capi.is_uid(queue_task_uid): ctx_id = capi.get_id(self.context) logger.info("Skip guard for {}: {}".format(ctx_id, action)) return True # Don't do anything if senaite.queue is not enabled if not api.is_queue_enabled(): return True # Check if the sample is queued if api.is_queued(self.context, status=["queued"]): return False # Check whether the sample contains queued analyses for brain in self.context.getAnalyses(): if api.is_queued(brain, status=["queued"]): return False return True
def process_value(self, value): """Process publication value """ # UID -> SuperModel if api.is_uid(value): # Do not process "0" as the portal object # -> Side effect in specifications when the value is "0" if value == "0": return "0" return self.to_super_model(value) # Content -> SuperModel elif api.is_object(value): return self.to_super_model(value) # String -> Unicode elif isinstance(value, basestring): return safe_unicode(value).encode("utf-8") # DateTime -> DateTime elif isinstance(value, DateTime): return value # Process list values elif isinstance(value, (LazyMap, list, tuple)): return map(self.process_value, value) # Process dict values elif isinstance(value, (dict)): return {k: self.process_value(v) for k, v in value.iteritems()} # Process function elif safe_callable(value): return self.process_value(value()) # Always return the unprocessed value last return value
def to_service_uid(uid_brain_obj_str): """Resolves the passed in element to a valid uid. Returns None if the value cannot be resolved to a valid uid """ if api.is_uid(uid_brain_obj_str) and uid_brain_obj_str != "0": return uid_brain_obj_str if api.is_object(uid_brain_obj_str): obj = api.get_object(uid_brain_obj_str) if IAnalysisService.providedBy(obj): return api.get_uid(obj) elif IRoutineAnalysis.providedBy(obj): return obj.getServiceUID() else: logger.error("Type not supported: {}".format(obj.portal_type)) return None if isinstance(uid_brain_obj_str, six.string_types): # Maybe is a keyword? query = dict(portal_type="AnalysisService", getKeyword=uid_brain_obj_str) brains = api.search(query, SETUP_CATALOG) if len(brains) == 1: return api.get_uid(brains[0]) # Or maybe a title query = dict(portal_type="AnalysisService", title=uid_brain_obj_str) brains = api.search(query, SETUP_CATALOG) if len(brains) == 1: return api.get_uid(brains[0]) return None
def get_brain(self, uid, catalog): if not api.is_uid(uid): return None query = dict(UID=uid) brains = api.search(query, catalog) if brains and len(brains) == 1: return brains[0] return None
def to_super_model(self, thing): """Wraps an object into a Super Model """ if api.is_uid(thing): return SuperModel(thing) if not api.is_object(thing): raise TypeError("Expected a portal object, got '{}'".format( type(thing))) return thing
def get_object(self, brain_or_object_or_uid): """Get the full content object. Returns None if the param passed in is not a valid, not a valid object or not found :param brain_or_object_or_uid: UID/Catalog brain/content object :returns: content object """ if api.is_uid(brain_or_object_or_uid): return api.get_object_by_uid(brain_or_object_or_uid, default=None) if api.is_object(brain_or_object_or_uid): return api.get_object(brain_or_object_or_uid) return None
def get_task(task_uid): """Resolves the task for the given task uid """ if not api.is_uid(task_uid) or task_uid == "0": # 400 Bad Request, wrong task uid _fail(412, "Task uid empty or no valid format") task = qapi.get_queue().get_task(task_uid) if not task: _fail(404, "Task {}".format(task_uid)) return task
def addDepartment(self, dep): """Adds a department :param dep: UID or department object :returns: True when the department was added """ if api.is_uid(dep): dep = api.get_object_by_uid(dep) deps = self.getDepartments() if dep not in deps: return False deps.append(dep) self.setDepartments(deps) return True
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 removeDepartment(self, dep): """Removes a department :param dep: UID or department object :returns: True when the department was removed """ if api.is_uid(dep): dep = api.get_object_by_uid(dep) deps = self.getDepartments() if dep not in deps: return False deps.remove(dep) self.setDepartments(deps) return True
def addReferenceAnalysis(self, service): """ Creates a new Reference Analysis object based on this Sample Reference, with the type passed in and associates the newly created object to the Analysis Service passed in. :param service: Object, brain or UID of the Analysis Service :param reference_type: type of ReferenceAnalysis, where 'b' is is Blank and 'c' is Control :type reference_type: A String :returns: the newly created Reference Analysis :rtype: string """ if api.is_uid(service) or api.is_brain(service): return self.addReferenceAnalysis(api.get_object(service)) if not IAnalysisService.providedBy(service): return None interim_fields = service.getInterimFields() analysis = _createObjectByType("ReferenceAnalysis", self, id=tmpID()) # Copy all the values from the schema # TODO Add Service as a param in ReferenceAnalysis constructor and do # this logic there instead of here discard = [ 'id', ] keys = service.Schema().keys() for key in keys: if key in discard: continue if key not in analysis.Schema().keys(): continue val = service.getField(key).get(service) # Campbell's mental note:never ever use '.set()' directly to a # field. If you can't use the setter, then use the mutator in order # to give the value. We have realized that in some cases using # 'set' when the value is a string, it saves the value # as unicode instead of plain string. # analysis.getField(key).set(analysis, val) mutator_name = analysis.getField(key).mutator mutator = getattr(analysis, mutator_name) mutator(val) analysis.setAnalysisService(service) ref_type = self.getBlank() and 'b' or 'c' analysis.setReferenceType(ref_type) analysis.setInterimFields(interim_fields) analysis.unmarkCreationFlag() renameAfterCreation(analysis) return analysis
def __init__(self, thing): # Type based initializers if isinstance(thing, basestring) and thing == "0": self.init_with_instance(api.get_portal()) elif api.is_uid(thing): self.init_with_uid(thing) elif api.is_brain(thing): self.init_with_brain(thing) elif api.is_object(thing): self.init_with_instance(thing) else: raise TypeError("Can not initialize a SuperModel with '{}'".format( repr(thing)))
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)
def get_client_from_query(self, query, purge=False): """Resolves the client from the query passed-in """ keys = ["getPrimaryReferrerUID", "getClientUID", "getParentUID", "UID"] for key in keys: uid = query.get(key) if not api.is_uid(uid) or uid == "0": continue client = api.get_object_by_uid(uid) if IClient.providedBy(client): if purge: # Remove the key:value from the query del (query[key]) return client return None
def get_task_uid(task_or_uid, default=_marker): """Returns the task unique identifier :param task_or_uid: QueueTask/task uid/dict :param default: (Optional) fallback value :return: the task's unique identifier """ if api.is_uid(task_or_uid) and task_or_uid != "0": return task_or_uid if isinstance(task_or_uid, QueueTask): return get_task_uid(task_or_uid.task_uid, default=default) if isinstance(task_or_uid, dict): task_uid = task_or_uid.get("task_uid", None) return get_task_uid(task_uid, default=default) if default is _marker: raise ValueError("{} is not supported".format(repr(task_or_uid))) return default
def getResultRange(self, values, uid_keyword_service): if not uid_keyword_service: return None if api.is_object(uid_keyword_service): uid_keyword_service = api.get_uid(uid_keyword_service) key = "keyword" if api.is_uid(uid_keyword_service) and uid_keyword_service != "0": # We always assume a uid of "0" refers to portal key = "uid" # Find out the item for the given uid/keyword from bika.lims.content.analysisspec import ResultsRangeDict value = filter(lambda v: v.get(key) == uid_keyword_service, values) return value and ResultsRangeDict(dict(value[0].items())) or None
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), })
def resolve_uid(self, result_range): """Resolves the uid key for the result_range passed in if it does not exist when contains a keyword """ value = result_range.copy() uid = value.get("uid") if api.is_uid(uid) and uid != "0": return value # uid key does not exist or is not valid, try to infere from keyword keyword = value.get("keyword") if keyword: query = dict(portal_type="AnalysisService", getKeyword=keyword) brains = api.search(query, SETUP_CATALOG) if len(brains) == 1: uid = api.get_uid(brains[0]) value["uid"] = uid return value
def resolve_to_uid(item): if api.is_uid(item): return item elif IAnalysisService.providedBy(item): return item.UID() elif IRoutineAnalysis.providedBy(item): return item.getServiceUID() bsc = api.get_tool("bika_setup_catalog") brains = bsc(portal_type='AnalysisService', getKeyword=item) if brains: return brains[0].UID brains = bsc(portal_type='AnalysisService', title=item) if brains: return brains[0].UID raise RuntimeError( str(item) + " should be the UID, title, keyword " " or title of an AnalysisService.")
def resolve_uid(self, raw_dict): """Returns a copy of the raw dictionary passed in, but with additional key "uid". It's value is inferred from "keyword" if present """ value = raw_dict.copy() uid = value.get("uid") if api.is_uid(uid) and uid != "0": return value # uid key does not exist or is not valid, try to infere from keyword keyword = value.get("keyword") if keyword: query = dict(portal_type="AnalysisService", getKeyword=keyword) brains = api.search(query, SETUP_CATALOG) if len(brains) == 1: uid = api.get_uid(brains[0]) value["uid"] = uid return value
def get_form_value(self, form_key, object_brain_uid, default=None): """Returns a value from the request's form for the given uid, if any """ if form_key not in self.request.form: return default uid = object_brain_uid if not api.is_uid(uid): uid = api.get_uid(object_brain_uid) values = self.request.form.get(form_key) if isinstance(values, list): if len(values) == 0: return default if len(values) > 1: logger.warn("Multiple set of values for {}".format(form_key)) values = values[0] return values.get(uid, default)
def _process_value(value): """Convert the value into a human readable diff string """ if not value: value = _("Not set") # XXX: bad data, e.g. in AS Method field elif value == "None": value = _("Not set") # 0 is detected as the portal UID elif value == "0": pass elif api.is_uid(value): value = _get_title_or_id_from_uid(value) elif isinstance(value, (dict)): value = json.dumps(sorted(value.items()), indent=1) elif isinstance(value, (list, tuple)): value = sorted(map(_process_value, value)) value = "; ".join(value) elif isinstance(value, unicode): value = api.safe_unicode(value).encode("utf8") return str(value)
def _get_service_uid(self, item): if api.is_uid(item): return item if not api.is_object(item): logger.warn("Not an UID: {}".format(item)) return None obj = api.get_object(item) if IAnalysisService.providedBy(obj): return api.get_uid(obj) if IAnalysis.providedBy(obj) and IRequestAnalysis.providedBy(obj): return obj.getServiceUID() # An object, but neither an Analysis nor AnalysisService? # This should never happen. msg = "ARAnalysesField doesn't accept objects from {} type. " \ "The object will be dismissed." logger.warn(msg.format(api.get_portal_type(obj))) return None