Esempio n. 1
0
    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.")
Esempio n. 2
0
    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.")
Esempio n. 3
0
    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))
            )
Esempio n. 4
0
    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)
            )
Esempio n. 5
0
    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.")
Esempio n. 6
0
    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')
Esempio n. 7
0
    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."
            )
Esempio n. 8
0
    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
Esempio n. 9
0
    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__)
Esempio n. 10
0
    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)
Esempio n. 11
0
    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
Esempio n. 12
0
    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."
            )
Esempio n. 13
0
    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
Esempio n. 14
0
    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', [])
        ]
Esempio n. 15
0
    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
Esempio n. 16
0
    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