def folderitem(self, obj, item, index): """Service triggered each time an item is iterated in folderitems. The use of this service prevents the extra-loops in child objects. :obj: the instance of the class to be foldered :item: dict containing the properties of the object to be used by the template :index: current index of the item """ item["Description"] = obj.Description() item["replace"]["Title"] = get_link(item["url"], item["Title"]) instrument = obj.getInstrument() if instrument: instrument_url = api.get_url(instrument) instrument_title = api.get_title(instrument) item["Instrument"] = instrument_title item["replace"]["Instrument"] = get_link(instrument_url, value=instrument_title) # Method method_uid = obj.getMethodUID() if method_uid: method = api.get_object_by_uid(method_uid) method_url = api.get_url(method) method_title = api.get_title(method) item["Method"] = method_title item["replace"]["Method"] = get_link(method_url, value=method_title) return item
def __call__(self, action, uids): published = [] # get the selected ARReport objects reports = map(api.get_object_by_uid, uids) # get all the contained sample UIDs of the generated PDFs sample_uids = map(self.get_sample_uids_in_report, reports) # uniquify the UIDs of the contained samples unique_sample_uids = set( list(itertools.chain.from_iterable(sample_uids))) # publish all the contained samples of the selected reports for uid in unique_sample_uids: sample = api.get_object_by_uid(uid) if self.publish_sample(sample): published.append(sample) # generate a status message of the published sample IDs message = _("No items published") if published: message = _("Published {}".format(", ".join( map(api.get_id, published)))) # add the status message for the response self.add_status_message(message, "info") # redirect back referer = self.request.get_header("referer") return self.redirect(redirect_url=referer)
def create_sample(**kwargs): """Creates a new sample """ values = kwargs and kwargs or {} request = _api.get_request() date_sampled = DateTime().strftime("%Y-%m-%d") values.update({ "DateSampled": values.get("DateSampled") or date_sampled, }) to_update = ["Client", "Contact", "SampleType"] for portal_type in to_update: field_value = values.get(portal_type) if not field_value: field_value = _api.get_uid(get_object(portal_type)) values[portal_type] = field_value services = None if "services" in values: services = values.pop("services") if not services: services = map(_api.get_uid, get_objects("AnalysisService")) client = _api.get_object_by_uid(values.get("Client")) sample = create_analysisrequest(client, request, values, services) return sample
def action_update(self): """Form action enpoint to update the attachments """ order = [] form = self.request.form attachments = form.get("attachments", []) for attachment in attachments: # attachment is a form mapping, not a dictionary -> convert values = dict(attachment) uid = values.pop("UID") obj = api.get_object_by_uid(uid) # delete the attachment if the delete flag is true if values.pop("delete", False): self.delete_attachment(obj) continue # remember the order order.append(uid) # update the attachment with the given data obj.update(**values) obj.reindexObject() # set the attachments order to the annotation storage self.set_attachments_order(order) # redirect back to the default view return self.request.response.redirect(self.context.absolute_url())
def __call__(self, value, *args, **kwargs): instance = kwargs['instance'] request = kwargs.get('REQUEST', {}) fieldname = kwargs['field'].getName() # This value in request prevents running once per subfield value. # self.name returns the name of the validator. This allows other # subfield validators to be called if defined (eg. in other add-ons) key = '{}-{}-{}'.format(self.name, instance.getId(), fieldname) if instance.REQUEST.get(key, False): return True # Walk through all AS UIDs and validate each parameter for that AS service_uids = request.get("uids", []) for uid in service_uids: err_msg = self.validate_service(request, uid) if not err_msg: continue # Validation failed service = api.get_object_by_uid(uid) title = api.get_title(service) err_msg = "{}: {}".format(title, _(err_msg)) translate = api.get_tool('translation_service').translate instance.REQUEST[key] = to_utf8(translate(safe_unicode(err_msg))) return instance.REQUEST[key] instance.REQUEST[key] = True return True
def get_attachment_data_by_uid(self, uid): """Retrieve attachment data by UID """ attachment = api.get_object_by_uid(uid, default=None) # Attachment file not found/deleted if attachment is None: return {} f = attachment.getAttachmentFile() attachment_type = attachment.getAttachmentType() attachment_keys = attachment.getAttachmentKeys() filename = f.filename filesize = self.get_filesize(f) mimetype = f.getContentType() report_option = attachment.getReportOption() return { "obj": attachment, "attachment_type": attachment_type, "attachment_keys": attachment_keys, "file": f, "uid": uid, "filesize": filesize, "filename": filename, "mimetype": mimetype, "report_option": report_option, }
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 getResultsRange(self): """Returns the valid result range for this routine analysis based on the results ranges defined in the Analysis Request this routine analysis is assigned to. A routine analysis will be considered out of range if it result falls out of the range defined in "min" and "max". If there are values set for "warn_min" and "warn_max", these are used to compute the shoulders in both ends of the range. Thus, an analysis can be out of range, but be within shoulders still. :return: A dictionary with keys "min", "max", "warn_min" and "warn_max" :rtype: dict """ specs = ResultsRangeDict() analysis_request = self.getRequest() if not analysis_request: return specs keyword = self.getKeyword() ar_ranges = analysis_request.getResultsRange() # Get the result range that corresponds to this specific analysis an_range = [rr for rr in ar_ranges if rr.get('keyword', '') == keyword] rr = an_range and an_range[0].copy() or specs # Calculated Specification calc_uid = rr.get("calculation") calc = api.get_object_by_uid(calc_uid, None) if calc: spec = rr.copy() spec["analysis_uid"] = self.UID() calc_spec = calc.calculate_result(mapping={"spec": spec}, default=rr) if calc_spec: rr.update(calc_spec) return 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 _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 process_form(self, instance, field, form, empty_marker=None, emptyReturnsMarker=False): """Return a list of dictionaries fit for ReferenceResultsField consumption. Only services which have float()able entries in result,min and max field will be included. If any of min, max, or result fields are blank, the row value is ignored here. """ values = {} # Process settings from the reference definition first ref_def = form.get("ReferenceDefinition") ref_def_uid = ref_def and ref_def[0] if ref_def_uid: ref_def_obj = api.get_object_by_uid(ref_def_uid) ref_results = ref_def_obj.getReferenceResults() # store reference results by UID to avoid duplicates rr_by_uid = dict(map(lambda r: (r.get("uid"), r), ref_results)) values.update(rr_by_uid) # selected services service_uids = form.get("uids", []) for uid in service_uids: result = self._get_spec_value(form, uid, "result") if not result: # User has to set a value for result subfield at least continue # If neither min nor max have been set, assume we only accept a # discrete result (like if % of error was 0). s_min = self._get_spec_value(form, uid, "min", result) s_max = self._get_spec_value(form, uid, "max", result) service = api.get_object_by_uid(uid) values[uid] = { "keyword": service.getKeyword(), "uid": uid, "result": result, "min": s_min, "max": s_max } return values.values(), {}
def action_add(self): """Form action to add a new attachment Code taken from bika.lims.content.addARAttachment. """ form = self.request.form parent = api.get_parent(self.context) attachment_file = form.get('AttachmentFile_file', None) AttachmentType = form.get('AttachmentType', '') AttachmentKeys = form.get('AttachmentKeys', '') ReportOption = form.get('ReportOption', 'r') # nothing to do if the attachment file is missing if attachment_file is None: logger.warn( "AttachmentView.action_add_attachment: Attachment file is missing" ) return # create attachment attachment = self.create_attachment(parent, attachment_file, AttachmentType=AttachmentType, AttachmentKeys=AttachmentKeys, ReportOption=ReportOption) # append the new UID to the end of the current order self.set_attachments_order(api.get_uid(attachment)) # handle analysis attachment analysis_uid = form.get("Analysis", None) if analysis_uid: analysis = api.get_object_by_uid(analysis_uid) others = analysis.getAttachment() attachments = [] for other in others: attachments.append(other.UID()) attachments.append(attachment.UID()) analysis.setAttachment(attachments) # The metadata for getAttachmentUIDs need to get updated, # otherwise the attachments are not displayed # https://github.com/senaite/bika.lims/issues/521 analysis.reindexObject() else: others = self.context.getAttachment() attachments = [] for other in others: attachments.append(other.UID()) attachments.append(attachment.UID()) self.context.setAttachment(attachments) if self.request['HTTP_REFERER'].endswith('manage_results'): self.request.response.redirect('{}/manage_results'.format( self.context.absolute_url())) else: self.request.response.redirect(self.context.absolute_url())
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 get_title_or_id_from_uid(uid): """Returns the title or ID from the given UID """ obj = api.get_object_by_uid(uid, default=None) if obj is None: return "" title_or_id = api.get_title(obj) or api.get_id(obj) return title_or_id
def _get_title_or_id_from_uid(uid): """Returns the title or ID from the given UID """ try: obj = api.get_object_by_uid(uid) except api.APIError: return "<Deleted {}>".format(uid) title_or_id = api.get_title(obj) or api.get_id(obj) return title_or_id
def process_form(self, instance, field, form, empty_marker=None, emptyReturnsMarker=False): """Return a list of dictionaries fit for AnalysisSpecsResultsField consumption. If neither hidemin nor hidemax are specified, only services which have float()able entries in result,min and max field will be included. If hidemin and/or hidemax specified, results might contain empty min and/or max fields. """ values = [] # selected services service_uids = form.get("uids", []) if not service_uids: # Inject empty fields for the validator values = [dict.fromkeys(field.getSubfields())] for uid in service_uids: s_min = self._get_spec_value(form, uid, "min") s_max = self._get_spec_value(form, uid, "max") if not s_min and not s_max: # If user has not set value neither for min nor max, omit this # record. Otherwise, since 'min' and 'max' are defined as # mandatory subfields, the following message will appear after # submission: "Specifications is required, please correct." continue # TODO: disallow this case in the UI if s_min and s_max: if float(s_min) > float(s_max): logger.warn("Min({}) > Max({}) is not allowed" .format(s_min, s_max)) continue min_operator = self._get_spec_value( form, uid, "min_operator", check_floatable=False) max_operator = self._get_spec_value( form, uid, "max_operator", check_floatable=False) service = api.get_object_by_uid(uid) values.append({ "keyword": service.getKeyword(), "uid": uid, "min_operator": min_operator, "min": s_min, "max_operator": max_operator, "max": s_max, "warn_min": self._get_spec_value(form, uid, "warn_min"), "warn_max": self._get_spec_value(form, uid, "warn_max"), "hidemin": self._get_spec_value(form, uid, "hidemin"), "hidemax": self._get_spec_value(form, uid, "hidemax"), "rangecomment": self._get_spec_value(form, uid, "rangecomment", check_floatable=False)}) return values, {}
def getRetest(self): """Returns the retest that comes from this analysis, if any """ back_refs = get_backreferences(self, 'AnalysisRetestOf') if not back_refs: return None if len(back_refs) > 1: logger.warn("Analysis {} with multiple retests".format(self.id)) return api.get_object_by_uid(back_refs[0])
def add_filter_by_instrument(self, query, out_params): if not self.request.form.get("getInstrumentUID", ""): return query["getInstrumentUID"] = self.request.form["getInstrumentUID"] instrument = api.get_object_by_uid(query["getInstrumentUID"]) out_params.append({ "title": _("Instrument"), "value": instrument.Title(), "type": "text" })
def add_filter_by_service(self, query, out_params): if not self.request.form.get("ServiceUID", ""): return query["getServiceUID"] = self.request.form["ServiceUID"] service = api.get_object_by_uid(query["getServiceUID"]) out_params.append({ "title": _("Analysis Service"), "value": service.Title(), "type": "text" })
def async_create_analysisrequest(self): msgs = [] form = self.request.form records = json.loads(form.get('records', '[]')) attachments = json.loads(form.get('attachments', '[]')) ARs = [] logger.info('Async create %s records' % len(records)) for n, record in enumerate(records): client_uid = record.get("Client") client = api.get_object_by_uid(client_uid) if not client: msgs.append("Error: Client {} found".format(client_uid)) continue # get the specifications and pass them directly to the AR # create function. specifications = record.pop("Specifications", {}) # Create the Analysis Request ar = create_analysisrequest(client, self.request, values=record, specifications=specifications) ARs.append(ar.getId()) _attachments = [] for att_uid in attachments.get(str(n), []): attachment = api.get_object_by_uid(att_uid) _attachments.append(attachment) if _attachments: ar.setAttachment(_attachments) if len(ARs) == 1: msgs.append('Created AR {}'.format(ARs[0])) elif len(ARs) > 1: msgs.append('Created ARs {}'.format(', '.join(ARs))) else: msgs.append('No ARs created') message = '; '.join(msgs) logger.info('AR Creation complete: {}'.format(message)) self._email_analyst(message) return
def get_next_container(self): """Returns the next container from the list of uids from the request that have not been yet processed or None """ next_uids = self.get_next_uids() if next_uids: container = api.get_object_by_uid(next_uids[0]) if IStorageSamplesContainer.providedBy(container): return container return None
def getRetest(self): """Returns the retest that comes from this analysis, if any """ relationship = "{}RetestOf".format(self.portal_type) back_refs = get_backreferences(self, relationship) if not back_refs: return None if len(back_refs) > 1: logger.warn("Analysis {} with multiple retests".format(self.id)) return api.get_object_by_uid(back_refs[0])
def _get_selected_items(self): """return a list of selected form objects full_objects defaults to True """ uids = self.get_selected_uids() selected_items = collections.OrderedDict() for uid in uids: obj = get_object_by_uid(uid) if obj: selected_items[uid] = obj return selected_items
def calc_dependants_gen(service, collector=None): """Generator for recursive resolution of dependant sevices. """ # The UID of the service service_uid = api.get_uid(service) # maintain an internal dependency mapping if collector is None: collector = {} # Stop iteration if we processed this service already if service_uid in collector: raise StopIteration # Get the dependant calculations of the service # (calculations that use the service in their formula). calc_uids = get_backreferences( service, relationship="CalculationDependentServices") for calc_uid in calc_uids: # Get the calculation object calc = api.get_object_by_uid(calc_uid) # Get the Analysis Services which have this calculation assigned dep_service_uids = get_backreferences( calc, relationship='AnalysisServiceCalculation') for dep_service_uid in dep_service_uids: dep_service = api.get_object_by_uid(dep_service_uid) # remember the dependent service collector[dep_service_uid] = dep_service # yield the dependent service yield dep_service # check the dependants of the dependant services for ddep_service in calc_dependants_gen( dep_service, collector=collector): yield ddep_service
def add_filter_by_client(self, query, out_params): """Applies the filter by client to the search query """ current_client = logged_in_client(self.context) if current_client: query['getClientUID'] = api.get_uid(current_client) elif self.request.form.get("ClientUID", ""): query['getClientUID'] = self.request.form['ClientUID'] client = api.get_object_by_uid(query['getClientUID']) out_params.append({'title': _('Client'), 'value': client.Title(), 'type': 'text'})
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 __call__(self): self.results = {} # {category_title: listofdicts} for r in self.context.getReferenceResults(): service = api.get_object_by_uid(r["uid"]) cat = service.getCategoryTitle() if cat not in self.results: self.results[cat] = [] r["service"] = service self.results[cat].append(r) self.categories = self.results.keys() self.categories.sort() return self.template()
def get_container(self): """Returns the current samples container based on the uids passed in the request """ if self.container: return self.container request_uids = self.get_uids_from_request() if not request_uids: if IStorageSamplesContainer.providedBy(self.context): self.container = self.context else: self.container = api.get_object_by_uid(request_uids[0]) return self.container
def create_items(portal_type=None, uid=None, endpoint=None, **kw): """ create items 1. If the uid is given, get the object and create the content in there (assumed that it is folderish) 2. If the uid is 0, the target folder is assumed the portal. 3. If there is no uid given, the payload is checked for either a key - `parent_uid` specifies the *uid* of the target folder - `parent_path` specifies the *physical path* of the target folder """ # import pdb;pdb.set_trace() # disable CSRF req.disable_csrf_protection() # destination where to create the content container = uid and api.get_object_by_uid(uid) or None # extract the data from the request records = req.get_request_data() results = [] for record in records: # get the portal_type if portal_type is None: # try to fetch the portal type out of the request data portal_type = record.pop("portal_type", None) # check if it is allowed to create the portal_type if not is_creation_allowed(portal_type): fail(401, "Creation of '{}' is not allowed".format(portal_type)) if container is None: # find the container for content creation container = find_target_container(portal_type, record) # Check if we have a container and a portal_type if not all([container, portal_type]): fail(400, "Please provide a container path/uid and portal_type") # create the object and pass in the record data obj = create_object(container, portal_type, **record) if type(obj) is list: results.extend(obj) else: results.append(obj) if not results: fail(400, "No Objects could be created") return make_items_for(results, endpoint=endpoint)
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 process_form(self, instance, field, form, empty_marker=None, emptyReturnsMarker=False): """Return a list of dictionaries fit for ARTemplate/Analyses field consumption. """ value = [] # selected services service_uids = form.get("uids", []) # defined partitions partitions = form.get("Partition", []) partitions = partitions and partitions[0] or {} # hidden services hidden_services = form.get("Hidden", {}) # get the service objects services = map(api.get_object_by_uid, service_uids) # get dependencies dependencies = map(lambda s: s.getServiceDependencies(), services) dependencies = list(itertools.chain.from_iterable(dependencies)) # Merge dependencies and services services = set(services + dependencies) # get the profile profile_uid = form.get("AnalysisProfile_uid") if profile_uid: profile = api.get_object_by_uid(profile_uid) # update the services with those from the profile services.update(profile.getService()) as_settings = [] for service in services: service_uid = api.get_uid(service) value.append({ "service_uid": service_uid, "partition": partitions.get(service_uid, "part-1") }) hidden = hidden_services.get(service_uid, "") == "on" as_settings.append({"uid": service_uid, "hidden": hidden}) # set the analysis services settings instance.setAnalysisServicesSettings(as_settings) # This returns the value for the Analyses Schema Field return value, {}
def folderitems(self): ars = {} for slot in self.context.getLayout(): if slot['type'] != 'a': continue ar = slot['container_uid'] if ar not in ars: ars[ar] = slot['position'] items = [] for ar, pos in ars.items(): pos = str(pos) ar = api.get_object_by_uid(ar) # this folderitems doesn't subclass from the bika_listing.py # so we create items from scratch item = { 'obj': ar, 'id': ar.id, 'uid': ar.UID(), 'title': ar.Title(), 'type_class': 'contenttype-AnalysisService', 'url': ar.absolute_url(), 'relative_url': ar.absolute_url(), 'view_url': ar.absolute_url(), 'Position': int(pos) if pos.isdigit() else pos, 'RequestID': ar.id, 'Client': ar.aq_parent.Title(), 'created': self.ulocalized_time(ar.created()), 'replace': {}, 'before': {}, 'after': {}, 'choices': {}, 'class': {}, 'state_class': 'state-active', 'allow_edit': [], 'required': [], } items.append(item) items = sorted(items, key=itemgetter('Position')) return items
def get_clients_vocabulary(self): """ Vocabulary list with clients :return: A DisplayList object """ if self.getBatches() or self.getAnalysisRequests(): # Allow to change the client if there are no ARs associated client = self.getPrimaryReferrer() if not client: # Maybe all Batches and ARs assigned to this Doctor belong to # the same Client.. If so, just assign this client by default client_uids = map(lambda ar: ar.getClientUID, self.getAnalysisRequests()) client_uids = list(set(client_uids)) if len(client_uids) > 1: # More than one client assigned! return DisplayList([('', '')]) clients = map(lambda batch: batch.getClient(), self.getBatches(full_objects=True)) client_uids += map(lambda client: api.get_uid(client), clients) client_uids = list(set(client_uids)) if len(client_uids) > 1: # More than one client assigned! return DisplayList([('', '')]) client = api.get_object_by_uid(client_uids[0]) return DisplayList([(api.get_uid(client), client.Title())]) # If the current user is a client contact, do not display other clients client = api.get_current_client() if client: return DisplayList([(api.get_uid(client), client.Title())]) # Search for clients query = dict(portal_type='Client', is_active=True, sort_order='ascending', sort_on='title') brains = api.search(query, 'portal_catalog') clients = map(lambda brain: [api.get_uid(brain), brain.Title], brains) clients.insert(0, ['', '']) return DisplayList(clients)
def get_object_by_uid(uid, default=None): """Proxy to bika.lims.api.get_object_by_uid """ return api.get_object_by_uid(uid, default)