예제 #1
0
    def _get_value(self, instance):  # type: (base.Entity) -> M
        if self.cardinality == MappingCardinality.ONE:
            try:
                id = instance.__dict__[self.mapped_field]

                # Hack to resolve default if the ID is defined but None
                if id is None:
                    raise KeyError
            except KeyError:
                default = self.default

                # No default, no point of continuing
                if default is NOTSET:
                    raise AttributeError(
                        'Instance {} has not set mapping field \'{}\'/\'{}\''.
                        format(instance, self.name, self.mapped_field))

                if callable(default):
                    default = default(getattr(instance, '_config', None))

                if isinstance(default, base.TogglEntity):
                    return default

                id = default

            return self.mapped_cls.objects.get(id,
                                               config=getattr(
                                                   instance, '_config', None))

        elif self.cardinality == MappingCardinality.MANY:
            raise NotImplementedError("Not implemented yet")
        else:
            raise exceptions.TogglException(
                '{}: Unknown cardinality \'{}\''.format(
                    self.name, self.cardinality))
예제 #2
0
파일: base.py 프로젝트: tbrodbeck/toggl-cli
    def filter(self, order='asc', config=None, contain=False, **conditions):  # type: (str, utils.Config, bool, **typing.Any) -> typing.List[Entity]
        """
        Method that fetches all entries and filter them out based on specified conditions.

        :param order: Strings 'asc' or 'desc' which specifies how the results will be sorted (
        :param config: Config instance
        :param contain: Specify how evaluation of conditions is performed. If True condition is evaluated using 'in' operator, otherwise hard equality (==) is enforced.
        :param conditions: Dict of conditions to filter the results. It has structure 'name of property' => 'value'
        """
        config = config or utils.Config.factory()

        if self.entity_cls is None:
            raise exceptions.TogglException('The TogglSet instance is not binded to any TogglEntity!')

        if not self.can_get_list:
            raise exceptions.TogglNotAllowedException('Entity {} is not allowed to fetch list from the API!'
                                            .format(self.entity_cls))

        url = self.build_list_url('filter', config, conditions)
        fetched_entities = self._fetch_all(url, order, config)

        if fetched_entities is None:
            return []

        # There are no specified conditions ==> return all
        if not conditions:
            return fetched_entities

        return [entity for entity in fetched_entities if evaluate_conditions(conditions, entity, contain)]
예제 #3
0
    def __set__(self, instance, value):
        if not self.name:
            raise RuntimeError('Name of the field is not defined!')

        if not self.write:
            raise exceptions.TogglException(
                'You are not allowed to write into \'{}\' attribute!'.format(
                    self.name))

        if self.admin_only:
            from .models import WorkspacedEntity, Workspace
            workspace = instance.workspace if isinstance(
                instance, WorkspacedEntity) else instance  # type: Workspace
            if not workspace.admin:
                raise exceptions.TogglNotAllowedException(
                    'You are trying edit field \'{}.{}\' which is admin only field, '
                    'but you are not an admin in workspace \'{}\'!'.format(
                        instance.__class__.__name__, self.name,
                        workspace.name))

        has_updated_state = self.setter(self.name, instance, value, init=False)

        if not isinstance(has_updated_state, bool):
            raise TypeError('Setter must return bool!')

        if has_updated_state is True:
            instance.__change_dict__[self.name] = value
예제 #4
0
    def all(
        self,
        order='asc',
        config=None,
        **kwargs
    ):  # type: (str, utils.Config, **typing.Any) -> typing.List[Entity]
        """
        Method that fetches all entries and deserialize them into instances of the binded entity.

        :param order: Strings 'asc' or 'desc' which specifies how the results will be sorted.
        :param config: Config instance
        :raises exceptions.TogglNotAllowedException: When retrieving a list of objects is not allowed.
        """
        if self.entity_cls is None:
            raise exceptions.TogglException(
                'The TogglSet instance is not binded to any TogglEntity!')

        if not self.can_get_list:
            raise exceptions.TogglNotAllowedException(
                'Entity {} is not allowed to fetch list from the API!'.format(
                    self.entity_cls))

        config = config or utils.Config.factory()
        url = self.build_list_url('all', config, kwargs)

        return self._fetch_all(url, order, config)
예제 #5
0
파일: base.py 프로젝트: tbrodbeck/toggl-cli
    def bind_to_class(self, cls):  # type: (Entity) -> None
        """
        Binds an Entity to the instance.

        :raises exceptions.TogglException: When instance is already bound TogglException is raised.
        """
        if self.entity_cls is not None:
            raise exceptions.TogglException('The instance is already bound to a class {}!'.format(self.entity_cls))

        self.entity_cls = cls
예제 #6
0
파일: base.py 프로젝트: tbrodbeck/toggl-cli
    def base_url(self):  # type: (TogglSet) -> str
        """
        Returns base URL which will be used for building listing or detail URLs.
        """
        if self._url:
            return self._url

        if self.entity_cls is None:
            raise exceptions.TogglException('The TogglSet instance is not binded to any TogglEntity!')

        return self.entity_cls.get_url()
예제 #7
0
    def get(self,
            id=None,
            config=None,
            **conditions
            ):  # type: (typing.Any, utils.Config, **typing.Any) -> Entity
        """
        Method for fetching detail object of the entity. it fetches the object based on specified conditions.

        If ID is used then detail URL is used to fetch object.
        If other conditions are used to specify the object, then TogglSet will fetch all objects using listing URL and
        filter out objects based on passed conditions.

        In any case result must be only one object or no object at all. Returned is the fetched object or None.

        :raises exceptions.TogglMultipleResultsException: When multiple results is returned base on the specified conditions.
        """
        if self.entity_cls is None:
            raise exceptions.TogglException(
                'The TogglSet instance is not binded to any TogglEntity!')

        config = config or utils.Config.factory()

        if id is not None:
            if self.can_get_detail:
                try:
                    fetched_entity = utils.toggl(self.build_detail_url(
                        id, config),
                                                 'get',
                                                 config=config)
                    if fetched_entity['data'] is None:
                        return None

                    return self.entity_cls.deserialize(
                        config=config, **fetched_entity['data'])
                except exceptions.TogglNotFoundException:
                    return None
            else:
                # TODO: [Q/Design] Is this desired fallback?
                # Most probably it is desired for Toggl usecase, because some Entities does not have detail view (eq. Users) and need
                # to do query for whole list and then filter out the entity based on ID.
                conditions['id'] = id

        entries = self.filter(config=config, **conditions)

        if len(entries) > 1:
            raise exceptions.TogglMultipleResultsException()

        if not entries:
            return None

        return entries[0]
예제 #8
0
파일: base.py 프로젝트: tbrodbeck/toggl-cli
    def delete(self, config=None):  # type: (utils.Config) -> None
        """
        Method for deletion of the entity through API using DELETE call.

        This will not delete the instance's object in Python, therefore calling save() method after deletion will
        result in new object created using POST call.

        :raises exceptions.TogglNotAllowedException: When action is not allowed.
        """
        if not self._can_delete:
            raise exceptions.TogglNotAllowedException('Deleting this entity is not allowed!')

        if not self.id:
            raise exceptions.TogglException('This instance has not been saved yet!')

        utils.toggl('/{}/{}'.format(self.get_url(), self.id), 'delete', config=config or self._config)
        self.id = None  # Invalidate the object, so when save() is called after delete a new object is created
예제 #9
0
    def invite(self, *emails):  # type: (str) -> None
        """
        Invites users defined by email addresses. The users does not have to have account in Toggl, in that case after
        accepting the invitation, they will go through process of creating the account in the Toggl web.

        :param emails: List of emails to invite.
        :return: None
        """
        for email in emails:
            if not validate_email(email):
                raise exceptions.TogglValidationException('Supplied email \'{}\' is not valid email!'.format(email))

        emails_json = json.dumps({'emails': emails})
        data = utils.toggl("/workspaces/{}/invite".format(self.id), "post", emails_json, config=self._config)

        if 'notifications' in data and data['notifications']:
            raise exceptions.TogglException(data['notifications'])
예제 #10
0
    def init(self, instance, value):  # type: (base.Entity, T) -> None
        """
        Method used to initialize the value in the instance.

        Used mainly for TogglEntity's __init__() and deserialize().
        """
        if self.name in instance.__dict__:
            raise exceptions.TogglException(
                'Field \'{}.{}\' is already initiated!'.format(
                    instance.__class__.__name__, self.name))

        try:
            value = self.parse(value, instance)
        except ValueError:
            raise TypeError('Expected for field \'{}\' type {} got {}'.format(
                self.name, self._field_type, type(value)))

        instance.__dict__[self.name] = value