def update(self, client=None): """ Update the resource on the server with fields changed for the current resource since the last update call. This method includes extra logic for handling Map's image. It also does some pretty nasty method calls due to ssid mangling, but it's necessary for function. :param client: The client that should be used (if None, the global fetchcore client is used). :raise fetchcore.exceptions.ConnectionError: Thrown if a connection to fetchcore cannot be made. :raise fetchcore.exceptions.ValidationError: Thrown if there was an issue validating the data on the server. :raise fetchcore.exceptions.DoesNotExist: Thrown if the resource does not exist on the server (updates only). :raise fetchcore.exceptions.UnsupportedOperation: Thrown if the saves are not supported for this resource. :raise fetchcore.exceptions.InternalServerError: Thrown if the server responds with an error. """ # Check if we are using the global client if client is None: client = configuration.__GLOBAL_CLIENT__ self._Resource__validate_client(client) request_kwargs = self._get_request_args_from_dict() try: # First, check if we are creating a new instance if not self.is_set('id'): raise exceptions.DoesNotExist( "You cannot update a resource that is not on the server.") else: # Generate minimal PATCH update patch_data = {} data_key = 'data' if len(request_kwargs) == 2 else 'json' json_data = request_kwargs.get(data_key) if 'aggregate_image' in self._Resource__set_since_update: # Base image has changed, so just discard it from the set (it should already be in request_kwargs) self._Resource__set_since_update.discard('aggregate_image') else: # Base image hasn't changed, so just remove it safely (if it exists) request_kwargs.pop('files', None) for field in list(self._Resource__set_since_update): patch_data[field] = json_data[field] self._Resource__set_since_update.discard(field) request_kwargs[data_key] = patch_data client.patch(self.endpoint, getattr(self, self.pk), **request_kwargs) # TODO: add exceptions and such for unauthed states when we add in more permissions except exceptions.NotFound: # Cleanup internally self._set('id', None) raise exceptions.DoesNotExist( "Resource no longer exists on the server.") except exceptions.BadRequest as e: raise exceptions.ValidationError(e.message) except exceptions.MethodNotAllowed: raise exceptions.UnsupportedOperation( "Deleting the current data is unsupported by fetchcore.")
def update(self, client=None): """Update the resource on the server with fields changed for the current resource since the last update call. :param client: The client that should be used (if None, the global fetchcore client is used). :raise ConnectionError: Thrown if a connection to fetchcore cannot be made. :raise ValidationError: Thrown if there was an issue validating the data on the server. :raise DoesNotExist: Thrown if the resource does not exist on the server (updates only). :raise UnsupportedOperation: Thrown if the saves are not supported for this resource. :raise InternalServerError: Thrown if the server responds with an error. """ # Check if we are using the global client if client is None: client = configuration.__GLOBAL_CLIENT__ self.__validate_client(client) try: # First, check if we are creating a new instance if not self.is_set('id'): raise exceptions.DoesNotExist( "You cannot update a resource that is not on the server.") elif self.__set_since_update: # Generate minimal PATCH update iff we've changed fields since the last update patch_data = {} actual_set = self.__set_since_update.copy() json_data = self.to_json_dict() for field in list(actual_set): patch_data[field] = json_data[field] actual_set.discard(field) self.__set_since_update = actual_set # Check if we have anything to send if patch_data: return client.patch(self.endpoint, getattr(self, self.pk), patch_data) else: # We should just update in that case self.refresh(client=client) return self.to_json_dict() # TODO: add exceptions and such for unauthed states when we add in more permissions except exceptions.NotFound: # Cleanup internally self._set('id', None) raise exceptions.DoesNotExist( "Resource no longer exists on the server.") except exceptions.BadRequest as e: raise exceptions.ValidationError(e.message) except exceptions.MethodNotAllowed: raise exceptions.UnsupportedOperation( "Deleting the current data is unsupported by fetchcore.")
def load(cls, identifier, client=None): """ Load the resource from the fetchcore server with the given identifier. :param identifier: The unique identifier of the resource. :param client: The client that should be used (if None, the global fetchcore client is used). :return: The loaded resource from the server. :raise fetchcore.exceptions.ConnectionError: Thrown if a connection to fetchcore cannot be made. :raise fetchcore.exceptions.DoesNotExist: Thrown if the resource does not exist with that identifier. :raise fetchcore.exceptions.InternalServerError: Thrown if the server responds with an error. """ # Check if we are using the global client if client is None: client = configuration.__GLOBAL_CLIENT__ cls._Resource__validate_client(client) try: response = client.get('maps/annotations/revisions', identifier) return cls.set_response(response) # TODO: add exceptions and such for unauthed states when we add in more permissions except exceptions.NotFound: raise exceptions.DoesNotExist( "%s with unique identifier '%s' does not exist on the server." % (cls.__name__, str(identifier)) )
def load_version_from_map(cls, version, map_id, client=None): """ Load the revision from the fetchcore server with the given version number from the given map. :param version: The version number of the revision to retrieve. Also accepted: 'latest' and 'draft' for the latest and draft versions, respectively. :param map_id: The resource ID of the map whose revision we are retrieving. :param client: The client that should be used (if None, the global fetchcore client is used). :type version: int, str :type map_id: int :type client: object :return: The loaded resource from the server. :raise fetchcore.exceptions.ConnectionError: Thrown if a connection to fetchcore cannot be made. :raise fetchcore.exceptions.DoesNotExist: Thrown if the resource does not exist with that identifier. :raise fetchcore.exceptions.InternalServerError: Thrown if the server responds with an error. """ # Check if we are using the global client if client is None: client = configuration.__GLOBAL_CLIENT__ cls._Resource__validate_client(client) try: response = client.get(cls.endpoint % {'map_id': map_id}, version) return cls.set_response(response) except exceptions.NotFound: raise exceptions.DoesNotExist( "%s with version number '%s' for map with ID '%s' does not exist on the server." % (cls.__name__, str(version), map_id) )
def refresh(self, client=None): """Refresh this resource instance with the latest data from the server. :param client: The client that should be used (if None, the global fetchcore client is used). :raise ConnectionError: Thrown if a connection to fetchcore cannot be made. :raise DoesNotExist: Thrown if the resource longer exists on the server (updates only). :raise InternalServerError: Thrown if the server responds with an error. """ # Check if we are using the global client if client is None: client = configuration.__GLOBAL_CLIENT__ self.__validate_client(client) try: # TODO: check if ID is set response = client.get(self.endpoint, getattr(self, self.pk)) or {} # Save back the data we got for key, value in response.iteritems(): self.set_values(key, value) self.__set_since_update.discard(key) # TODO: add exceptions and such for unauthed states when we add in more permissions except exceptions.NotFound: # Cleanup internally self._set('id', None) raise exceptions.DoesNotExist( "Resource no longer exists on the server.") except exceptions.MethodNotAllowed: raise exceptions.UnsupportedOperation( "Loading of the current data is unsupported by fetchcore.")
def delete(self, client=None): """Delete this resource from the server. The resource's ID must be set for this operation to work. :param client: The client that should be used (if None, the global fetchcore client is used). :raise ConnectionError: Thrown if a connection to fetchcore cannot be made. :raise ValidationError: Thrown if there was an issue validating the data on the server. :raise DoesNotExist: Thrown if the resource longer exists on the server (updates only). :raise UnsupportedOperation: Thrown if DELETE is not allowed or the resource doesn't exist. :raise InternalServerError: Thrown if the server responds with an error. """ # Check if we are using the global client if client is None: client = configuration.__GLOBAL_CLIENT__ self.__validate_client(client) try: # First, check if we are creating a new instance if not self.is_set('id'): raise exceptions.DoesNotExist( "You cannot delete a resource that is not on the server.") client.delete(self.endpoint, getattr(self, self.pk)) # TODO: add exceptions and such for unauthed states when we add in more permissions except exceptions.NotFound: # Cleanup internally self._set('id', None) raise exceptions.DoesNotExist( "Resource no longer exists on the server.") except exceptions.BadRequest as e: raise exceptions.ValidationError(e.message) except exceptions.MethodNotAllowed: raise exceptions.UnsupportedOperation( "Deleting the current data is unsupported by fetchcore.") # TODO: Add actual destructor? I don't think it's necessary or done in most modules anyway # Safe removal of ID self._pop('id')
def save(self, client=None): """ Save the current resource to the server. If the resource does not yet exist, it will be created. If it already exists all fields on the server will be overwritten with the current values. This method has extra behaviors for dealing with the Map's image field. It does some pretty nasty method calls due to ssid mangling in parent methods, but it's necessary to function. :param client: The client that should be used (if None, the global fetchcore client is used). :raise fetchcore.exceptions.ConnectionError: Thrown if a connection to fetchcore cannot be made. :raise fetchcore.exceptions.ValidationError: Thrown if there was an issue validating the data on the server. :raise fetchcore.exceptions.DoesNotExist: Thrown if the resource longer exists on the server (updates only). :raise fetchcore.exceptions.UnsupportedOperation: Thrown if the saves are not supported for this resource. :raise fetchcore.exceptions.InternalServerError: Thrown if the server responds with an error. """ # Check if we are using the global client if client is None: client = configuration.__GLOBAL_CLIENT__ self._Resource__validate_client(client) request_kwargs = self._get_request_args_from_dict() try: # First, check if we are creating a new instance if not self.is_set('id'): # TODO if map add 'format' argument response = client.post(self.endpoint, **request_kwargs) else: response = client.put(self.endpoint, getattr(self, self.pk), **request_kwargs) # Save back the data we got for key, value in response.iteritems(): self.set_values(key, value) self._Resource__set_since_update.discard(key) if 'files' in request_kwargs: self._Resource__set_since_update.discard('aggregate_image') # TODO: add exceptions and such for unauthed states when we add in more permissions except exceptions.NotFound: # Cleanup internally self._set('id', None) raise exceptions.DoesNotExist( "Resource no longer exists on the server.") except exceptions.BadRequest as e: raise exceptions.ValidationError(e.message) except exceptions.MethodNotAllowed: raise exceptions.UnsupportedOperation( "Saving or updating of the current data is unsupported by fetchcore." )
def load(cls, identifier, client=None, ignore_image=False): """ Load the resource from the fetchcore server with the given identifier. This calls the original code, and then makes an extra request for the base image. :param identifier: The unique identifier of the resource. :param client: The client that should be used (if None, the global fetchcore client is used). :param ignore_image: If the image load should be skipped. :return: The loaded resource from the server. :raise fetchcore.exceptions.ConnectionError: Thrown if a connection to fetchcore cannot be made. :raise fetchcore.exceptions.DoesNotExist: Thrown if the resource does not exist with that identifier. :raise fetchcore.exceptions.InternalServerError: Thrown if the server responds with an error. """ # Check if we are using the global client if client is None: client = configuration.__GLOBAL_CLIENT__ cls._Resource__validate_client(client) try: response = client.get(cls.endpoint, identifier) # TODO: add exceptions and such for unauthed states when we add in more permissions except exceptions.NotFound: raise exceptions.DoesNotExist( "%s with unique identifier '%s' does not exist on the server." % (cls.__name__, str(identifier))) if not ignore_image: try: image_url = cls._get_image_url_from_id(response['id']) image_data = cls.load_url_content(image_url, client=client) if image_data is not None: response['image'] = image_data except TypeError: # NoneType response (disconnected) return None except (KeyError, exceptions.DoesNotExist): # Response didn't come with an ID or image doesn't exist pass if not response: raise exceptions.ConnectionError( "Received no response when attempting to load resource.") map_object = cls.set_response(response) map_object.refresh(client, ignore_image=ignore_image) return map_object
def count(cls, client=None): """Gets the number of tasks in the system. :param client: The client to make this request with. If not provided, it will default to the global client. :return: The number of tasks :rtype: Integer """ # Check if we are using the global client if client is None: client = configuration.__GLOBAL_CLIENT__ cls._Resource__validate_client(client) try: response = client.get(cls.endpoint) return response['count'] except exceptions.NotFound: raise exceptions.DoesNotExist("%s does not exist on the server." % cls.__name__)
def load_url_content(cls, endpoint, client=None): """Load the content from an endpoint. :param endpoint: The target endpoint URL to retrieve. :param client: The client that should be used (if None, the global fetchcore client is used). :return: The content of the response from the URL. """ # Check if we are using the global client if client is None: client = configuration.__GLOBAL_CLIENT__ cls.__validate_client(client) try: response = client.get(endpoint, raw=True) return response except exceptions.NotFound: raise exceptions.DoesNotExist( "Could not retrieve content specified at endpoint %s." % endpoint)
def load(cls, identifier, client=None): """ Load the resource from the Fetchcore server with the given identifier. This calls the original code, and then makes an extra request for the file. :param identifier: The unique identifier of the resource. :param client: The client that should be used (if None, the global Fetchcore client is used). :return: The loaded resource from the server. :raise fetchcore.exceptions.ConnectionError: Thrown if a connection to Fetchcore cannot be made. :raise fetchcore.exceptions.DoesNotExist: Thrown if the resource does not exist with that identifier. :raise fetchcore.exceptions.InternalServerError: Thrown if the server responds with an error. """ # Check if we are using the global client if client is None: client = configuration.__GLOBAL_CLIENT__ cls._Resource__validate_client(client) try: response = client.get(cls.endpoint, identifier) except exceptions.NotFound: raise exceptions.DoesNotExist( "%s with unique identifier '%s' does not exist on the server." % (cls.__name__, str(identifier))) try: file_url = "sounds/%s/file" % str(response['id']) file_data = cls.load_url_content(file_url, client=client) if file_data is not None: response['file'] = file_data except TypeError: # NoneType response (disconnected) return None except (KeyError, exceptions.DoesNotExist): # Response didn't come with an ID or file doesn't exist pass if not response: raise exceptions.ConnectionError( "Received no response when attempting to load resource.") file_object = cls.set_response(response) file_object.refresh(client) return file_object
def save(self, client=None): """Save the current resource to the server. If the resource does not yet exist, it will be created. If it already exists all fields on the server will be overwritten with the current values. :param client: The client that should be used (if None, the global fetchcore client is used). :raise ConnectionError: Thrown if a connection to fetchcore cannot be made. :raise ValidationError: Thrown if there was an issue validating the data on the server. :raise DoesNotExist: Thrown if the resource longer exists on the server (updates only). :raise UnsupportedOperation: Thrown if the saves are not supported for this resource. :raise InternalServerError: Thrown if the server responds with an error. """ # Check if we are using the global client if client is None: client = configuration.__GLOBAL_CLIENT__ self.__validate_client(client) try: # First, check if we are creating a new instance if not self.is_set('id'): # TODO if map add 'format' argument response = client.post(self.endpoint, self.to_json_dict()) or {} else: response = client.put(self.endpoint, getattr(self, self.pk), self.to_json_dict()) or {} # Save back the data we got for key, value in response.iteritems(): self.set_values(key, value) self.__set_since_update.discard(key) return response # TODO: add exceptions and such for unauthed states when we add in more permissions except exceptions.NotFound: # Cleanup internally self._set('id', None) raise exceptions.DoesNotExist( "Resource no longer exists on the server.") except exceptions.BadRequest as e: raise exceptions.ValidationError(e.message) except exceptions.MethodNotAllowed: raise exceptions.UnsupportedOperation( "Saving or updating of the current data is unsupported by fetchcore." )
def list(cls, client=None, page=None, amount=None, offset=None): """List all of the given resource from the fetchcore server. :param client: The client that should be used (if None, the global fetchcore client is used). :return: The loaded resources from the server. :raise fetchcore.exceptions.ConnectionError: Thrown if a connection to fetchcore cannot be made. :raise fetchcore.exceptions.DoesNotExist: Thrown if the resource does not exist with that identifier. :raise fetchcore.exceptions.InternalServerError: Thrown if the server responds with an error. """ # Check if we are using the global client if client is None: client = configuration.__GLOBAL_CLIENT__ cls._Resource__validate_client(client) try: response = client.get(cls.endpoint, page=page, amount=amount, offset=offset) # TODO: add exceptions and such for unauthed states when we add in more permissions except exceptions.NotFound: raise exceptions.DoesNotExist("%s does not exist on the server." % cls.__name__) map_list = [] for i in range(response['count']): result = response['results'][i] if result.get('id'): try: image_url = cls._get_image_url_from_id(result['id']) image_data = cls.load_url_content(image_url, client=client) result['image'] = image_data except exceptions.DoesNotExist: pass map_object = cls.set_response(result) map_list.append(map_object) return map_list
def list(cls, client=None, endpoint=None, page=None, offset=None, amount=None): """List the specified amount of the given resource from the fetchcore server. :param client: The client that should be used (if None, the global fetchcore client is used). :param page: The optional page to load. :param offset: The first n elements to skip when loading. :param amount: The number of elements to load (set to any negative integer to try to load all of them). :return: The loaded resources from the server. :raise ~fetchcore.exceptions.ConnectionError: Thrown if a connection to fetchcore cannot be made. :raise ~fetchcore.exceptions.DoesNotExist: Thrown if the resource does not exist with that identifier. :raise ~fetchcore.exceptions.InternalServerError: Thrown if the server responds with an error. """ if not client: client = configuration.__GLOBAL_CLIENT__ cls._Resource__validate_client(client) try: endpoint_to_use = endpoint if endpoint else cls.endpoint response = client.get(endpoint_to_use, page=page, offset=offset, amount=amount) # TODO: add exceptions and such for unauthed states when we add in more permissions except exceptions.NotFound: raise exceptions.DoesNotExist("%s does not exist on the server." % cls.__name__) if not response: return [] return [ cls.set_response(result) for result in response.get('results', []) ]
def load(cls, identifier, client=None): """ Load the resource from the fetchcore server with the given identifier. This calls the original code, and then makes an extra request for the base image. :param identifier: The unique identifier of the resource. :param client: The client that should be used (if None, the global fetchcore client is used). :return: The loaded resource from the server. :raise fetchcore.exceptions.ConnectionError: Thrown if a connection to fetchcore cannot be made. :raise fetchcore.exceptions.DoesNotExist: Thrown if the resource does not exist with that identifier. :raise fetchcore.exceptions.InternalServerError: Thrown if the server responds with an error. """ # Check if we are using the global client if client is None: client = configuration.__GLOBAL_CLIENT__ cls._Resource__validate_client(client) try: response = client.get(cls.endpoint, identifier) # TODO: add exceptions and such for unauthed states when we add in more permissions except exceptions.NotFound: raise exceptions.DoesNotExist( "%s with unique identifier '%s' does not exist on the server." % (cls.__name__, str(identifier))) if response.get('id'): try: image_url = cls._get_image_url_from_id(response['id']) image_data = cls.load_url_content(image_url, client=client) response['aggregate_image'] = image_data except exceptions.DoesNotExist: pass map_object = cls.set_response(response) map_object.refresh(client) return map_object
def list(cls, client=None, page=None, amount=None, offset=None): """ List all of the given resource from the Fetchcore server. :param client: The client that should be used (if None, the global Fetchcore client is used). :return: The loaded resources from the server. :raise fetchcore.exceptions.ConnectionError: Thrown if a connection to Fetchcore cannot be made. :raise fetchcore.exceptions.DoesNotExist: Thrown if the resource does not exist with that identifier. :raise fetchcore.exceptions.InternalServerError: Thrown if the server responds with an error. """ # Check if we are using the global client if client is None: client = configuration.__GLOBAL_CLIENT__ cls._Resource__validate_client(client) try: response = client.get(cls.endpoint, page=page, amount=amount, offset=offset) except exceptions.NotFound: raise exceptions.DoesNotExist("%s does not exist on the server." % cls.__name__) sound_list = [] for i in range(response['count']): result = response['results'][i] if result.get('id'): try: file_url = "sounds/%s/file" % str(result['id']) file_data = cls.load_url_content(file_url, client=client) result['file'] = file_data except exceptions.DoesNotExist: pass sound_object = cls.set_response(result) sound_list.append(sound_object) return sound_list