示例#1
0
    def __init__(self, main_config, main_section, target):
        self.config = ServiceConfig(self.CONFIG_PREFIX, main_config, target)
        self.main_section = main_section
        self.main_config = main_config
        self.target = target

        self.desc_len = self._get_config_or_default('description_length', 35,
                                                    asint)
        self.anno_len = self._get_config_or_default('annotation_length', 45,
                                                    asint)
        self.inline_links = self._get_config_or_default(
            'inline_links', True, asbool)
        self.annotation_links = self._get_config_or_default(
            'annotation_links', not self.inline_links, asbool)
        self.annotation_comments = self._get_config_or_default(
            'annotation_comments', True, asbool)
        self.shorten = self._get_config_or_default('shorten', False, asbool)
        self.default_priority = self._get_config_or_default(
            'default_priority', 'M')

        self.add_tags = []
        for raw_option in aslist(self.config.get('add_tags', '')):
            option = raw_option.strip(' +;')
            if option:
                self.add_tags.append(option)

        log.info("Working on [%s]", self.target)
示例#2
0
    def __init__(self, main_config, main_section, target):
        self.config = ServiceConfig(self.CONFIG_PREFIX, main_config, target)
        self.main_section = main_section
        self.target = target

        self.desc_len = 35
        if main_config.has_option(self.main_section, 'description_length'):
            self.desc_len = main_config.getint(
                self.main_section, 'description_length')

        self.anno_len = 45
        if main_config.has_option(self.main_section, 'annotation_length'):
            self.anno_len = main_config.getint(
                self.main_section, 'annotation_length')

        self.inline_links = True
        if main_config.has_option(self.main_section, 'inline_links'):
            self.inline_links = asbool(main_config.get(
                self.main_section, 'inline_links'))

        self.annotation_links = not self.inline_links
        if main_config.has_option(self.main_section, 'annotation_links'):
            self.annotation_links = asbool(
                main_config.get(self.main_section, 'annotation_links')
            )

        self.annotation_comments = True
        if main_config.has_option(self.main_section, 'annotation_comments'):
            self.annotation_comments = asbool(
                main_config.get(self.main_section, 'annotation_comments')
            )

        self.shorten = False
        if main_config.has_option(self.main_section, 'shorten'):
            self.shorten = asbool(main_config.get(self.main_section, 'shorten'))

        self.add_tags = []
        if 'add_tags' in self.config:
            for raw_option in self.config.get('add_tags').split(','):
                option = raw_option.strip(' +;')
                if option:
                    self.add_tags.append(option)

        self.default_priority = 'M'
        if 'default_priority' in self.config:
            self.default_priority = self.config.get('default_priority')

        log.info("Working on [%s]", self.target)
示例#3
0
 def setUp(self):
     super(TestTrelloService, self).setUp()
     self.config = configparser.RawConfigParser()
     self.config.add_section('general')
     self.config.add_section('mytrello')
     self.config.set('mytrello', 'trello.api_key', 'XXXX')
     self.config.set('mytrello', 'trello.token', 'YYYY')
     self.service_config = ServiceConfig(
         TrelloService.CONFIG_PREFIX, self.config, 'mytrello')
     responses.add(responses.GET,
                   'https://api.trello.com/1/lists/L15T/cards/open',
                   json=[self.CARD1, self.CARD2, self.CARD3])
     responses.add(responses.GET,
                   'https://api.trello.com/1/boards/B04RD/lists/open',
                   json=[self.LIST1, self.LIST2])
     responses.add(responses.GET,
                   'https://api.trello.com/1/boards/F00',
                   json={'id': 'F00', 'name': 'Foo Board'})
     responses.add(responses.GET,
                   'https://api.trello.com/1/boards/B4R',
                   json={'id': 'B4R', 'name': 'Bar Board'})
     responses.add(responses.GET,
                   'https://api.trello.com/1/members/me/boards',
                   json=[self.BOARD])
     responses.add(responses.GET,
                   'https://api.trello.com/1/cards/C4RD/actions',
                   json=[self.COMMENT1, self.COMMENT2])
示例#4
0
 def setUp(self):
     super(TestBugzillaServiceConfig, self).setUp()
     self.config = configparser.RawConfigParser()
     self.config.add_section('general')
     self.config.add_section('mybz')
     self.service_config = ServiceConfig(BugzillaService.CONFIG_PREFIX,
                                         self.config, 'mybz')
示例#5
0
 def setUp(self):
     super(TestGitlabService, self).setUp()
     self.config = configparser.RawConfigParser()
     self.config.add_section('general')
     self.config.add_section('myservice')
     self.config.set('myservice', 'gitlab.login', 'foobar')
     self.config.set('myservice', 'gitlab.token', 'XXXXXX')
     self.service_config = ServiceConfig(
         GitlabService.CONFIG_PREFIX, self.config, 'myservice')
示例#6
0
 def setUp(self):
     self.config = RawConfigParser()
     self.config.interactive = False
     self.config.add_section('general')
     self.config.add_section('mygithub')
     self.config.set('mygithub', 'service', 'github')
     self.config.set('mygithub', 'github.login', 'tintin')
     self.config.set('mygithub', 'github.username', 'milou')
     self.config.set('mygithub', 'github.password', 't0ps3cr3t')
     self.service_config = ServiceConfig(
         GithubService.CONFIG_PREFIX, self.config, 'mygithub')
示例#7
0
def targets():
    config = load_config('general')
    for section in config.sections():
        if section in ['general', 'notifications'] or \
           section.startswith('flavor.'):
            continue
        service_name = config.get(section, 'service')
        service_class = get_service(service_name)
        for option in config.options(section):
            value = config.get(section, option)
            if not value:
                continue
            if '@oracle:use_keyring' in value:
                service_config = ServiceConfig(service_class.CONFIG_PREFIX,
                                               config, section)
                yield service_class.get_keyring_service(service_config)
示例#8
0
    def __init__(self, main_config, main_section, target):
        self.config = ServiceConfig(self.CONFIG_PREFIX, main_config, target)
        self.main_section = main_section
        self.main_config = main_config
        self.target = target

        self.desc_len = self._get_config_or_default('description_length', 35, asint);
        self.anno_len = self._get_config_or_default('annotation_length', 45, asint);
        self.inline_links = self._get_config_or_default('inline_links', True, asbool);
        self.annotation_links = self._get_config_or_default('annotation_links', not self.inline_links, asbool)
        self.annotation_comments = self._get_config_or_default('annotation_comments', True, asbool)
        self.shorten = self._get_config_or_default('shorten', False, asbool)
        self.default_priority = self._get_config_or_default('default_priority','M')

        self.add_tags = []
        for raw_option in aslist(self.config.get('add_tags', '')):
            option = raw_option.strip(' +;')
            if option:
                self.add_tags.append(option)

        log.info("Working on [%s]", self.target)
示例#9
0
class IssueService(object):
    """ Abstract base class for each service """
    # Which class should this service instantiate for holding these issues?
    ISSUE_CLASS = None
    # What prefix should we use for this service's configuration values
    CONFIG_PREFIX = ''

    def __init__(self, main_config, main_section, target):
        self.config = ServiceConfig(self.CONFIG_PREFIX, main_config, target)
        self.main_section = main_section
        self.main_config = main_config
        self.target = target

        self.desc_len = self._get_config_or_default('description_length', 35,
                                                    asint)
        self.anno_len = self._get_config_or_default('annotation_length', 45,
                                                    asint)
        self.inline_links = self._get_config_or_default(
            'inline_links', True, asbool)
        self.annotation_links = self._get_config_or_default(
            'annotation_links', not self.inline_links, asbool)
        self.annotation_comments = self._get_config_or_default(
            'annotation_comments', True, asbool)
        self.shorten = self._get_config_or_default('shorten', False, asbool)
        self.default_priority = self._get_config_or_default(
            'default_priority', 'M')

        self.add_tags = []
        for raw_option in aslist(self.config.get('add_tags', '')):
            option = raw_option.strip(' +;')
            if option:
                self.add_tags.append(option)

        log.info("Working on [%s]", self.target)

    def _get_config_or_default(self, key, default, as_type=lambda x: x):
        """Return a main config value, or default if it does not exist."""

        if self.main_config.has_option(self.main_section, key):
            return as_type(self.main_config.get(self.main_section, key))
        return default

    def get_templates(self):
        """ Get any defined templates for configuration values.

        Users can override the value of any Taskwarrior field using
        this feature on a per-key basis.  The key should be the name of
        the field to you would like to configure the value of, followed
        by '_template', and the value should be a Jinja template
        generating the field's value.  As context variables, all fields
        on the taskwarrior record are available.

        For example, to prefix the returned
        project name for tickets returned by a service with 'workproject_',
        you could add an entry reading:

            project_template = workproject_{{project}}

        Or, if you'd simply like to override the returned project name
        for all tickets incoming from a specific service, you could add
        an entry like:

            project_template = myprojectname

        The above would cause all issues to recieve a project name
        of 'myprojectname', regardless of what the project name of the
        generated issue was.

        """
        templates = {}
        for key in six.iterkeys(Task.FIELDS):
            template_key = '%s_template' % key
            if template_key in self.config:
                templates[key] = self.config.get(template_key)
        return templates

    def get_password(self, key, login='******'):
        password = self.config.get(key)
        keyring_service = self.get_keyring_service(self.config)
        if not password or password.startswith("@oracle:"):
            password = get_service_password(
                keyring_service,
                login,
                oracle=password,
                interactive=self.config.interactive)
        return password

    def get_service_metadata(self):
        return {}

    def get_issue_for_record(self, record, extra=None):
        origin = {
            'annotation_length': self.anno_len,
            'default_priority': self.default_priority,
            'description_length': self.desc_len,
            'templates': self.get_templates(),
            'target': self.target,
            'shorten': self.shorten,
            'inline_links': self.inline_links,
            'add_tags': self.add_tags,
        }
        origin.update(self.get_service_metadata())
        return self.ISSUE_CLASS(record, origin=origin, extra=extra)

    def build_annotations(self, annotations, url):
        final = []
        if self.annotation_links:
            final.append(url)
        if self.annotation_comments:
            for author, message in annotations:
                message = message.strip()
                if not message or not author:
                    continue
                message = message.replace('\n', '').replace('\r', '')
                if self.anno_len:
                    message = '%s%s' % (message[:self.anno_len], '...' if
                                        len(message) > self.anno_len else '')
                final.append('@%s - %s' % (author, message))
        return final

    @classmethod
    def validate_config(cls, service_config, target):
        """ Validate generic options for a particular target """
        if service_config.has_option(target, 'only_if_assigned'):
            die("[%s] has an 'only_if_assigned' option.  Should be "
                "'%s.only_if_assigned'." % (target, cls.CONFIG_PREFIX))
        if service_config.has_option(target, 'also_unassigned'):
            die("[%s] has an 'also_unassigned' option.  Should be "
                "'%s.also_unassigned'." % (target, cls.CONFIG_PREFIX))
        if service_config.has_option(target, 'default_priority'):
            die("[%s] has a 'default_priority' option.  Should be "
                "'%s.default_priority'." % (target, cls.CONFIG_PREFIX))
        if service_config.has_option(target, 'add_tags'):
            die("[%s] has an 'add_tags' option.  Should be "
                "'%s.add_tags'." % (target, cls.CONFIG_PREFIX))

    def include(self, issue):
        """ Return true if the issue in question should be included """
        only_if_assigned = self.config.get('only_if_assigned', None)

        if only_if_assigned:
            owner = self.get_owner(issue)
            include_owners = [only_if_assigned]

            if self.config.get('also_unassigned', None, asbool):
                include_owners.append(None)

            return owner in include_owners

        only_if_author = self.config.get('only_if_author', None)

        if only_if_author:
            return self.get_author(issue) == only_if_author

        return True

    def get_owner(self, issue):
        """ Override this for filtering on tickets """
        raise NotImplementedError()

    def get_author(self, issue):
        """ Override this for filtering on tickets """
        raise NotImplementedError()

    def issues(self):
        """ Returns a list of dicts representing issues from a remote service.

        This is the main place to begin if you are implementing a new service
        for bugwarrior.  Override this to gather issues for each service.

        Each item in the list should be a dict that looks something like this:

            {
                "description": "Some description of the issue",
                "project": "some_project",
                "priority": "H",
                "annotations": [
                    "This is an annotation",
                    "This is another annotation",
                ]
            }


        The description can be 'anything' but must be consistent and unique for
        issues you're pulling from a remote service.  You can and should use
        the ``.description(...)`` method to help format your descriptions.

        The project should be a string and may be anything you like.

        The priority should be one of "H", "M", or "L".
        """
        raise NotImplementedError()

    @staticmethod
    def get_keyring_service(service_config):
        """ Given the keyring service name for this service. """
        raise NotImplementedError
示例#10
0
class IssueService(object):
    """ Abstract base class for each service """
    # Which class should this service instantiate for holding these issues?
    ISSUE_CLASS = None
    # What prefix should we use for this service's configuration values
    CONFIG_PREFIX = ''

    def __init__(self, main_config, main_section, target):
        self.config = ServiceConfig(self.CONFIG_PREFIX, main_config, target)
        self.main_section = main_section
        self.main_config = main_config
        self.target = target

        self.desc_len = self._get_config_or_default('description_length', 35, asint);
        self.anno_len = self._get_config_or_default('annotation_length', 45, asint);
        self.inline_links = self._get_config_or_default('inline_links', True, asbool);
        self.annotation_links = self._get_config_or_default('annotation_links', not self.inline_links, asbool)
        self.annotation_comments = self._get_config_or_default('annotation_comments', True, asbool)
        self.shorten = self._get_config_or_default('shorten', False, asbool)
        self.default_priority = self._get_config_or_default('default_priority','M')

        self.add_tags = []
        for raw_option in aslist(self.config.get('add_tags', '')):
            option = raw_option.strip(' +;')
            if option:
                self.add_tags.append(option)

        log.info("Working on [%s]", self.target)


    def _get_config_or_default(self, key, default, as_type=lambda x: x):
        """Return a main config value, or default if it does not exist."""

        if self.main_config.has_option(self.main_section, key):
            return as_type(self.main_config.get(self.main_section, key))
        return default


    def get_templates(self):
        """ Get any defined templates for configuration values.

        Users can override the value of any Taskwarrior field using
        this feature on a per-key basis.  The key should be the name of
        the field to you would like to configure the value of, followed
        by '_template', and the value should be a Jinja template
        generating the field's value.  As context variables, all fields
        on the taskwarrior record are available.

        For example, to prefix the returned
        project name for tickets returned by a service with 'workproject_',
        you could add an entry reading:

            project_template = workproject_{{project}}

        Or, if you'd simply like to override the returned project name
        for all tickets incoming from a specific service, you could add
        an entry like:

            project_template = myprojectname

        The above would cause all issues to recieve a project name
        of 'myprojectname', regardless of what the project name of the
        generated issue was.

        """
        templates = {}
        for key in six.iterkeys(Task.FIELDS):
            template_key = '%s_template' % key
            if template_key in self.config:
                templates[key] = self.config.get(template_key)
        return templates

    def get_password(self, key, login='******'):
        password = self.config.get(key)
        keyring_service = self.get_keyring_service(self.config)
        if not password or password.startswith("@oracle:"):
            password = get_service_password(
                keyring_service, login, oracle=password,
                interactive=self.config.interactive)
        return password

    def get_service_metadata(self):
        return {}

    def get_issue_for_record(self, record, extra=None):
        origin = {
            'annotation_length': self.anno_len,
            'default_priority': self.default_priority,
            'description_length': self.desc_len,
            'templates': self.get_templates(),
            'target': self.target,
            'shorten': self.shorten,
            'inline_links': self.inline_links,
            'add_tags': self.add_tags,
        }
        origin.update(self.get_service_metadata())
        return self.ISSUE_CLASS(record, origin=origin, extra=extra)

    def build_annotations(self, annotations, url):
        final = []
        if self.annotation_links:
            final.append(url)
        if self.annotation_comments:
            for author, message in annotations:
                message = message.strip()
                if not message or not author:
                    continue
                message = message.replace('\n', '').replace('\r', '')
                if self.anno_len:
                    message = '%s%s' % (
                        message[:self.anno_len],
                        '...' if len(message) > self.anno_len else ''
                    )
                final.append('@%s - %s' % (author, message))
        return final

    @classmethod
    def validate_config(cls, service_config, target):
        """ Validate generic options for a particular target """
        if service_config.has_option(target, 'only_if_assigned'):
            die("[%s] has an 'only_if_assigned' option.  Should be "
                "'%s.only_if_assigned'." % (target, cls.CONFIG_PREFIX))
        if service_config.has_option(target, 'also_unassigned'):
            die("[%s] has an 'also_unassigned' option.  Should be "
                "'%s.also_unassigned'." % (target, cls.CONFIG_PREFIX))
        if service_config.has_option(target, 'default_priority'):
            die("[%s] has a 'default_priority' option.  Should be "
                "'%s.default_priority'." % (target, cls.CONFIG_PREFIX))
        if service_config.has_option(target, 'add_tags'):
            die("[%s] has an 'add_tags' option.  Should be "
                "'%s.add_tags'." % (target, cls.CONFIG_PREFIX))

    def include(self, issue):
        """ Return true if the issue in question should be included """
        only_if_assigned = self.config.get('only_if_assigned', None)

        if only_if_assigned:
            owner = self.get_owner(issue)
            include_owners = [only_if_assigned]

            if self.config.get('also_unassigned', None, asbool):
                include_owners.append(None)

            return owner in include_owners

        only_if_author = self.config.get('only_if_author', None)

        if only_if_author:
            return self.get_author(issue) == only_if_author

        return True

    def get_owner(self, issue):
        """ Override this for filtering on tickets """
        raise NotImplementedError()

    def get_author(self, issue):
        """ Override this for filtering on tickets """
        raise NotImplementedError()

    def issues(self):
        """ Returns a list of dicts representing issues from a remote service.

        This is the main place to begin if you are implementing a new service
        for bugwarrior.  Override this to gather issues for each service.

        Each item in the list should be a dict that looks something like this:

            {
                "description": "Some description of the issue",
                "project": "some_project",
                "priority": "H",
                "annotations": [
                    "This is an annotation",
                    "This is another annotation",
                ]
            }


        The description can be 'anything' but must be consistent and unique for
        issues you're pulling from a remote service.  You can and should use
        the ``.description(...)`` method to help format your descriptions.

        The project should be a string and may be anything you like.

        The priority should be one of "H", "M", or "L".
        """
        raise NotImplementedError()

    @staticmethod
    def get_keyring_service(service_config):
        """ Given the keyring service name for this service. """
        raise NotImplementedError