Example #1
0
    def ssid(self, ssid):
        """Set the human-readable ssid of the map.

        :param string ssid: The map ssid.
        :raise fetchcore.exceptions.ValidationError:: Thrown if value is not a string or if value is an empty string.
        """
        if isinstance(ssid, basestring):
            # Server does not allow ssid to be blank
            if not ssid:
                raise exceptions.ValidationError("Map ssid cannot be empty.")
            self._set('ssid', ssid)
        else:
            raise exceptions.ValidationError(
                "Map ssid must be a string, not a %s." % type(ssid).__name__)
    def name(self, name):
        """Set the human-readable name of the map.

        :param string name: The map name.
        :raise fetchcore.exceptions.ValidationError:: Thrown if value is not a string or if value is an empty string.
        """
        if isinstance(name, basestring):
            # Server does not allow name to be blank
            if not name:
                raise exceptions.ValidationError("Map name cannot be empty.")
            self._set('name', name)
        else:
            raise exceptions.ValidationError(
                "Map name must be a string, not a %s." % type(name).__name__)
Example #3
0
    def y(self, y):
        """Sets the Y position of the lower-left corner of the map (in meters).

        :param float y: The y position.
        :raise fetchcore.exceptions.ValidationError: Thrown if y is not a finite number.
        """
        if Number.is_real_number(y):
            if not Number.is_finite(y):
                raise exceptions.ValidationError(
                    "Y position must be a finite non-negative number (y is %s)"
                    % y)
            self._set("y", y)
        else:
            raise exceptions.ValidationError(
                "Y position must be a number (y is %s)" % y)
Example #4
0
    def x(self, x):
        """Sets the X position of the lower-left corner of the map (in meters).

        :param float x: The x position.
        :raise fetchcore.exceptions.ValidationError: Thrown if x is not a finite number.
        """
        if Number.is_real_number(x):
            if not Number.is_finite(x):
                raise exceptions.ValidationError(
                    "X position must be a finite non-negative number (x is %s)"
                    % x)
            self._set("x", x)
        else:
            raise exceptions.ValidationError(
                "X position must be a number (x is %s)" % x)
Example #5
0
    def resolution(self, resolution):
        """Set the resolution of the map in meters/pixel.

        :param float resolution: The map resolution.
        :raise fetchcore.exceptions.ValidationError: Thrown if resolution is not a finite non-negative number.
        """
        if Number.is_real_number(resolution):
            if not Number.is_finite_non_negative(resolution):
                raise exceptions.ValidationError(
                    "Resolution must be a finite non-negative number (resolution is %s)"
                    % resolution)
            self._set("resolution", resolution)
        else:
            raise exceptions.ValidationError(
                "Resolution must be a number (resolution is %s)" % resolution)
Example #6
0
 def password(self, password):
     """Set the user's password.
     :param password: (string) The user's password.
     :raise: fetchcore.exceptions.ValidationError: Thrown if password is not a string or if password is empty.
     """
     if password == INVALID_PASSWORD:
         self._set('password', None)
     elif isinstance(password, basestring):
         if not password:
             raise exceptions.ValidationError("Password cannot be empty.")
         self._set('password', password)
     else:
         raise exceptions.ValidationError(
             "Password must be a string, not a %s." %
             type(password).__name__)
Example #7
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.")
Example #8
0
    def map(self, map_id):
        """Set the map ID that generated this map.

        :param integer map: The map ID.
        :raise fetchcore.exceptions.ValidationError:: Thrown if map_id not a finite positive integer.
        """
        if map_id is None:
            self._set('map', map_id)
        elif Number.is_integer(map_id):
            if not Number.is_finite_positive(map_id):
                raise exceptions.ValidationError(
                    "Map ID must be finite positive (item is %s)." % map_id)
            self._set('map', map_id)
        else:
            raise exceptions.ValidationError(
                "Map ID must be an integer (%s is %s)." %
                (map_id, type(map_id).__name__))
Example #9
0
    def action_id(self, action_id):
        """Set the action ID that generated this map.

        :param integer action_id: The action ID.
        :raise fetchcore.exceptions.ValidationError:: Thrown if action_id not a finite positive integer.
        """
        if action_id is None:
            self._set('action', action_id)
        elif Number.is_integer(action_id):
            if not Number.is_finite_positive(action_id):
                raise exceptions.ValidationError(
                    "Action ID must be finite positive (item is %s)." %
                    action_id)
            self._set('action', action_id)
        else:
            raise exceptions.ValidationError(
                "Action ID must be an integer (%s is %s)." %
                (action_id, type(action_id).__name__))
Example #10
0
    def theta(self, theta):
        """Set the angle of the lower-left corner of the map (in radians).

        :param float theta: The map angle.
        :raise fetchcore.exceptions.ValidationError: Thrown if theta is not a finite number in between 0 and pi.
        """
        if not Number.is_finite(theta):
            raise exceptions.ValidationError(
                "Theta must be a finite number (theta is %s)" % theta)
        else:
            self._set("theta", Number.bind_radians_to_pi(theta))
Example #11
0
    def in_progress(self, in_progress):
        """Set if the map is currently building and not final.

        :param boolean in_progress: Says whether this map is being built.
        :raise fetchcore.exceptions.ValidationError: Thrown if in_progress is not a boolean.
        """
        if not isinstance(in_progress, bool):
            raise exceptions.ValidationError(
                "In progress must be a bool, not a %s." %
                type(in_progress).__name__)
        else:
            self._set('in_progress', in_progress)
Example #12
0
    def last_connection_time(self, last_connection_time):
        """Sets the timestamp that indicates when the robot last connected

        :param last_connection_time: (string|datetime.datetime) A timestamp
        """
        if last_connection_time is None:
            self._set("last_connection_time", last_connection_time)
        else:
            try:
                self._set("last_connection_time", Timestamp.to_datetime(last_connection_time))
            except (TypeError, ValueError) as e:
                raise exceptions.ValidationError(e)
Example #13
0
    def last_boot(self, last_boot):
        """Sets the timestamp that indicates when the robot's computer last booted

        :param last_boot: (string|datetime.datetime) A timestamp
        """
        if last_boot is None:
            self._set("last_boot", last_boot)
        else:
            try:
                self._set("last_boot", Timestamp.to_datetime(last_boot))
            except (TypeError, ValueError) as e:
                raise exceptions.ValidationError(e)
Example #14
0
    def set_image_from_path(self, path):
        """Set the raw base image from a path.

        :param path: The path of the image to upload.
        :raises fetchcore.exceptions.ValidationError: if path is either not a string or an invalid path.
        """
        if isinstance(path, basestring) and os.path.isfile(path):
            with open(path, 'rb') as opened_image:
                self._set("aggregate_image", opened_image.read())
        else:
            raise exceptions.ValidationError(
                "Given path %s of type %s is not a valid path." %
                (path, type(path).__name__))
Example #15
0
    def last_name(self, last_name):
        """Set the last name of the user.

        :param last_name: (string) The user's last name.
        :raise: fetchcore.exceptions.ValidationError: Thrown if last_name is not a string.
        """
        if last_name is None or not last_name:
            self._set('last_name', None)
        elif isinstance(last_name, basestring):
            self._set('last_name', last_name)
        else:
            raise exceptions.ValidationError(
                "Last name must be a string, not a %s." %
                type(last_name).__name__)
Example #16
0
    def first_name(self, first_name):
        """Set the first name of the user

        :param first_name: (string) The user's first name.
        :raise: fetchcore.exceptions.ValidationError: Thrown if first_name is not a string.
        """
        if first_name is None or not first_name:
            self._set('first_name', None)
        elif isinstance(first_name, basestring):
            self._set('first_name', first_name)
        else:
            raise exceptions.ValidationError(
                "First name must be a string, not a %s." %
                type(first_name).__name__)
Example #17
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.")
Example #18
0
    def aggregate_image(self, image):
        """Set the raw base map image.

        :param image: (string|file) The file object or binary data string for the base map image
        :raises: fetchcore.exceptions.ValidationError: Thrown if image is neither a string nor a file object.
        """
        if isinstance(image, basestring):
            self._set("aggregate_image", image)
        elif isinstance(image, file):
            # We were provided a file object, so convert it to string and assign it
            self._set("aggregate_image", image.read())
        else:
            raise exceptions.ValidationError(
                "Aggregate image must be either a file object or string, not a %s."
                % type(image).__name__)
Example #19
0
 def file(self, f):
     """
     Set the raw base file.
     :param f: (string|file) The file object or binary data string for the file.
     :raises: fetchcore.exceptions.ValidationError: Thrown if file is neither a string nor a file object.
     """
     if isinstance(f, basestring):
         self._set('file', f)
     elif isinstance(f, file):
         # We were provided a file object, so convert it to string and assign it
         self._set('file', f.read())
     else:
         raise exceptions.ValidationError(
             "File must be either a file object or string, not a %s." %
             type(f).__name__)
Example #20
0
    def groups(self, groups):
        """Set the associated group IDs for this user.

        :param groups: (list of integers) A list of group IDs.
        :raise: fetchcore.exceptions.ValidationError Thrown if groups is not a list or if each item in the list is not a
        finite positive integer.
        """
        if groups is None:
            self._set('groups', [])
        elif not isinstance(groups, list):
            raise exceptions.ValidationError(
                "Groups must be a list, not a %s." % type(groups).__name__)
        else:
            groups = list(set(groups))
            for group in groups:
                if not Number.is_integer(group):
                    raise exceptions.ValidationError(
                        "Each item in groups must be an integer (%s is %s)." %
                        (group, type(group).__name__))
                elif not Number.is_finite_positive(group):
                    raise exceptions.ValidationError(
                        "Each item in groups must be finite positive (item is %s)."
                        % group)
            self._set('groups', groups)
Example #21
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."
            )
Example #22
0
    def action(self, action):
        """Set the action object that generated this map.

        :param action: (integer|Action) An Action or action ID.
        :raise fetchcore.exceptions.ValidationError:: Thrown if action is not an action object, integer, or None.
        """
        if isinstance(action, Action):
            if not action.is_set("id"):
                action.save()
            self.action_id = action.id
        elif action is None or isinstance(action, int):
            self.action_id = action
        else:
            raise exceptions.ValidationError(
                'Action can only be an integer, Action object or None (%s is %s).'
                % (action, type(action).__name__))
Example #23
0
    def set_values(self, key, value):
        """Saves the data that is received from the server.

        :param key: The key to save the data for.
        :param value: The value to save.
        """
        if key == 'created' or key == 'modified':
            try:
                self._set(key, Timestamp.to_datetime(value))
            except (TypeError, ValueError) as e:
                raise exceptions.ValidationError(e)
        elif key == 'id':
            self._set(key, value)
        elif key in self.read_only_fields:
            self._set(key, value)
        else:
            self.__setattr__(key, value)
Example #24
0
    def __init__(self, id=None, created=None, modified=None, **kwargs):
        """Creates a new Resource with the given optional arguments.

        :param int id: The ID of the resource (assigned automatically upon creation).
        :param created: The created timestamp (assigned automatically).
        :param modified: The modification timestamp (updated automatically).
        :type created: str, ~datetime.datetime
        :type modified: str, ~datetime.datetime
        """
        super(TimestampedResource, self).__init__(id=id)

        try:
            if created:
                self._set('created', Timestamp.to_datetime(created))
            if modified:
                self._set('modified', Timestamp.to_datetime(modified))
        except (TypeError, ValueError) as e:
            raise exceptions.ValidationError(e)
Example #25
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."
            )
Example #26
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')