def save(self): """Either create or persist changes on this object back to the One Codex server.""" check_bind(self) creating = self.id is None if creating and not self.__class__._has_schema_method("create"): raise MethodNotSupported("{} do not support creating.".format(self.__class__.__name__)) if not creating and not self.__class__._has_schema_method("update"): raise MethodNotSupported("{} do not support updating.".format(self.__class__.__name__)) try: self._resource.save() except HTTPError as e: if e.response.status_code == 400: err_json = e.response.json().get("errors", []) msg = pretty_print_error(err_json) raise ServerError(msg) elif e.response.status_code == 404: action = "creating" if creating else "updating" raise MethodNotSupported( "{} do not support {}.".format(self.__class__.__name__, action) ) elif e.response.status_code == 409: raise ServerError("This {} object already exists".format(self.__class__.__name__)) else: raise e
def save(self): """Either create or persist changes on this object back to the One Codex server.""" check_bind(self) creating = self.id is None if creating and not self.__class__._has_schema_method("create"): raise MethodNotSupported("{} do not support creating.".format( self.__class__.__name__)) if not creating and not self.__class__._has_schema_method("update"): raise MethodNotSupported("{} do not support updating.".format( self.__class__.__name__)) try: self._resource.save() except HTTPError as e: if e.response.status_code == 400: err_json = e.response.json().get("errors", []) msg = pretty_print_error(err_json) raise ServerError(msg) elif e.response.status_code == 404: action = "creating" if creating else "updating" raise MethodNotSupported("{} do not support {}.".format( self.__class__.__name__, action)) elif e.response.status_code == 409: raise ServerError("This {} object already exists".format( self.__class__.__name__)) else: raise e
def delete(self): """Delete this object from the One Codex server.""" check_bind(self) if self.id is None: raise ServerError("{} object does not exist yet".format(self.__class__.name)) elif not self.__class__._has_schema_method("destroy"): raise MethodNotSupported("{} do not support deletion.".format(self.__class__.__name__)) try: self._resource.delete() except HTTPError as e: if e.response.status_code == 403: raise PermissionDenied("") # FIXME: is this right? else: raise e
def delete(self): """Delete this object from the One Codex server.""" check_bind(self) if self.id is None: raise ServerError("{} object does not exist yet".format( self.__class__.name)) elif not self.__class__._has_schema_method("destroy"): raise MethodNotSupported("{} do not support deletion.".format( self.__class__.__name__)) try: self._resource.delete() except HTTPError as e: if e.response.status_code == 403: raise PermissionDenied("") # FIXME: is this right? else: raise e
def get(cls, uuid): """ Retrieve one specific {classname} object from the server by its UUID (unique 16-character id). UUIDs can be found in the web browser's address bar while viewing analyses and other objects. Parameters ---------- uuid : string UUID of the {classname} object to retrieve. Returns ------- OneCodexBase | None The {classname} object with that UUID or None if no {classname} object could be found. Examples -------- >>> api.Samples.get('xxxxxxxxxxxxxxxx') <Sample xxxxxxxxxxxxxxxx> """.format(classname=cls.__name__) check_bind(cls) # we're just retrieving one object from its uuid try: resource = cls._resource.fetch(uuid) if isinstance(resource, list): # TODO: Investigate why potion .fetch() # method is occassionally returning a list here... if len(resource) == 1: resource = resource[0] else: raise TypeError("Potion-Client error in fetching resource") except HTTPError as e: # 404 error means this doesn't exist if e.response.status_code == 404: return None else: raise e return cls(_resource=resource)
def get(cls, uuid): """Retrieve one specific object from the server by its UUID (unique 16-character id). UUIDs can be found in the web browser's address bar while viewing analyses and other objects. Parameters ---------- uuid : string UUID of the object to retrieve. Returns ------- OneCodexBase | None The object with that UUID or None if no object could be found. Examples -------- >>> api.Samples.get('xxxxxxxxxxxxxxxx') <Sample xxxxxxxxxxxxxxxx> """ check_bind(cls) # we're just retrieving one object from its uuid try: resource = cls._resource.fetch(uuid) if isinstance(resource, list): # TODO: Investigate why potion .fetch() # method is occassionally returning a list here... if len(resource) == 1: resource = resource[0] else: raise TypeError("Potion-Client error in fetching resource") except HTTPError as e: # 404 error means this doesn't exist if e.response.status_code == 404: return None else: raise e return cls(_resource=resource)
def where(cls, *filters, **keyword_filters): """Retrieve objects (Samples, Classifications, etc.) from the One Codex server. Parameters ---------- filters : `object` Advanced filters to use (not implemented) sort : `str` or `list`, optional Sort the results by this field (or list of fields). By default in descending order, but if any of the fields start with the special character ^, sort in ascending order. For example, sort=['size', '^filename'] will sort by size from largest to smallest and filename from A-Z for items with the same size. limit : `int`, optional Number of records to return. For smaller searches, this can reduce the number of network requests made. keyword_filters : `str` or `object` Filter the results by specific keywords (or filter objects, in advanced usage) Examples -------- You can filter objects that are returned locally using a lambda function: # returns only samples with a filename ending in '.gz' my_samples = Samples.where(filter=lambda s: s.filename.endswith('.gz')) Returns ------- `list` A list of all objects matching these filters. If no filters are passed, this matches all objects. """ check_bind(cls) # do this here to avoid passing this on to potion filter_func = keyword_filters.pop("filter", None) public = False if any(x["rel"] == "instances_public" for x in cls._resource._schema["links"]): public = keyword_filters.pop("public", False) instances_route = keyword_filters.pop( "_instances", "instances" if not public else "instances_public") schema = next(link for link in cls._resource._schema["links"] if link["rel"] == instances_route) sort_schema = schema["schema"]["properties"]["sort"]["properties"] where_schema = schema["schema"]["properties"]["where"]["properties"] sort = generate_potion_sort_clause(keyword_filters.pop("sort", None), sort_schema) limit = keyword_filters.pop("limit", None if not public else 1000) where = {} # we're filtering by fancy objects (like SQLAlchemy's filter) if len(filters) > 0: if len(filters) == 1 and isinstance(filters[0], dict): where = filters[0] elif all(isinstance(f, six.string_types) for f in filters): # if it's a list of strings, treat it as an multiple "get" request where = { "$uri": { "$in": [cls._convert_id_to_uri(f) for f in filters] } } else: # we're doing some more advanced filtering raise NotImplementedError( "Advanced filtering hasn't been implemented yet") # we're filtering by keyword arguments (like SQLAlchemy's filter_by) if len(keyword_filters) > 0: for k, v in generate_potion_keyword_where(keyword_filters, where_schema, cls).items(): if k in where: raise AttributeError( "Multiple definitions for same field {}".format(k)) where[k] = v # the potion-client method returns an iterator (which lazily fetchs the records # using `per_page` instances per request) so for limiting we only want to fetch the first # n (and not instantiate all the available which is what would happen if we just sliced) cursor = getattr(cls._resource, instances_route)(where=where, sort=sort, per_page=DEFAULT_PAGE_SIZE) if limit is not None: cursor = itertools.islice(cursor, limit) # finally, apply local filtering function on objects before returning wrapped = [cls(_resource=r) for r in cursor] if filter_func: if callable(filter_func): wrapped = [obj for obj in wrapped if filter_func(obj) is True] else: raise OneCodexException( "Expected callable for filter, got: {}".format( type(filter_func).__name__)) return wrapped
def where(cls, *filters, **keyword_filters): """Retrieves objects (Samples, Classifications, etc.) from the One Codex server. Parameters ---------- filters : objects Advanced filters to use (not implemented) sort : string | list, optional Sort the results by this field (or list of fields). By default in descending order, but if any of the fields start with the special character ^, sort in ascending order. For example, sort=['size', '^filename'] will sort by size from largest to smallest and filename from A-Z for items with the same size. limit : integer, optional Number of records to return. For smaller searches, this can reduce the number of network requests made. keyword_filters : strings | objects Filter the results by specific keywords (or filter objects, in advanced usage) Examples -------- You can filter objects that are returned locally using a lambda function: # returns only samples with a filename ending in '.gz' my_samples = Samples.where(filter=lambda s: s.filename.endswith('.gz')) Returns ------- list A list of all objects matching these filters. If no filters are passed, this matches all objects. """ check_bind(cls) # do this here to avoid passing this on to potion filter_func = keyword_filters.pop("filter", None) public = False if any(x["rel"] == "instances_public" for x in cls._resource._schema["links"]): public = keyword_filters.pop("public", False) instances_route = keyword_filters.pop( "_instances", "instances" if not public else "instances_public" ) schema = next(l for l in cls._resource._schema["links"] if l["rel"] == instances_route) sort_schema = schema["schema"]["properties"]["sort"]["properties"] where_schema = schema["schema"]["properties"]["where"]["properties"] sort = generate_potion_sort_clause(keyword_filters.pop("sort", None), sort_schema) limit = keyword_filters.pop("limit", None if not public else 1000) where = {} # we're filtering by fancy objects (like SQLAlchemy's filter) if len(filters) > 0: if len(filters) == 1 and isinstance(filters[0], dict): where = filters[0] elif all(isinstance(f, six.string_types) for f in filters): # if it's a list of strings, treat it as an multiple "get" request where = {"$uri": {"$in": [cls._convert_id_to_uri(f) for f in filters]}} else: # we're doing some more advanced filtering raise NotImplementedError("Advanced filtering hasn't been implemented yet") # we're filtering by keyword arguments (like SQLAlchemy's filter_by) if len(keyword_filters) > 0: for k, v in generate_potion_keyword_where(keyword_filters, where_schema, cls).items(): if k in where: raise AttributeError("Multiple definitions for same field {}".format(k)) where[k] = v # the potion-client method returns an iterator (which lazily fetchs the records # using `per_page` instances per request) so for limiting we only want to fetch the first # n (and not instantiate all the available which is what would happen if we just sliced) cursor = getattr(cls._resource, instances_route)( where=where, sort=sort, per_page=DEFAULT_PAGE_SIZE ) if limit is not None: cursor = itertools.islice(cursor, limit) # finally, apply local filtering function on objects before returning wrapped = [cls(_resource=r) for r in cursor] if filter_func: if callable(filter_func): wrapped = [obj for obj in wrapped if filter_func(obj) is True] else: raise OneCodexException( "Expected callable for filter, got: {}".format(type(filter_func).__name__) ) return wrapped
def where(cls, *filters, **keyword_filters): """ Retrieves {classname} from the One Codex server. Parameters ---------- filters : objects Advanced filters to use (not implemented) sort : string | list, optional Sort the results by this field (or list of fields). By default in descending order, but if any of the fields start with the special character ^, sort in ascending order. For example, sort=['size', '^filename'] will sort by size from largest to smallest and filename from A-Z for items with the same size. limit : integer, optional Number of records to return. For smaller searches, this can reduce the number of network requests made. keyword_filters : strings | objects Filter the results by specific keywords (or filter objects, in advanced usage) Returns ------- list A list of all {classname} matching these filters. If no filters are passed, this matches all {classname}. """.format(classname=cls.__name__) check_bind(cls) public = False if any(x['rel'] == 'instances_public' for x in cls._resource._schema['links']): public = keyword_filters.pop('public', False) instances_route = keyword_filters.pop('_instances', 'instances' if not public else 'instances_public') schema = next(l for l in cls._resource._schema['links'] if l['rel'] == instances_route) sort_schema = schema['schema']['properties']['sort']['properties'] where_schema = schema['schema']['properties']['where']['properties'] sort = generate_potion_sort_clause(keyword_filters.pop('sort', None), sort_schema) limit = keyword_filters.pop('limit', None if not public else 1000) where = {} # we're filtering by fancy objects (like SQLAlchemy's filter) if len(filters) > 0: if len(filters) == 1 and isinstance(filters[0], dict): where = filters[0] elif all(isinstance(f, six.string_types) for f in filters): # if it's a list of strings, treat it as an multiple "get" request where = {'$uri': {'$in': [cls._convert_id_to_uri(f) for f in filters]}} else: # we're doing some more advanced filtering raise NotImplementedError('Advanced filtering hasn\'t been implemented yet') # we're filtering by keyword arguments (like SQLAlchemy's filter_by) if len(keyword_filters) > 0: for k, v in generate_potion_keyword_where(keyword_filters, where_schema, cls).items(): if k in where: raise AttributeError('Multiple definitions for same field {}'.format(k)) where[k] = v # the potion-client method returns an iterator (which lazily fetchs the records # using `per_page` instances per request) so for limiting we only want to fetch the first # n (and not instantiate all the available which is what would happen if we just sliced) cursor = getattr(cls._resource, instances_route)(where=where, sort=sort, per_page=DEFAULT_PAGE_SIZE) if limit is not None: cursor = itertools.islice(cursor, limit) return [cls(_resource=r) for r in cursor]