Пример #1
0
class WorkspacedEntity(base.TogglEntity):
    """
    Abstract entity which has linked Workspace
    """

    workspace = fields.MappingField(Workspace, 'wid', write=False,
                                               default=lambda config: config.default_workspace)
    """
Пример #2
0
    def test_make_mapped_fields(self):
        mapping_field_instance = fields.MappingField(RandomEntity, 'mapped_field')

        fields_set = {
            'id': fields.IntegerField(),
            'str': fields.StringField(),
            'mapping': mapping_field_instance,
            'something_random': 'asdf'
        }

        result = base.TogglEntityMeta._make_mapped_fields(fields_set)

        # Four because there is one Field taken from RandomEntity and 'something_random' is ignored
        assert len(result) == 1
        assert result['mapped_field'] is mapping_field_instance
Пример #3
0
    def test_basic(self):
        class A:
            pass

        with pytest.raises(TypeError):
            fields.MappingField(A, 'a')
Пример #4
0
class TimeEntry(WorkspacedEntity):
    description = fields.StringField()
    """
    Description of the entry.
    """

    project = fields.MappingField(Project, 'pid')
    """
    Project to which the Time entry is linked to.
    """

    task = fields.MappingField(Task, 'tid', premium=True)
    """
    Task to which the Time entry is linked to.

    (Available only for Premium workspaces)
    """

    billable = fields.BooleanField(default=False, premium=True)
    """
    If available to be billed. (Default: False)

    (Available only for Premium workspaces)
    """

    start = TimeEntryDateTimeField(required=True)
    """
    DateTime of start of the time entry. (Required)
    """

    stop = TimeEntryDateTimeField()
    """
    DateTime of end of the time entry.
    """

    duration = fields.PropertyField(get_duration,
                                    set_duration,
                                    formatter=format_duration)
    """
    Dynamic field of entry's duration in seconds.

    If the time entry is currently running, the duration attribute contains a negative value,
    denoting the start of the time entry in seconds since epoch (Jan 1 1970). The correct duration can be
    calculated as current_time + duration, where current_time is the current time in seconds since epoch.
    """

    created_with = fields.StringField(required=True,
                                      default='TogglCLI',
                                      read=False)
    """
    Information who created the time entry.
    """

    tags = fields.SetField()
    """
    Set of tags associated with the time entry.
    """

    objects = TimeEntrySet()

    def __init__(self, start, stop=None, duration=None, **kwargs):
        if stop is None and duration is None:
            raise ValueError(
                'You can create only finished time entries through this way! '
                'You must supply either \'stop\' or \'duration\' parameter!')

        super().__init__(start=start, stop=stop, duration=duration, **kwargs)

    @classmethod
    def get_url(cls):
        return 'time_entries'

    def to_dict(self, serialized=False, changes_only=False):
        # Enforcing serialize duration when start or stop changes
        if changes_only and (self.__change_dict__.get('start')
                             or self.__change_dict__.get('stop')):
            self.__change_dict__['duration'] = None

        return super().to_dict(serialized=serialized,
                               changes_only=changes_only)

    @classmethod
    def start_and_save(
        cls,
        start=None,
        config=None,
        **kwargs
    ):  # type: (pendulum.DateTime, utils.Config, **typing.Any) -> TimeEntry
        """
        Creates a new running entry.

        If there is another running time entry in the time of calling this method, then the running entry is stopped.
        This is handled by Toggl's backend.

        :param start: The DateTime object representing start of the new TimeEntry. If None than current time is used.
        :param config:
        :param kwargs: Other parameters for creating the new TimeEntry
        :return: New running TimeEntry
        """
        config = config or utils.Config.factory()

        if start is None:
            start = pendulum.now(config.timezone)

        if 'stop' in kwargs or 'duration' in kwargs:
            raise RuntimeError(
                'With start_and_save() method you can not create finished entries!'
            )

        instance = cls.__new__(cls)
        instance.__change_dict__ = {}
        instance.is_running = True
        instance._config = config
        instance.start = start

        for key, value in kwargs.items():
            setattr(instance, key, value)

        instance.save()

        return instance

    def stop_and_save(self=None, stop=None):
        """
        Stops running the entry. It has to be running entry.

        :param stop: DateTime which should be set as stop time. If None, then current time is used.
        :return: Self
        """
        if self is None:
            # noinspection PyMethodFirstArgAssignment
            self = TimeEntry.objects.current()
            if self is None:
                raise exceptions.TogglValidationException(
                    'There is no running entry to be stoped!')

        if not self.is_running:
            raise exceptions.TogglValidationException(
                'You can\'t stop not running entry!')

        config = self._config or utils.Config.factory()

        if stop is None:
            stop = pendulum.now(config.timezone)

        self.stop = stop
        self.is_running = False
        self.save(config=config)

        return self

    def continue_and_save(self, start=None):
        """
        Creates new time entry with same description as the self entry and starts running it.

        :param start: The DateTime object representing start of the new TimeEntry. If None than current time is used.
        :return: The new TimeEntry.
        """
        if self.is_running:
            logger.warning(
                'Trying to continue time entry {} which is already running!'.
                format(self))

        config = self._config or utils.Config.factory()

        if start is None:
            start = pendulum.now(config.timezone)

        new_entry = copy(self)
        new_entry.start = start
        new_entry.stop = None
        new_entry.is_running = True

        new_entry.save(config=config)

        return new_entry

    def __str__(self):
        return '{} (#{})'.format(getattr(self, 'description', ''), self.id)
Пример #5
0
class User(WorkspacedEntity):
    """
    User entity.
    """

    _can_create = False
    _can_update = False
    _can_delete = False
    _can_get_detail = False

    api_token = fields.StringField()
    """
    API token to use for API calls.

    (Returned only for User.objects.current_user() call.)
    """

    send_timer_notifications = fields.BooleanField()

    default_workspace = fields.MappingField(Workspace,
                                            'default_wid')  # type: Workspace
    """
    Default workspace for calls that does not specify Workspace.

    (Returned only for User.objects.current_user() call.)
    """

    email = fields.EmailField()
    """
    Email address of user.
    """

    fullname = fields.StringField()
    """
    Full name of the user.
    """

    beginning_of_week = fields.ChoiceField({
        '0': 'Sunday',
        '1': 'Monday',
        '2': 'Tuesday',
        '3': 'Wednesday',
        '4': 'Thursday',
        '5': 'Friday',
        '6': 'Saturday'
    })
    """
    Defines which day is the first day of week for the user.
    """

    language = fields.StringField()
    """
    Stores language used for the user.
    """

    image_url = fields.StringField()
    """
    URL of the profile image of the user.
    """

    timezone = fields.StringField()
    """
    Timezone which is used to convert the times into.

    May differ from one used in this tool, see toggl.utils.Config().
    """

    # TODO: Add possibility to use this value in Config.time_format
    timeofday_format = fields.ChoiceField({
        'H:mm': '24-hour',
        'h:mm A': '12-hour'
    })
    """
    Format of time used to display time.
    """

    # TODO: Add possibility to use this value in Config.datetime_format
    date_format = fields.ChoiceField([
        "YYYY-MM-DD", "DD.MM.YYYY", "DD-MM-YYYY", "MM/DD/YYYY", "DD/MM/YYYY",
        "MM-DD-YYYY"
    ])
    """
    Format of date used to display dates.
    """

    objects = UserSet()

    @classmethod
    def signup(
            cls,
            email,
            password,
            timezone=None,
            created_with='TogglCLI',
            config=None):  # type: (str, str, str, str, utils.Config) -> User
        """
        Creates brand new user. After creation confirmation email is sent to him.

        :param email: Valid email of the new user.
        :param password: Password of the new user.
        :param timezone: Timezone to be associated with the user. If empty, than timezone from config is used.
        :param created_with: Name of application that created the user.
        :param config:
        :return:
        """
        if config is None:
            config = utils.Config.factory()

        if timezone is None:
            timezone = config.timezone

        if not validate_email(email):
            raise exceptions.TogglValidationException(
                'Supplied email \'{}\' is not valid email!'.format(email))

        user_json = json.dumps({
            'user': {
                'email': email,
                'password': password,
                'timezone': timezone,
                'created_with': created_with
            }
        })
        data = utils.toggl("/signups", "post", user_json, config=config)
        return cls.deserialize(config=config, **data['data'])

    def is_admin(self, workspace):
        wid = workspace.id if isinstance(workspace, Workspace) else workspace

        workspace_user = WorkspaceUser.objects.get(wid=wid, uid=self.id)
        return workspace_user.admin

    def __str__(self):
        return '{} (#{})'.format(self.fullname, self.id)
Пример #6
0
class Project(WorkspacedEntity):
    """
    Project entity
    """

    name = fields.StringField(required=True)
    """
    Name of the project. (Required)
    """

    client = fields.MappingField(Client, 'cid')
    """
    Client associated to the project.
    """

    active = fields.BooleanField(default=True)
    """
    Whether the project is archived or not. (Default: True)
    """

    is_private = fields.BooleanField(default=True)
    """
    Whether project is accessible for only project users or for all workspace users. (Default: True)
    """

    billable = fields.BooleanField(premium=True)
    """
    Whether the project is billable or not.

    (Available only for Premium workspaces)
    """

    auto_estimates = fields.BooleanField(default=False, premium=True)
    """
    Whether the estimated hours are automatically calculated based on task estimations or manually
    fixed based on the value of 'estimated_hours'.

    (Available only for Premium workspaces)
    """

    estimated_hours = fields.IntegerField(premium=True)
    """
    If auto_estimates is true then the sum of task estimations is returned, otherwise user inserted hours.

    (Available only for Premium workspaces)
    """

    color = fields.IntegerField()
    """
    Id of the color selected for the project
    """

    hex_color = fields.StringField()
    """
    Hex code of the color selected for the project
    """

    rate = fields.FloatField(premium=True)
    """
    Hourly rate of the project.

    (Available only for Premium workspaces)
    """
    def add_user(
        self,
        user,
        manager=False,
        rate=None
    ):  # type: (User, bool, typing.Optional[float]) -> ProjectUser
        """
        Add new user to a project.

        :param user: User to be added
        :param manager: Specifies if the user should have manager's rights
        :param rate: Rate for billing
        :return: ProjectUser instance.
        """
        project_user = ProjectUser(project=self,
                                   user=user,
                                   workspace=self.workspace,
                                   manager=manager,
                                   rate=rate)
        project_user.save()

        return project_user
Пример #7
0
class EvaluateConditionsEntityMapping(EvaluateConditionsEntity):
    mapping = fields.MappingField(RandomEntity, 'rid')
Пример #8
0
 class EntityWithDefaultMapping(Entity):
     default = fields.MappingField(RandomEntity, 'eid', default=a)
Пример #9
0
class EntityWithRequiredMapping(Entity):
    mapping = fields.MappingField(Entity, 'eid', required=True)
Пример #10
0
class EntityWithMapping(Entity):
    mapping = fields.MappingField(RandomEntity, 'eid')
Пример #11
0
 class ExtendedMetaTestEntityWithConflicts(MetaTestEntity):
     some_other_mapped_field = fields.MappingField(RandomEntity, 'mapping_field')
Пример #12
0
class ExtendedMetaTestEntity(MetaTestEntity):
    another_string = fields.StringField()
    another_mapped = fields.MappingField(RandomEntity, 'another_mapping_field')
Пример #13
0
class MetaTestEntity(metaclass=base.TogglEntityMeta):
    id = fields.IntegerField()
    string = fields.StringField()
    boolean = fields.BooleanField()
    mapped = fields.MappingField(RandomEntity, 'mapping_field')