コード例 #1
1
ファイル: log_time.py プロジェクト: rhinosaurus/jira_goodies
def getEm():
	options = { 'server': 'https://jira.server.com' }

	jira = JIRA( options=options, basic_auth=('user','pass') )

	tickets = jira.search_issues( 'Participants = currentUser() AND updated > startOfDay() ORDER BY updated DESC' )

	for ticket in tickets:
		comments = jira.comments( ticket.key )
		for comment in comments:
			if comment.author.name == 'username':
				t = comment.created
				t = unicodedata.normalize( 'NFKD', t ).encode( 'UTF-8', 'ignore' )
				t = t.split("T")
				t = t[0]
				now = time.strftime("%Y-%m-%d")
				if t == now:
					minute_len = len( comment.body.split() )
					if minute_len <= 40:
						minutes = "3m"
					else:
						minutes = str( int( round( math.ceil( minute_len/40 )*3 ) ) ) + "m"
					jira.add_worklog( issue=str(ticket.key), timeSpent=minutes )
コード例 #2
0
ファイル: jira.py プロジェクト: ubuntudroid/bugwarrior
class JiraService(IssueService):
    # A map of jira priorities to taskwarrior priorities
    priorities = {"Trivial": "L", "Minor": "L", "Major": "M", "Critical": "H", "Blocker": "H"}

    def __init__(self, *args, **kw):
        super(JiraService, self).__init__(*args, **kw)
        self.username = self.config.get(self.target, "jira.username")
        self.url = self.config.get(self.target, "jira.base_uri")
        self.query = "assignee=" + self.username + " AND status != closed and status != resolved"
        self.jira = JIRA(
            options={"server": self.config.get(self.target, "jira.base_uri")},
            basic_auth=(self.username, self.config.get(self.target, "jira.password")),
        )

    @classmethod
    def validate_config(cls, config, target):
        for option in ["jira.username", "jira.password", "jira.base_uri"]:
            if not config.has_option(target, option):
                die("[%s] has no '%s'" % (target, option))

        IssueService.validate_config(config, target)

    def get_owner(self, issue):
        return True

    def annotations(self, issue):

        annotations = []

        comments = self.jira.comments(issue)

        if comments is []:
            pass
        else:
            for comment in comments:
                created = date.fromtimestamp(time.mktime(time.strptime(comment.created[0:10], "%Y-%m-%d")))

                annotations.append(self.format_annotation(created, comment.author.name, comment.body))

        return dict(annotations)

    def issues(self):
        cases = self.jira.search_issues(self.query, maxResults=-1)

        log.debug(" Found {0} total.", len(cases))

        return [
            dict(
                description=self.description(
                    title=case.fields.summary,
                    url=self.url + "/browse/" + case.key,
                    number=case.key.rsplit("-", 1)[1],
                    cls="issue",
                ),
                project=case.key.rsplit("-", 1)[0],
                priority=self.priorities.get(get_priority(case.fields.priority), self.default_priority),
                **self.annotations(case.key)
            )
            for case in cases
        ]
コード例 #3
0
def getEm( streamlined ):
	options = { 'server': 'https://jira.server.com' }

	jira = JIRA( options=options, basic_auth=('username','password') )

	tickets = jira.search_issues( '("Dev Lead"=currentUser() OR assignee = currentUser()) AND ( "Last Resolution Date" > startOfWeek() OR status != Closed)' )

	for ticket in tickets:
		comments = jira.comments( ticket.key )
		for comment in comments:
			if comment.author.name == 'automation':
				answ = 'y' # for streamlining the process
				if streamlined == False:
					answ = raw_input( str(comment.body)[:80] + ' - ' + '\033[93m' + 'DELETE y/n? ' + '\033[0m' )
				if answ == 'y':
					print 'Deleting comment...'
					comment.delete()
					print 'Deleted.'
				else:
					print 'Ok - I will ask later on.'
コード例 #4
0
class JiraService(IssueService):
    # A map of jira priorities to taskwarrior priorities
    priorities = {
        'Trivial': 'L',
        'Minor': 'L',
        'Major': 'M',
        'Critical': 'H',
        'Blocker': 'H',
    }

    def __init__(self, *args, **kw):
        super(JiraService, self).__init__(*args, **kw)
        self.username = self.config.get(self.target, 'jira.username')
        self.url = self.config.get(self.target, 'jira.base_uri')
        password = self.config.get(self.target, 'jira.password')
        if not password or password.startswith("@oracle:"):
            service = "jira://%s@%s" % (self.username, self.url)
            password = get_service_password(service, self.username,
                                            oracle=password,
                                            interactive=self.config.interactive)

        default_query = 'assignee=' + self.username + \
            ' AND status != closed and status != resolved'
        self.query = self.config.get(self.target, 'jira.query', default_query)
        self.project_prefix = self.config.get(
            self.target, 'jira.project_prefix', '')
        self.jira = JIRA(
            options={
                'server': self.config.get(self.target, 'jira.base_uri'),
                'rest_api_version': 'latest',
            },
            basic_auth=(self.username, password)
        )

    @classmethod
    def validate_config(cls, config, target):
        for option in ['jira.username', 'jira.password', 'jira.base_uri']:
            if not config.has_option(target, option):
                die("[%s] has no '%s'" % (target, option))

        IssueService.validate_config(config, target)

    def get_owner(self, issue):
        return True

    def annotations(self,  issue):

        annotations = []

        comments = self.jira.comments(issue)

        if comments is []:
            pass
        else:
            for comment in comments:
                created = date.fromtimestamp(time.mktime(time.strptime(
                    comment.created[0:10], '%Y-%m-%d')))

                annotations.append(self.format_annotation(
                    created, comment.author.name, comment.body))

        return dict(annotations)

    def __convert_for_jira4(self, issue):
        print(issue.key)

        class IssueWrapper:
            pass
        #print(self.jira.issue(issue.key).fields.summary.value)
        #print(self.jira.issue(issue.key).fields.summary)
        new_issue = self.jira.issue(issue.key)
        result = IssueWrapper()
        fields = IssueWrapper()
        fields.__dict__ = {
            'summary': new_issue.fields.summary.value,
            'priority': new_issue.fields.priority.name,
        }
        result.__dict__ = {
            'key': issue.key,
            'fields': fields,
        }
        return result

    def __issue(self, case, jira_version):
        result = dict(
            description=self.description(
                title=case.fields.summary,
                url=self.url + '/browse/' + case.key,
                number=case.key.rsplit('-', 1)[1],
                cls="issue"),
            project=self.project_prefix + case.key.rsplit('-', 1)[0],
            priority=self.priorities.get(
                get_priority(case.fields.priority),
                self.default_priority,
            )
        )
        if jira_version != 4:
            result.update(self.annotations(case.key))
        return result
コード例 #5
0
class JiraService(IssueService):
    ISSUE_CLASS = JiraIssue
    CONFIG_PREFIX = 'jira'

    def __init__(self, *args, **kw):
        super(JiraService, self).__init__(*args, **kw)
        self.username = self.config_get('username')
        self.url = self.config_get('base_uri')
        password = self.config_get('password')
        if not password or password.startswith("@oracle:"):
            password = get_service_password(
                self.get_keyring_service(self.config, self.target),
                self.username,
                oracle=password,
                interactive=self.config.interactive)

        default_query = 'assignee=' + self.username + \
            ' AND resolution is null'
        self.query = self.config_get_default('query', default_query)
        self.jira = JIRA(options={
            'server':
            self.config_get('base_uri'),
            'rest_api_version':
            'latest',
            'verify':
            self.config_get_default('verify_ssl', default=None,
                                    to_type=asbool),
        },
                         basic_auth=(self.username, password))
        self.import_labels_as_tags = self.config_get_default(
            'import_labels_as_tags', default=False, to_type=asbool)
        self.label_template = self.config_get_default('label_template',
                                                      default='{{label}}',
                                                      to_type=six.text_type)

    @classmethod
    def get_keyring_service(cls, config, section):
        username = config.get(section, cls._get_key('username'))
        base_uri = config.get(section, cls._get_key('base_uri'))
        return "jira://%s@%s" % (username, base_uri)

    def get_service_metadata(self):
        return {
            'url': self.url,
            'import_labels_as_tags': self.import_labels_as_tags,
            'label_template': self.label_template,
        }

    @classmethod
    def validate_config(cls, config, target):
        for option in ('jira.username', 'jira.password', 'jira.base_uri'):
            if not config.has_option(target, option):
                die("[%s] has no '%s'" % (target, option))

        IssueService.validate_config(config, target)

    def annotations(self, issue, issue_obj):
        comments = self.jira.comments(issue.key)

        if not comments:
            return []
        else:
            return self.build_annotations(
                ((comment.author.name, comment.body) for comment in comments),
                issue_obj.get_processed_url(issue_obj.get_url()))

    def issues(self):
        cases = self.jira.search_issues(self.query, maxResults=-1)

        jira_version = 5
        if self.config.has_option(self.target, 'jira.version'):
            jira_version = self.config.getint(self.target, 'jira.version')

        for case in cases:
            issue = self.get_issue_for_record(case.raw)
            extra = {
                'jira_version': jira_version,
            }
            if jira_version > 4:
                extra.update({'annotations': self.annotations(case, issue)})
            issue.update_extra(extra)
            yield issue
コード例 #6
0
ファイル: jira.py プロジェクト: qwertos/bugwarrior
class JiraService(IssueService):
    ISSUE_CLASS = JiraIssue
    CONFIG_PREFIX = "jira"

    def __init__(self, *args, **kw):
        super(JiraService, self).__init__(*args, **kw)
        self.username = self.config_get("username")
        self.url = self.config_get("base_uri")
        password = self.config_get("password")
        if not password or password.startswith("@oracle:"):
            password = get_service_password(
                self.get_keyring_service(self.config, self.target),
                self.username,
                oracle=password,
                interactive=self.config.interactive,
            )

        default_query = "assignee=" + self.username + " AND resolution is null"
        self.query = self.config_get_default("query", default_query)
        self.jira = JIRA(
            options={
                "server": self.config_get("base_uri"),
                "rest_api_version": "latest",
                "verify": self.config_get_default("verify_ssl", default=None, to_type=asbool),
            },
            basic_auth=(self.username, password),
        )
        self.import_labels_as_tags = self.config_get_default("import_labels_as_tags", default=False, to_type=asbool)
        self.label_template = self.config_get_default("label_template", default="{{label}}", to_type=six.text_type)

    @classmethod
    def get_keyring_service(cls, config, section):
        username = config.get(section, cls._get_key("username"))
        base_uri = config.get(section, cls._get_key("base_uri"))
        return "jira://%s@%s" % (username, base_uri)

    def get_service_metadata(self):
        return {
            "url": self.url,
            "import_labels_as_tags": self.import_labels_as_tags,
            "label_template": self.label_template,
        }

    @classmethod
    def validate_config(cls, config, target):
        for option in ("jira.username", "jira.password", "jira.base_uri"):
            if not config.has_option(target, option):
                die("[%s] has no '%s'" % (target, option))

        IssueService.validate_config(config, target)

    def annotations(self, issue, issue_obj):
        comments = self.jira.comments(issue.key)

        if not comments:
            return []
        else:
            return self.build_annotations(
                ((comment.author.name, comment.body) for comment in comments),
                issue_obj.get_processed_url(issue_obj.get_url()),
            )

    def issues(self):
        cases = self.jira.search_issues(self.query, maxResults=-1)

        jira_version = 5
        if self.config.has_option(self.target, "jira.version"):
            jira_version = self.config.getint(self.target, "jira.version")

        for case in cases:
            issue = self.get_issue_for_record(case.raw)
            extra = {"jira_version": jira_version}
            if jira_version > 4:
                extra.update({"annotations": self.annotations(case, issue)})
            issue.update_extra(extra)
            yield issue
コード例 #7
0
ファイル: jira.py プロジェクト: b-boogaard/bugwarrior
class JiraService(IssueService):
    ISSUE_CLASS = JiraIssue
    CONFIG_PREFIX = 'jira'

    def __init__(self, *args, **kw):
        super(JiraService, self).__init__(*args, **kw)
        self.username = self.config_get('username')
        self.url = self.config_get('base_uri')
        password = self.config_get('password')
        if not password or password.startswith("@oracle:"):
            password = get_service_password(
                self.get_keyring_service(self.config, self.target),
                self.username, oracle=password,
                interactive=self.config.interactive
            )

        default_query = 'assignee=' + self.username + \
            ' AND status != closed and status != resolved'
        self.query = self.config_get_default('query', default_query)
        self.jira = JIRA(
            options={
                'server': self.config_get('base_uri'),
                'rest_api_version': 'latest',
                'verify': self.config_get_default('verify_ssl', default=None, to_type=asbool),
            },
            basic_auth=(self.username, password)
        )
        self.import_labels_as_tags = self.config_get_default(
            'import_labels_as_tags', default=False, to_type=asbool
        )
        self.label_template = self.config_get_default(
            'label_template', default='{{label}}', to_type=six.text_type
        )

    @classmethod
    def get_keyring_service(cls, config, section):
        username = config.get(section, cls._get_key('username'))
        base_uri = config.get(section, cls._get_key('base_uri'))
        return "jira://%s@%s" % (username, base_uri)

    def get_service_metadata(self):
        return {
            'url': self.url,
            'import_labels_as_tags': self.import_labels_as_tags,
            'label_template': self.label_template,
        }

    @classmethod
    def validate_config(cls, config, target):
        for option in ('jira.username', 'jira.password', 'jira.base_uri'):
            if not config.has_option(target, option):
                die("[%s] has no '%s'" % (target, option))

        IssueService.validate_config(config, target)

    def annotations(self, issue, issue_obj):
        comments = self.jira.comments(issue.key)

        if not comments:
            return []
        else:
            return self.build_annotations(
                ((
                    comment.author.name,
                    comment.body
                ) for comment in comments),
                issue_obj.get_processed_url(issue_obj.get_url())
            )

    def issues(self):
        cases = self.jira.search_issues(self.query, maxResults=-1)

        jira_version = 5
        if self.config.has_option(self.target, 'jira.version'):
            jira_version = self.config.getint(self.target, 'jira.version')

        for case in cases:
            issue = self.get_issue_for_record(case.raw)
            extra = {
                'jira_version': jira_version,
            }
            if jira_version > 4:
                extra.update({
                    'annotations': self.annotations(case, issue)
                })
            issue.update_extra(extra)
            yield issue
コード例 #8
0
ファイル: jiratest.py プロジェクト: mthierry/scripts
    msg.attach( MIMEText(emailtext, 'html') )
    smtp = smtplib.SMTP(emailsmtp)
    smtp.sendmail(emailfrom, emailto, msg.as_string())
    smtp.close()
    print "email sent to " + emailto

# Summary of my last 10 assigned issues
emailbody = "<b>Found these JIRAs:</b><BR>"
emailbodynocommentsthisweek = "<BR><hr><b>Found these JIRAs without comments this week:</b><BR>"
emailbodynocomments = "<BR><hr><b>Found these JIRAs without comments:</b><BR>"
for issue in jira.search_issues('assignee = currentUser() and project=ACD order by updated desc', maxResults=10):
    print issue
    print "Issue summary: " + issue.fields.summary
    numcomments = 0
    numcommentsthisweek = 0
    for comment in jira.comments(issue):
        numcomments += 1
        author = str(comment.author)
        author = author.strip(' ,\t\n\r')
        if author == myname:
            print "Comment by: " + myname
            commentdate = parse(str(comment.updated))
            print "comment updated on: " + str(commentdate)
            if commentdate >= lastweek:
                if numcomments == 1:
                    emailbody += "<BR>" + str(issue) + ": " + issue.fields.summary + "<BR>"
                numcommentsthisweek += 1
                print "Ok, comment after: " + str(lastweek)
                print comment.body
                emailbody += unicode(comment.body).encode('ascii', 'ignore') + "<BR>"
            else:
コード例 #9
0
ファイル: rt2jira.py プロジェクト: ssavva05/rt2jira
                        ') to (' + jira_issue.key + ')')
                    jira_issue.fields.labels.append(new_issue_label)
                    jira_issue.update(
                        fields={"labels": jira_issue.fields.labels})

                # Once the JIRA ticket is created, should the security level be modified?
                if config.getboolean('jira', 'modify_security_level'):
                    original_security_level = config.get(
                        'jira', 'original_security_level')
                    original_security_level_mentioned = False
                    if original_security_level != "None":
                        if original_security_level in jira_issue.fields.description:
                            original_security_level_mentioned = True
                        else:
                            # Obtain all current comments on the JIRA ticket.
                            jira_comments = jira.comments(jira_issue)
                            for existing_comment in jira_comments:
                                if original_security_level in existing_comment.body:
                                    original_security_level_mentioned = True
                                    break

                    if original_security_level_mentioned:
                        logger.debug(
                            'Original security level for JIRA ticket (' +
                            jira_issue.key + ') mentioned')
                        syslog.syslog(
                            syslog.LOG_DEBUG,
                            'Original security level for JIRA ticket (' +
                            jira_issue.key + ') mentioned')

                    # If checks passed, then modify the security level.
コード例 #10
0
ファイル: client.py プロジェクト: rawfalafel/docs-tools
class JeerahClient(object):
    def __init__(self, conf):
        self.conf = conf
        self.credentials = CredentialsConfig(self.conf.site.credentials)
        self.c = None
        self.issues_created = []
        self.abort_on_error = True
        self.results_format = 'list'
        self.versions_cache = {}

    def connect(self):
        if self.c is None:
            self.c = JIRA(options={'server': self.conf.site.url},
                          basic_auth=(self.credentials.jira.username,
                                      self.credentials.jira.password))
            logger.debug('created jira connection')
        else:
            logger.debug('jira connection exists')

        logger.debug('configured user: '******'actual user: '******'{0}' to version cache".format(ver.name))

            if project not in self.versions_cache:
                self.versions_cache[project] = {}

            self.versions_cache[project][ver.name] = ver.id

    def create_issue(self,
                     title,
                     text,
                     assignee,
                     project,
                     reporter=None,
                     tags=None,
                     version=None,
                     uid=None):
        issue = {
            'project': {
                'key': project
            },
            'issuetype': {
                'name': 'Task'
            },
            'summary': title,
            'description': text,
            'assignee': {
                'name': assignee
            }
        }

        if reporter is not None:
            issue['reporter'] = {'name': reporter}
        if tags is not None:
            issue['labels'] = [tags]
        if version is not None:
            if project not in self.versions_cache:
                logger.debug(
                    "updating version cache to include {0} versions".format(
                        project))
                self.update_version_cache(project)

            if version not in self.versions_cache[project]:
                logger.error("version {0} doesn't exist in {1} project".format(
                    version, project))
            else:
                issue['fixVersions'] = [{
                    'id':
                    self.versions_cache[project][version]
                }]
                logger.debug('adding version to issue: {0}'.format(
                    issue['fixVersions']))

        new_issue = self.c.create_issue(fields=issue)

        logger.debug('created new issue {0}'.format(new_issue.key))
        self.issues_created.append({
            'key': new_issue.key,
            'uid': uid,
            'title': title
        })

    def query(self, query_string):
        logger.info('running query for: {0}'.format(query_string))
        try:
            query_results = self.c.search_issues(jql_str=query_string,
                                                 maxResults=200)
        except Exception as e:
            logger.warning(query_string)
            logger.error(e)
            if self.abort_on_error is True:
                raise SystemExit(e)

        if self.results_format == 'dict':
            return {issue.key: issue for issue in query_results}
        elif self.results_format == 'list':
            return [issue for issue in query_results]

    def versions(self, project, released=False, archived=False):
        return [
            v for v in self.c.project_versions(project)
            if v.released is released and v.archived is archived
        ]

    def release_version(self, version):
        if not isinstance(version, Version):
            logger.error('{0} is not a jira version.'.format(version))
        else:
            logger.info('releasing version {0}'.format(version.name))
            version.update(released=True)

    def archive_version(self, version):
        if not isinstance(version, Version):
            logger.error('{0} is not a jira version.'.format(version))
        else:
            logger.info('archiving version {0}'.format(version.name))
            version.update(archived=True)

    def create_version(self, project, name, description='', release=None):
        if release is None:
            release = str(datetime.date.today() + datetime.timedelta(days=14))
        elif release is False:
            release = None
        elif isinstance(release, datetime.date):
            release = str(release)

        self.c.create_version(name=name,
                              project=project,
                              description=description,
                              releaseDate=release)

        logger.debug('created version {0} in project {0}'.format(
            name, project))
コード例 #11
0
class Housekeeping():
    """
    This class is the container for all automated Jira functions performed
    by the Housekeeping agent.

    """
    def __init__(self):
        # class variables
        self.ac_label =  u'auto-close-24-hours'
        # open JIRA API Connection
        self.jira = JIRA(options=secrets.options,
                            basic_auth=secrets.housekeeping_auth)

        # commands to run
        self.content_acquisition_auto_qc()
        self.requeue_free_indexing()
        self.auto_assign()
        self.remind_reporter_to_close()
        self.close_resolved()
        self.clear_auto_close_label()
        self.resolved_issue_audit()
        self.handle_audited_tickets()


    def content_acquisition_auto_qc(self):
        """
        Takes INDEXREP issues that have been Merged for 30+ minutes and
        transitions them to Quality Control. It then adds a comment that
        tags the reporter to inform them that the issue is ready for review.

        """
        # get CA tickets merged 30+ minute ago
        issues = self.get_issues("auto_qc")

        for issue in issues:
            reporter = issue.fields.reporter.key
            message = '[~%s], this issue is ready for QC.' % reporter
            """
            771 is the transition ID spedific to this step for this project.
            Anything more generic will need to parse the transitions list.
            """
            tran_id = self.get_transition_id(issue,"qc")
            self.jira.transition_issue(issue.key,tran_id)
            self.jira.add_comment(issue.key, message)

    def handle_audited_tickets(self):
        """
        Handles audit tickets that are failed. Closed tickets are ignored. Failed
        tickets trigger the creation of a new ticket in the same project as the
        original ticket.

        Inputs: None
        Returns: None

        """
        issues = self.jira.search_issues(   # get all the ADT issues
            'project=ADT and status="Failed Audit"')

        # For each failed issue, generate a new work ticket then close this one
        for issue in issues:
            #BUID
            adt_buid=issue.fields.customfield_10502
            #WCID
            adt_wcid=issue.fields.customfield_10501
            #oldBUID
            adt_old_buid=issue.fields.customfield_13100
            #oldWCID
            adt_old_wcid=issue.fields.customfield_13101
            #Indexing Type
            adt_indexing_type=issue.fields.customfield_10500
            #comments
            adt_comments = []
            for comment in self.jira.comments(issue):
                node = {
                    'body':self.jira.comment(issue,comment).body,
                    'author': self.jira.comment(issue,comment).author.key
                }
                adt_comments.append(node)

            link_list = [issue.key,] # first linked ticket should be this audit ticket
            for link in issue.fields.issuelinks: # grab the rest of links
                try:
                    link_list.append(link.outwardIssue.key)
                except AttributeError:
                    pass

            # capture original ticket and project. If it can't be found, default to INDEXREP as the project
            try:
                original_ticket = issue.fields.summary.split("[")[1].split("]")[0]
                original_project = original_ticket.split("-")[0]
            except IndexError:
                original_ticket = ""
                original_project="INDEXREP"

            # build the new summary by parsing the audit summary
            indexrep_summary = issue.fields.summary #build the summary
            indexrep_summary = indexrep_summary.replace("compliance audit - ","")
            indexrep_summary = indexrep_summary.split("[")[0]
            indexrep_summary = ' %s - Failed Audit' % (indexrep_summary)

            # Build the issue description
            message = 'This issue failed audit. Please review %s and make any \
                necessary corrections.' % original_ticket

            # Construct the watcher list and de-dupe it
            watcher_list = [issue.fields.assignee.key,]
            for w in self.jira.watchers(issue).watchers:
                watcher_list.append(w.key)
            watcher_list = set(watcher_list)

            # get the reporter (reporter is preserved from audit to issue)
            reporter = issue.fields.reporter.key

            # Generate the new issue, then close the audit ticket.
            new_issue = self.make_new_issue(original_project,"",reporter,
                                                indexrep_summary,message,
                                                watcher_list,link_list,adt_buid,
                                                adt_wcid,adt_old_buid,adt_old_wcid,
                                                adt_indexing_type,adt_comments)
            close_me = self.close_issue(issue.key)


    def resolved_issue_audit(self):
        """
        TAKES issues that have been resolved from specified projectsfor a set
        interval and creates a new ticket in AUDIT, closes the INDEXREP ticket,
        and then assigns it to the audit user specified in the self.qa_auditor role.

        Inputs: None
        Returns:    Error message or Nothing

        """
        # get all the issues from projects in the audit list
        issues = self.get_issues("audit_list")

        #build a list of all users in the MS & MD groups
        member_svc = self.get_group_members("member-services")
        member_dev = self.get_group_members("membership-development")
        member_aud = self.get_group_members("issue audits")
        member_all = []
        for user in member_svc:
            member_all.append(user) #only need the user names, not the meta data
        for user in member_dev:
            member_all.append(user)
        for user in member_aud:
            member_all.append(user)
        member_all = set(member_all) #de-dupe


        # cycle through them and create a new ADT ticket for each
        for issue in issues:
            # capture issue fields
            ind_buid=issue.fields.customfield_10502 #BUID
            ind_wcid=issue.fields.customfield_10501 #WCID
            ind_old_buid=issue.fields.customfield_13100 #oldBUID
            ind_old_wcid=issue.fields.customfield_13101 #oldWCID
            ind_indexing_type=issue.fields.customfield_10500 #Indexing Type
            reporter = issue.fields.reporter.key #Reporter

            link_list = [issue.key,]
            for link in issue.fields.issuelinks: # grab the rest of links
                try:
                    link_list.append(link.outwardIssue.key)
                except AttributeError:
                    pass
            # build the new ticket summary based on the issue being audited
            # [ISSUE=123] is used to preserve the original issue key. Replace any brackets with ()
            # to prevent read errors later.
            adt_summary = issue.fields.summary.replace("[","(").replace("]",")")
            adt_summary = 'compliance audit - %s [%s]' % (adt_summary,issue.key)

            # check reporter to see if special consideration is needed
            # if reporter is not MS or MD, or it's a new member, assign to audit lead.
            new_member_setup = self.check_for_text(issue,
                                                   settings.member_setup_strs)
            assigned_audit_tasks_query = self.get_issues("assigned_audits",True)
            if reporter not in member_all or new_member_setup:
                qa_auditor = self.user_with_fewest_issues('issue audits lead',
                                                      assigned_audit_tasks_query,
                                                      [reporter])
            else:
                # get the users who can be assigned audit tickets, then select the
                # one with fewest assigned tickets
                qa_auditor = self.user_with_fewest_issues('issue audits',
                                                          assigned_audit_tasks_query,
                                                          [reporter])

            # build the description
            message = '[~%s], issue %s is ready to audit.' % (qa_auditor, issue.key)

            #build the watcher list, including original reporter and assignee of the audited ticket
            watcher_list = []
            for w in self.jira.watchers(issue).watchers:
                watcher_list.append(w.key)

            try:
                original_assignee = issue.fields.assignee.key
            except AttributeError:
                original_assignee=""

            # make the audit ticket
            new_issue = self.make_new_issue("ADT",qa_auditor,reporter,
                adt_summary,message,watcher_list,link_list,ind_buid,
                ind_wcid,ind_old_buid,ind_old_wcid,ind_indexing_type)

            # close the INDEXREP ticket
            close_me = self.close_issue(issue.key)

            # add comment to indexrep ticket
            link_back_comment = "This issue has been closed. The audit ticket is %s" % new_issue
            self.jira.add_comment(issue.key, link_back_comment)


    def requeue_free_indexing(self):
        """
        Takes a list of old FCA tickets, and clears the assignee fields in order to
        allow it to be reassigned.

        Inputs: None
        Returns: Nothing

        """
        # get issues that are stale and need reassigned
        issues = self.get_issues("stale_free")

        # itirate issues and set assignee to empty. This will allow
        # auto assignment to set the assignee.
        for issue in issues:
            #check for wait in label
            wait_label = self.label_contains(issue,"wait")
            # if no wait label, clear the assignee so it can be re-autoassigned
            if (not wait_label):
                issue.update(assignee={'name':""})


    def make_new_issue(self,project,issue_assignee,issue_reporter,summary,
                                      description="",watchers=[],links=[],
                                      buid="",wcid="",old_buid="",old_wcid="",
                                      indexing_type="",comments=[],
                                      issuetype="Task"):
        """
        Creates a new issue with the given parameters.
        Inputs:
        *REQUIRED*
            :project:   the jira project key in which to create the issue
            :issue_assignee:    user name who the issue will be assigned to
            :issue_reporter:    user name of the issue report
            :summary:   string value of the issue summary field
            *OPTIONAL*
            :description: Issue description. Defaults to empty string
            :watchers: list of user names to add as issue watchers
            :link:  list of issue keys to link to the issue as "Related To"
            :issuetype: the type of issue to create. Defaults to type.
            :buid: business unit - custom field 10502
            :wcid: wrapping company id - custom field 10501
            :old_buid: old business unit - custom field 13100
            :old_wcid: old wrapping company id - custom field 13101
            :indexing_type: the indexing type - custom field 10500
            :comments: list dictionaries of comments and authors to auto add.
        Returns: Jira Issue Object

        """
        issue_dict = {
            'project':{'key':project},
            'summary': summary,
            'issuetype': {'name':issuetype},
            'description':description,
            }
        new_issue = self.jira.create_issue(fields=issue_dict)

        # assign the audit tick to auditor
        new_issue.update(assignee={'name':issue_assignee})
        new_issue.update(reporter={'name':issue_reporter})

        # add watchers to audit ticket (reporter, assignee, wacthers from indexrep ticket)
        for watcher in watchers:
            try:
                self.jira.add_watcher(new_issue,watcher)
            except:
                pass

        # link the audit ticket back to indexrep ticket
        for link in links:
            self.jira.create_issue_link('Relates',new_issue,link)

        # add custom field values if set
        if buid:
            new_issue.update(fields={'customfield_10502':buid})
        if wcid:
            new_issue.update(fields={'customfield_10501':wcid})
        if indexing_type:
            new_issue.update(fields={'customfield_10500':{'value':indexing_type.value}})
        if old_buid:
            new_issue.update(fields={'customfield_13100':old_buid})
        if old_wcid:
            new_issue.update(fields={'customfield_13101':old_wcid})

        # add comments
        quoted_comments = ""
        for comment in comments:
            quoted_comments = "%s[~%s] Said:{quote}%s{quote}\\\ \\\ " % (quoted_comments,comment['author'],comment['body'])

        if quoted_comments:
            quoted_comments = "Comments from the parent issue:\\\ %s" % quoted_comments
            self.jira.add_comment(new_issue,quoted_comments)

        return new_issue

    # method to transistion audit ticket
    def get_group_members(self, group_name):
        """
        Returns the members of a group as a list
        """
        group = self.jira.groups(query=group_name)[0]
        members = self.jira.group_members(group)
        return members

    def auto_assign(self,project="INDEXREP"):
        """
        Looks up new INDEXREP issues with an empty assignee and non-agent
        reporter and assigns them to the user in the content-acquisition user
        group with the fewest assigned contect-acquistion tickets.

        """
        # get member INDEXREP issues that need to auto assigned
        mem_issues = self.get_issues("member_auto_assign")
        # get free indexing requests
        free_issues = self.get_issues("free_auto_assign")
        # get unassigned member engagement issues
        mer_issues = self.get_issues("mer_auto_assign")
        # get unassigned sales engineering issues
        se_issues = self.get_issues("se_auto_assign")

        # get non-resolved assigned Member issues
        member_assigned_issues_query = self.get_issues("member_assigned_issues",True)
        # get non-resolved assigned Free Indexing issues
        free_assigned_issues_query = self.get_issues("free_assigned_issues",True)
        # get non-resolved member enagement issues
        mer_assigned_issues_query = self.get_issues("mer_assigned_issues",True)
        # get non-resolved sales engineering issues
        se_assigned_issues_query = self.get_issues("se_assigned_issues",True)

        def _assign(issue,username):
            """
            Private method for assigning an issue.
            Inputs:
            issue: issue to assign
            username: person to assign the issue

            """
            reporter = issue.fields.reporter.key
            self.jira.assign_issue(issue=issue,assignee=username)

            message = ("[~%s], this issue has been automically assigned "
                "to [~%s].") % (reporter,username)
            self.jira.add_comment(issue.key, message)

        auto_assign_dicts = [
            {
                "issue_list": mem_issues,
                "assigned_list": member_assigned_issues_query,
                "assignee_group": "content-acquisition",
            },
            {
                "issue_list": free_issues,
                "assigned_list": free_assigned_issues_query,
                "assignee_group": "content-acquisition-free",
            },
            {
                "issue_list": mer_issues,
                "assigned_list": mer_assigned_issues_query,
                "assignee_group": "mer-assignees",
                "watch_list":"mer-auto-watch",
            },
            {
                "issue_list": se_issues,
                "assigned_list": se_assigned_issues_query,
                "assignee_group": "se-assignees",
            }]

        for auto_assign_dict in auto_assign_dicts:
            for issue in auto_assign_dict["issue_list"]:
                username = self.user_with_fewest_issues(auto_assign_dict["assignee_group"],
                                                        auto_assign_dict["assigned_list"])


                if (auto_assign_dict["issue_list"]==mem_issues or
                    auto_assign_dict["issue_list"]==free_issues):
                    # check if the indexing type is already set. If so, do nothing.
                    if issue.fields.customfield_10500 == None:
                        # default to member indexing for issues in mem_issues
                        if auto_assign_dict["issue_list"]==mem_issues:
                            issue.update({"customfield_10500":{"id":"10103"}})

                        elif auto_assign_dict["issue_list"]==free_issues:
                            free_index_mem = self.get_group_members("free-index-default")
                            # set the indexing type to free if the reporter is in the list
                            # of users who default to free
                            if issue.fields.reporter.key in free_index_mem:
                                issue.update({"customfield_10500":{"id":"10100"}}) #free
                            else: #default is member otherwise
                                issue.update({"customfield_10500":{"id":"10103"}})

                # if the dict object has a watch list item, add default watchers
                if "watch_list" in auto_assign_dict:
                    watchers = self.get_group_members(auto_assign_dict["watch_list"])
                    self.toggle_watchers("add",issue,watchers)

                _assign(issue,username)


    def remind_reporter_to_close(self):
        """
        Comments on all non-closed resolved issues that are 13 days without a
        change. Notifies the reporter it will be closed in 24 hours and adds a
        label to the issue that is used as a lookup key by the close method.

        """
        issues = self.get_issues("remind_close_issues")
        for issue in issues:
            reporter = issue.fields.reporter.key
            message = (
                "[~%s], this issue has been resolved for 13 days. It will be "
                "closed automatically in 24 hours.") % reporter
            watch_list = self.toggle_watchers("remove",issue)
            self.jira.add_comment(issue.key,message)
            issue.fields.labels.append(self.ac_label)
            issue.update(fields={"labels": issue.fields.labels})
            self.toggle_watchers("add",issue, watch_list)

    def close_resolved(self):
        """
        Looks up all issues labeled for auto-closing that have not been updated
        in 24 hours and closes them. Ignores INDEXREP so as to not interfere
        with the auditing process.

        """
        issues = self.get_issues("auto_close_issues")
        for issue in issues:
            reporter = issue.fields.reporter.key
            message = (
                "[~%s], this issue has closed automatically.") % reporter
            close_me = self.close_issue(issue)
            self.jira.add_comment(issue.key,message)

    def get_transition_id(self,issue,key):
        """
        Finds the transition id for an issue given a specific search string.
        Inputs:
            key: search string
            issue: jira issue
        Returns: transition id or False

        """
        trans = self.jira.transitions(issue)
        tran_id = False
        for tran in trans:
            tran_name = tran['name'].lower()
            if key in tran_name:
                tran_id = tran['id']
        return tran_id

    def close_issue(self, issue):
        """
        Closes the issue passed to it with a resolution of fixed.
        Inputs: Issue: the issue object to close
        Returns: True|False

        """
        trans = self.jira.transitions(issue)
        success_flag = False
        tran_id = self.get_transition_id(issue,"close")
        if not tran_id:
            tran_id = self.get_transition_id(issue,"complete")

        if tran_id:
            try:
                self.jira.transition_issue(issue,tran_id,
                                           {'resolution':{'id':'1'}})
            #some close transitions don't have a resolution screen
            except: #open ended, but the JIRAError exception is broken.
                self.jira.transition_issue(issue,tran_id)
            success_flag = True
        return success_flag

    def clear_auto_close_label(self):
        """
        Clears the auto-close label from issues that have been re-opened
        since the auto-close reminder was posted.

        """
        issues = self.jira.search_issues(
            'status in ("Quality Control", Reopened, Merged, open) \
            AND labels in (auto-close-24-hours)')
        for issue in issues:
            label_list =  issue.fields.labels
            watch_list = self.toggle_watchers("remove",issue)
            label_list.remove(self.ac_label)
            issue.update(fields={"labels": label_list})
            self.toggle_watchers("add",issue, watch_list)


    def toggle_watchers(self,action,issue,watch_list=[]):
        """
        Internal method that either adds or removes the watchers of an issue. If
        it removes them,it returns a list of users that were removed. If it
        adds, it returns an updated list of watchers.

        Inputs:
        :action: String "add"|"remove". The action to take
        :issue:  Issue whose watchers list is being modified
        :watch_list: list of users. Optional for remove. Required for add.

        Returns:
        :issue_watcher: List of users who are or were watching the issue.

        """
        if action=="remove":
            issue_watchers = self.jira.watchers(issue).watchers
            for issue_watcher in issue_watchers:
                # watch list can be inconsensent when returned by the jira api
                # same issue in the add loop
                try:
                    self.jira.remove_watcher(issue,issue_watcher.name)
                except AttributeError:
                    self.jira.add_watcher(issue,old_watcher)
        else:
            for old_watcher in watch_list:
                try:
                    self.jira.add_watcher(issue,old_watcher.name)
                except AttributeError:
                    self.jira.add_watcher(issue,old_watcher)
            issue_watchers = self.jira.watchers(issue).watchers
        return issue_watchers

    def label_contains(self,issue,search_string):
        """
        Internal method that searches the labels of an issue for a given string
        value. It allows filtering that is roughly "labels ~ 'string'", which
        is not supported by JQL.

        Inputs:
        :issue: Jira issue object that is being checked
        :search_string: the string value being checked for

        Returns:
        True|False  True if search_string exists in any label.

        """
        return any(search_string in label for label in issue.fields.labels)


    def check_for_text(self,issue,text_list):
        """
        Internal method that searches the summary and description of an issue for
        a given list of strings. Match is non-case sensative, and converts
        everything to lower case before checking for a match.

        Inputs:
        :issue: Jira issue object that is being checked
        :text_list: strings to checked for

        Returns:
        True|False  True if any of the values in text_list exist.

        """
        string_exists = False
        if issue.fields.summary:
            summary = issue.fields.summary.lower()
        else:
            summary = ""

        if issue.fields.description:
            description = issue.fields.description.lower()
        else:
            description = ""

        for text in text_list:
            text = text.lower()
            if text in summary or text in description:
                string_exists = True

        return string_exists


    def user_with_fewest_issues(self,group,query,blacklist=[]):
        """
        Given a query, return the username of the use with the fewest assigned
        issues in the result set.

        Inputs:
        group: the group of users for which to count issues.
        query: the issues to lookup. Should be a JQL string.
        blacklist: (optional) list of inelligible users

        """
        members = self.get_group_members(group)
        issues = self.jira.search_issues(query,maxResults=1000)

        member_count = {}

        for member in members:
            member_count[member]=0

        # perform the count anew for each ticket
        for issue in issues:
            if issue.fields.assignee:
                assignee = issue.fields.assignee.key
            else:
                assignee = None
            if assignee in members and not self.label_contains(issue,"wait"):
                member_count[assignee] = member_count[assignee]+1

        #sort the list so that the user with the lowest count is first
        member_count_sorted = sorted(member_count.items(),
            key=operator.itemgetter(1))

        # prevent assignment to a user in the blacklist, so long as there are
        # at least 2 available users
        while (str(member_count_sorted[0][0]) in blacklist and
            len(member_count_sorted)>1):
            del member_count_sorted[0]

        # return the username of the user
        return str(member_count_sorted[0][0])


    def get_issues(self,filter_key,return_jql=False):
        """
        Returns issues found using a jira filter.

        Inputs:
            :filter_key:    the dict key for the filter in settings
            :return_jql:    flag to return JQL instead on issues

        Returns:
            :issues:    Jira Issues object (default) or JQL string

        """
        filter_id = secrets.jira_filters[filter_key]
        jql_query = self.jira.filter(filter_id).jql
        if return_jql:
            # some functionality needs the JQL instead of an issue list
            # notably the method self.user_with_fewest_issues
            return jql_query
        else:
            issues = self.jira.search_issues(jql_query)
            return issues
コード例 #12
0
ファイル: jiratools.py プロジェクト: pombredanne/jiratools
class Housekeeping():
    """
    This class is the container for all automated Jira functions performed
    by the Housekeeping agent.
    
    """ 
    def __init__(self):
        # class variables
        self.ac_label =  u'auto-close-24-hours'
        self.audit_delay = '-72h'
        self.audit_projects = "INDEXREP" #comma delimited project keys
        # open JIRA API Connection
        self.jira = JIRA(options=secrets.options, 
                            basic_auth=secrets.housekeeping_auth) 
    
        # commands to run
        self.content_acquisition_auto_qc()
        self.auto_assign()
        self.remind_reporter_to_close()
        self.close_resolved() 
        self.clear_auto_close_label()
        self.resolved_issue_audit()
        self.handle_audited_tickets()

    def content_acquisition_auto_qc(self):
        """
        Takes INDEXREP issues that have been Merged for 30+ minutes and 
        transitions them to Quality Control. It then adds a comment that
        tags the reporter to inform them that the issue is ready for review.

        """
        issues = self.jira.search_issues(
            'project=INDEXREP and status=Merged and updated<="-30m"')
        
        for issue in issues:
            reporter = issue.fields.reporter.key
            message = '[~%s], this issue is ready for QC.' % reporter
            """ 
            771 is the transition ID spedific to this step for this project.
            Anything more generic will need to parse the transitions list.
            """
            self.jira.transition_issue(issue.key,'771')
            self.jira.add_comment(issue.key, message)
    
    def handle_audited_tickets(self):
        """
        Handles audit tickets that are failed. Closed tickets are ignored. Failed 
        tickets trigger the creation of a new ticket in the same project as the 
        original ticket.
        
        Inputs: None
        Returns: None
        
        """
        issues = self.jira.search_issues(   # get all the ADT issues
            'project=ADT and status="Failed Audit"')
        
        # For each failed issue, generate a new work ticket then close this one
        for issue in issues:
            #BUID
            adt_buid=issue.fields.customfield_10502
            #WCID
            adt_wcid=issue.fields.customfield_10501
            #Indexing Type
            adt_indexing_type=issue.fields.customfield_10500
            #comments
            adt_comments = []
            for comment in self.jira.comments(issue):
                node = {
                    'body':self.jira.comment(issue,comment).body,
                    'author': self.jira.comment(issue,comment).author.key
                }
                adt_comments.append(node)
                        
            link_list = [issue.key,] # first linked ticket should be this audit ticket
            for link in issue.fields.issuelinks: # grab the rest of links
                try:
                    link_list.append(link.outwardIssue.key)
                except AttributeError:
                    pass
            
            # capture orignal tick and project
            original_ticket = issue.fields.summary.split("[")[1].split("]")[0]
            original_project = original_ticket.split("-")[0]
            
            # build the new summary by parsing the audit summary
            indexrep_summary = issue.fields.summary #build the summary
            indexrep_summary = indexrep_summary.replace("compliance audit - ","")
            indexrep_summary = indexrep_summary.split("[")[0]
            indexrep_summary = ' %s - Failed Audit' % (indexrep_summary)
            
            # Build the issue description
            message = 'This issue failed audit. Please review %s and make any \
                necessary corrections.' % original_ticket

            # Construct the watcher list and de-dupe it
            watcher_list = [issue.fields.assignee.key,]
            for w in self.jira.watchers(issue).watchers:
                watcher_list.append(w.key)
            watcher_list = set(watcher_list)
            
            # get the reporter (reporter is preserved from audit to issue)
            reporter = issue.fields.reporter.key
            
            # Generate the new issue, then close the audit ticket.    
            new_issue = self.make_new_issue(original_project,"EMPTY",reporter,
                                                                    indexrep_summary,message,
                                                                    watcher_list,link_list,adt_buid,
                                                                    adt_wcid,adt_indexing_type,adt_comments)            
            close_me = self.close_issue(issue.key)
                        
    
    def resolved_issue_audit(self,delay="",projects=""):
        """
        TAKES issues that have been resolved from specified projectsfor a set 
        interval and creates a new ticket in AUDIT, closes the INDEXREP ticket, 
        and then assigns it to the audit user specified in the self.qa_auditor role.
        
        Inputs:
        :delay:      how long an issue should be resoved before being picked up
                        by this script. Defaults to class level variable
        :projects:  which projects are subject to auditing. Defaults to class level
                        variable
        Returns:    Error message or Nothing
        
        """
        delay = self.audit_delay if not delay else delay
        projects = self.audit_projects if not projects else projects
        # get all the issues from projects in the audit list
        issue_query = 'project in (%s) and status=Resolved and resolutiondate \
            <="%s"' % (projects,delay)
        issues = self.jira.search_issues(issue_query) 
        
        # get the users who can be assigned audit tickets. This should be just one person
        qa_members = self.get_group_members("issue audits")
        if len(qa_members)==1:
            qa_auditor=qa_members.keys()[0]
        else:
            # for now, throw an error. Later, assign to user with fewer ADT tickets
            # this will also mean turning the code in auto_assign into a method (DRY)
            return "Error: There is more than one possible auditor"
        
        # cycle through them and create a new ADT ticket for each 
        for issue in issues:
            #BUID
            ind_buid=issue.fields.customfield_10502
            #WCID
            ind_wcid=issue.fields.customfield_10501
            #Indexing Type
            ind_indexing_type=issue.fields.customfield_10500
            link_list = [issue.key,]
            for link in issue.fields.issuelinks: # grab the rest of links
                try:
                    link_list.append(link.outwardIssue.key)
                except AttributeError:
                    pass
            # build the new ticket summary based on the issue being audited
            # [ISSUE=123] is used to preserve the original issue key. Replace any brackets with () 
            # to prevent read errors later.
            adt_summary = issue.fields.summary.replace("[","(").replace("]",")")
            adt_summary = 'compliance audit - %s [%s]' % (adt_summary,issue.key)
            # build the description
            message = '[~%s], issue %s is ready to audit.' % (qa_auditor, issue.key)
            
            #build the watcher list, including original reporter and assignee of the audited ticket
            watcher_list = []
            for w in self.jira.watchers(issue).watchers:
                watcher_list.append(w.key)
            reporter = issue.fields.reporter.key
            try:
                original_assignee = issue.fields.assignee.key
            except AttributeError:
                original_assignee="EMPTY"         
           
            # make the audit ticket
            new_issue = self.make_new_issue("ADT",qa_auditor,reporter,
                adt_summary,message,watcher_list,link_list,ind_buid,
                ind_wcid,ind_indexing_type)
           
            # close the INDEXREP ticket
            close_me = self.close_issue(issue.key)
            
            # add comment to indexrep ticket
            link_back_comment = "This issue has been closed. The audit ticket is %s" % new_issue
            self.jira.add_comment(issue.key, link_back_comment)
            
        
    def make_new_issue(self,project,issue_assignee,issue_reporter,summary,
                                      description="",watchers=[],links=[],buid="",wcid="",
                                      indexing_type="",comments=[],issuetype="Task"):
        """
        Creates a new issue with the given parameters.
        Inputs:
        *REQUIRED*
            :project:   the jira project key in which to create the issue
            :issue_assignee:    user name who the issue will be assigned to
            :issue_reporter:    user name of the issue report
            :summary:   string value of the issue summary field
            *OPTIONAL*
            :description: Issue description. Defaults to empty string
            :watchers: list of user names to add as issue watchers
            :link:  list of issue keys to link to the issue as "Related To"
            :issuetype: the type of issue to create. Defaults to type.
            :buid: business unit - custom field 10502
            :wcid: wrapping company id - custom field 10501
            :indexing_type: the indexing type - custom field 10500
            :comments: list dictionaries of comments and authors to auto add.
        Returns: Jira Issue Object
        
        """
        issue_dict = {
            'project':{'key':project},
            'summary': summary,
            'issuetype': {'name':issuetype},
            'description':description,        
            }
        new_issue = self.jira.create_issue(fields=issue_dict)
        
        # assign the audit tick to auditor
        new_issue.update(assignee={'name':issue_assignee})
        new_issue.update(reporter={'name':issue_reporter})
        
        # add watchers to audit ticket (reporter, assignee, wacthers from indexrep ticket)
        for watcher in watchers:
            self.jira.add_watcher(new_issue,watcher)
        
        # link the audit ticket back to indexrep ticket
        for link in links:
            self.jira.create_issue_link('Relates',new_issue,link)
        
        # add custom field values if set
        if buid:
            new_issue.update(fields={'customfield_10502':buid})
        if wcid:
            new_issue.update(fields={'customfield_10501':wcid})
        if indexing_type:
            new_issue.update(fields={'customfield_10500':{'value':indexing_type.value}})
        
        # add comments
        quoted_comments = ""
        for comment in comments:
            quoted_comments = "%s[~%s] Said:{quote}%s{quote}\\\ \\\ " % (quoted_comments,comment['author'],comment['body'])
            
        if quoted_comments:
            quoted_comments = "Comments from the parent issue:\\\ %s" % quoted_comments
            self.jira.add_comment(new_issue,quoted_comments)
            
        return new_issue
            
    # method to transistion audit ticket    
    def get_group_members(self, group_name):
        """
        Returns the members of a 
        """
        group = self.jira.groups(
            query=group_name
            )['groups'][0]['name']
        members = self.jira.group_members(group)
        return members
    
    def auto_assign(self):
        """
        Looks up new INDEXREP issues with an empty assignee and non-agent
        reporter and assigns them to the user in the content-acquisition user 
        group with the fewest assigned contect-acquistion tickets. 

        """        
        # filter 20702 returns issues that need to auto assigned
        jql_query = self.jira.filter("20702").jql        
        issues = self.jira.search_issues(jql_query)
        
        #filter 21200 returns non-resolved assigned issues
        assigned_issues_query = self.jira.filter("21200").jql
        
        # cycle through each issue and assign it to the user in 
        # content acquisition with the fewest assigned tickets
        for issue in issues:
            username = self.user_with_fewest_issues('content-acquisition', 
                                                    assigned_issues_query)
            
            reporter = issue.fields.reporter.key
            watch_list = self.toggle_watchers("remove",issue)
            self.jira.assign_issue(issue=issue,assignee=username)
            message = ("[~%s], this issue has been automically assigned "
                "to [~%s].") % (reporter,username)
            self.jira.add_comment(issue.key, message)
            self.toggle_watchers("add",issue,watch_list)            
            
        
    def remind_reporter_to_close(self):
        """
        Comments on all non-closed resolved issues that are 13 days without a
        change. Notifies the reporter it will be closed in 24 hours and adds a
        label to the issue that is used as a lookup key by the close method.

        """
        issues = self.jira.search_issues(
            'resolution != EMPTY AND \
            status not in (closed, "Quality Control", Reopened, Merged, open) \
            AND updated <= -13d and project not in (INDEXREP)')
        for issue in issues:
            reporter = issue.fields.reporter.key
            message = (
                "[~%s], this issue has been resolved for 13 days. It will be "
                "closed automatically in 24 hours.") % reporter
            watch_list = self.toggle_watchers("remove",issue)
            self.jira.add_comment(issue.key,message)
            issue.fields.labels.append(self.ac_label)
            issue.update(fields={"labels": issue.fields.labels})
            self.toggle_watchers("add",issue, watch_list)

    def close_resolved(self):
        """
        Looks up all issues labeled for auto-closing that have not been updated
        in 24 hours and closes them. Ignores INDEXREP so as to not interfere
        with the auditing process.

        """
        issues = self.jira.search_issues(
            'resolution != EMPTY AND \
            status not in (closed, "Quality Control", Reopened, Merged, open, \
            passed,staged) AND project not in (INDEXREP) \
            AND updated <= -24h \
            AND labels in (auto-close-24-hours)')
        for issue in issues:
            reporter = issue.fields.reporter.key
            message = (
                "[~%s], this issue has closed automatically.") % reporter
            close_me = self.close_issue(issue)
            self.jira.add_comment(issue.key,message)
            
    def close_issue(self, issue):
        """
        Closes the issue passed to it with a resolution of fixed.
        Inputs: Issue: the issue object to close
        Returns: True|False
        
        """
        trans = self.jira.transitions(issue)
        success_flag = False
        for tran in trans:
            tran_name = tran['name'].lower()
            if 'close' in tran_name or 'complete' in tran_name:
                try:
                    self.jira.transition_issue(issue,tran['id'],{'resolution':{'id':'1'}})
                #some close transitions don't have a resolution screen
                except: #open ended, but the JIRAError exception is broken.
                    self.jira.transition_issue(issue,tran['id'])
                success_flag = True
        return success_flag
                
    def clear_auto_close_label(self):
        """
        Clears the auto-close label from issues that have been re-opened
        since the auto-close reminder was posted.

        """
        issues = self.jira.search_issues(
            'status in ("Quality Control", Reopened, Merged, open) \
            AND labels in (auto-close-24-hours)')
        for issue in issues:
            label_list =  issue.fields.labels
            watch_list = self.toggle_watchers("remove",issue)
            label_list.remove(self.ac_label)
            issue.update(fields={"labels": label_list})
            self.toggle_watchers("add",issue, watch_list)


    def toggle_watchers(self,action,issue,watch_list=[]):
        """
        Internal method that either adds or removes the watchers of an issue. If
        it removes them,it returns a list of users that were removed. If it 
        adds, it returns an updated list of watchers.
        
        Inputs:
        :action: String "add"|"remove". The action to take
        :issue:  Issue whose watchers list is being modified
        :watch_list: list of users. Optional for remove. Required for add.
        
        Returns:
        :issue_watcher: List of users who are or were watching the issue.
        
        """
        if action=="remove":
            issue_watchers = self.jira.watchers(issue).watchers
            for issue_watcher in issue_watchers:
                self.jira.remove_watcher(issue,issue_watcher.name)
        else:
            for old_watcher in watch_list:
                self.jira.add_watcher(issue,old_watcher.name)
            issue_watchers = self.jira.watchers(issue).watchers
        return issue_watchers

    def label_contains(self,issue,search_string):        
        """
        Internal method that searches the labels of an issue for a given string
        value. It allows filtering that is roughly "labels ~ 'string'", which
        is not supported by JQL.

        Inputs:
        :issue: Jira issue object that is being checked
        :search_string: the string value being checked for

        Returns:
        True|False  True if search_string exists in any label.

        """
        return any(search_string in label for label in issue.fields.labels)    
    
    def user_with_fewest_issues(self,group,query):
        """
        Given a query, return the username of the use with the fewest assigned
        issues in the result set.
        
        Inputs:
        group: the group of users for which to count issues.
        query: the issues to lookup. Should be a JQL string.
        
        """
        members = self.get_group_members(group)
        
        issues = self.jira.search_issues(query)
        
        member_count = {}

        for member in members:
            member_count[member]=0

        # perform the count anew for each ticket
        for issue in issues:
            if issue.fields.assignee:
                assignee = issue.fields.assignee.key
            else:
                assignee = None
            if assignee in members and not self.label_contains(issue,"wait"):
                member_count[assignee] = member_count[assignee]+1
                
        #sort the list so that the user with the lowest count is first
        member_count_sorted = sorted(member_count.items(), 
            key=operator.itemgetter(1))
        # return the username of the user 
        return str(member_count_sorted[0][0]) 
コード例 #13
0
ファイル: jiratools.py プロジェクト: pombredanne/jiratools
class Housekeeping():
    """
    This class is the container for all automated Jira functions performed
    by the Housekeeping agent.
    
    """
    def __init__(self):
        # class variables
        self.ac_label = u'auto-close-24-hours'
        self.audit_delay = '-72h'
        self.audit_projects = "INDEXREP"  #comma delimited project keys
        # open JIRA API Connection
        self.jira = JIRA(options=secrets.options,
                         basic_auth=secrets.housekeeping_auth)

        # commands to run
        self.content_acquisition_auto_qc()
        self.auto_assign()
        self.remind_reporter_to_close()
        self.close_resolved()
        self.clear_auto_close_label()
        self.resolved_issue_audit()
        self.handle_audited_tickets()

    def content_acquisition_auto_qc(self):
        """
        Takes INDEXREP issues that have been Merged for 30+ minutes and 
        transitions them to Quality Control. It then adds a comment that
        tags the reporter to inform them that the issue is ready for review.

        """
        issues = self.jira.search_issues(
            'project=INDEXREP and status=Merged and updated<="-30m"')

        for issue in issues:
            reporter = issue.fields.reporter.key
            message = '[~%s], this issue is ready for QC.' % reporter
            """ 
            771 is the transition ID spedific to this step for this project.
            Anything more generic will need to parse the transitions list.
            """
            self.jira.transition_issue(issue.key, '771')
            self.jira.add_comment(issue.key, message)

    def handle_audited_tickets(self):
        """
        Handles audit tickets that are failed. Closed tickets are ignored. Failed 
        tickets trigger the creation of a new ticket in the same project as the 
        original ticket.
        
        Inputs: None
        Returns: None
        
        """
        issues = self.jira.search_issues(  # get all the ADT issues
            'project=ADT and status="Failed Audit"')

        # For each failed issue, generate a new work ticket then close this one
        for issue in issues:
            #BUID
            adt_buid = issue.fields.customfield_10502
            #WCID
            adt_wcid = issue.fields.customfield_10501
            #Indexing Type
            adt_indexing_type = issue.fields.customfield_10500
            #comments
            adt_comments = []
            for comment in self.jira.comments(issue):
                node = {
                    'body': self.jira.comment(issue, comment).body,
                    'author': self.jira.comment(issue, comment).author.key
                }
                adt_comments.append(node)

            link_list = [
                issue.key,
            ]  # first linked ticket should be this audit ticket
            for link in issue.fields.issuelinks:  # grab the rest of links
                try:
                    link_list.append(link.outwardIssue.key)
                except AttributeError:
                    pass

            # capture orignal tick and project
            original_ticket = issue.fields.summary.split("[")[1].split("]")[0]
            original_project = original_ticket.split("-")[0]

            # build the new summary by parsing the audit summary
            indexrep_summary = issue.fields.summary  #build the summary
            indexrep_summary = indexrep_summary.replace(
                "compliance audit - ", "")
            indexrep_summary = indexrep_summary.split("[")[0]
            indexrep_summary = ' %s - Failed Audit' % (indexrep_summary)

            # Build the issue description
            message = 'This issue failed audit. Please review %s and make any \
                necessary corrections.' % original_ticket

            # Construct the watcher list and de-dupe it
            watcher_list = [
                issue.fields.assignee.key,
            ]
            for w in self.jira.watchers(issue).watchers:
                watcher_list.append(w.key)
            watcher_list = set(watcher_list)

            # get the reporter (reporter is preserved from audit to issue)
            reporter = issue.fields.reporter.key

            # Generate the new issue, then close the audit ticket.
            new_issue = self.make_new_issue(original_project, "EMPTY",
                                            reporter, indexrep_summary,
                                            message, watcher_list, link_list,
                                            adt_buid, adt_wcid,
                                            adt_indexing_type, adt_comments)
            close_me = self.close_issue(issue.key)

    def resolved_issue_audit(self, delay="", projects=""):
        """
        TAKES issues that have been resolved from specified projectsfor a set 
        interval and creates a new ticket in AUDIT, closes the INDEXREP ticket, 
        and then assigns it to the audit user specified in the self.qa_auditor role.
        
        Inputs:
        :delay:      how long an issue should be resoved before being picked up
                        by this script. Defaults to class level variable
        :projects:  which projects are subject to auditing. Defaults to class level
                        variable
        Returns:    Error message or Nothing
        
        """
        delay = self.audit_delay if not delay else delay
        projects = self.audit_projects if not projects else projects
        # get all the issues from projects in the audit list
        issue_query = 'project in (%s) and status=Resolved and resolutiondate \
            <="%s"' % (projects, delay)
        issues = self.jira.search_issues(issue_query)

        # get the users who can be assigned audit tickets. This should be just one person
        qa_members = self.get_group_members("issue audits")
        if len(qa_members) == 1:
            qa_auditor = qa_members.keys()[0]
        else:
            # for now, throw an error. Later, assign to user with fewer ADT tickets
            # this will also mean turning the code in auto_assign into a method (DRY)
            return "Error: There is more than one possible auditor"

        # cycle through them and create a new ADT ticket for each
        for issue in issues:
            #BUID
            ind_buid = issue.fields.customfield_10502
            #WCID
            ind_wcid = issue.fields.customfield_10501
            #Indexing Type
            ind_indexing_type = issue.fields.customfield_10500
            link_list = [
                issue.key,
            ]
            for link in issue.fields.issuelinks:  # grab the rest of links
                try:
                    link_list.append(link.outwardIssue.key)
                except AttributeError:
                    pass
            # build the new ticket summary based on the issue being audited
            # [ISSUE=123] is used to preserve the original issue key. Replace any brackets with ()
            # to prevent read errors later.
            adt_summary = issue.fields.summary.replace("[",
                                                       "(").replace("]", ")")
            adt_summary = 'compliance audit - %s [%s]' % (adt_summary,
                                                          issue.key)
            # build the description
            message = '[~%s], issue %s is ready to audit.' % (qa_auditor,
                                                              issue.key)

            #build the watcher list, including original reporter and assignee of the audited ticket
            watcher_list = []
            for w in self.jira.watchers(issue).watchers:
                watcher_list.append(w.key)
            reporter = issue.fields.reporter.key
            try:
                original_assignee = issue.fields.assignee.key
            except AttributeError:
                original_assignee = "EMPTY"

            # make the audit ticket
            new_issue = self.make_new_issue("ADT", qa_auditor, reporter,
                                            adt_summary, message, watcher_list,
                                            link_list, ind_buid, ind_wcid,
                                            ind_indexing_type)

            # close the INDEXREP ticket
            close_me = self.close_issue(issue.key)

            # add comment to indexrep ticket
            link_back_comment = "This issue has been closed. The audit ticket is %s" % new_issue
            self.jira.add_comment(issue.key, link_back_comment)

    def make_new_issue(self,
                       project,
                       issue_assignee,
                       issue_reporter,
                       summary,
                       description="",
                       watchers=[],
                       links=[],
                       buid="",
                       wcid="",
                       indexing_type="",
                       comments=[],
                       issuetype="Task"):
        """
        Creates a new issue with the given parameters.
        Inputs:
        *REQUIRED*
            :project:   the jira project key in which to create the issue
            :issue_assignee:    user name who the issue will be assigned to
            :issue_reporter:    user name of the issue report
            :summary:   string value of the issue summary field
            *OPTIONAL*
            :description: Issue description. Defaults to empty string
            :watchers: list of user names to add as issue watchers
            :link:  list of issue keys to link to the issue as "Related To"
            :issuetype: the type of issue to create. Defaults to type.
            :buid: business unit - custom field 10502
            :wcid: wrapping company id - custom field 10501
            :indexing_type: the indexing type - custom field 10500
            :comments: list dictionaries of comments and authors to auto add.
        Returns: Jira Issue Object
        
        """
        issue_dict = {
            'project': {
                'key': project
            },
            'summary': summary,
            'issuetype': {
                'name': issuetype
            },
            'description': description,
        }
        new_issue = self.jira.create_issue(fields=issue_dict)

        # assign the audit tick to auditor
        new_issue.update(assignee={'name': issue_assignee})
        new_issue.update(reporter={'name': issue_reporter})

        # add watchers to audit ticket (reporter, assignee, wacthers from indexrep ticket)
        for watcher in watchers:
            self.jira.add_watcher(new_issue, watcher)

        # link the audit ticket back to indexrep ticket
        for link in links:
            self.jira.create_issue_link('Relates', new_issue, link)

        # add custom field values if set
        if buid:
            new_issue.update(fields={'customfield_10502': buid})
        if wcid:
            new_issue.update(fields={'customfield_10501': wcid})
        if indexing_type:
            new_issue.update(
                fields={'customfield_10500': {
                    'value': indexing_type.value
                }})

        # add comments
        quoted_comments = ""
        for comment in comments:
            quoted_comments = "%s[~%s] Said:{quote}%s{quote}\\\ \\\ " % (
                quoted_comments, comment['author'], comment['body'])

        if quoted_comments:
            quoted_comments = "Comments from the parent issue:\\\ %s" % quoted_comments
            self.jira.add_comment(new_issue, quoted_comments)

        return new_issue

    # method to transistion audit ticket
    def get_group_members(self, group_name):
        """
        Returns the members of a 
        """
        group = self.jira.groups(query=group_name)['groups'][0]['name']
        members = self.jira.group_members(group)
        return members

    def auto_assign(self):
        """
        Looks up new INDEXREP issues with an empty assignee and non-agent
        reporter and assigns them to the user in the content-acquisition user 
        group with the fewest assigned contect-acquistion tickets. 

        """
        # filter 20702 returns issues that need to auto assigned
        jql_query = self.jira.filter("20702").jql
        issues = self.jira.search_issues(jql_query)

        #filter 21200 returns non-resolved assigned issues
        assigned_issues_query = self.jira.filter("21200").jql

        # cycle through each issue and assign it to the user in
        # content acquisition with the fewest assigned tickets
        for issue in issues:
            username = self.user_with_fewest_issues('content-acquisition',
                                                    assigned_issues_query)

            reporter = issue.fields.reporter.key
            watch_list = self.toggle_watchers("remove", issue)
            self.jira.assign_issue(issue=issue, assignee=username)
            message = ("[~%s], this issue has been automically assigned "
                       "to [~%s].") % (reporter, username)
            self.jira.add_comment(issue.key, message)
            self.toggle_watchers("add", issue, watch_list)

    def remind_reporter_to_close(self):
        """
        Comments on all non-closed resolved issues that are 13 days without a
        change. Notifies the reporter it will be closed in 24 hours and adds a
        label to the issue that is used as a lookup key by the close method.

        """
        issues = self.jira.search_issues('resolution != EMPTY AND \
            status not in (closed, "Quality Control", Reopened, Merged, open) \
            AND updated <= -13d and project not in (INDEXREP)')
        for issue in issues:
            reporter = issue.fields.reporter.key
            message = (
                "[~%s], this issue has been resolved for 13 days. It will be "
                "closed automatically in 24 hours.") % reporter
            watch_list = self.toggle_watchers("remove", issue)
            self.jira.add_comment(issue.key, message)
            issue.fields.labels.append(self.ac_label)
            issue.update(fields={"labels": issue.fields.labels})
            self.toggle_watchers("add", issue, watch_list)

    def close_resolved(self):
        """
        Looks up all issues labeled for auto-closing that have not been updated
        in 24 hours and closes them. Ignores INDEXREP so as to not interfere
        with the auditing process.

        """
        issues = self.jira.search_issues('resolution != EMPTY AND \
            status not in (closed, "Quality Control", Reopened, Merged, open, \
            passed,staged) AND project not in (INDEXREP) \
            AND updated <= -24h \
            AND labels in (auto-close-24-hours)')
        for issue in issues:
            reporter = issue.fields.reporter.key
            message = (
                "[~%s], this issue has closed automatically.") % reporter
            close_me = self.close_issue(issue)
            self.jira.add_comment(issue.key, message)

    def close_issue(self, issue):
        """
        Closes the issue passed to it with a resolution of fixed.
        Inputs: Issue: the issue object to close
        Returns: True|False
        
        """
        trans = self.jira.transitions(issue)
        success_flag = False
        for tran in trans:
            tran_name = tran['name'].lower()
            if 'close' in tran_name or 'complete' in tran_name:
                try:
                    self.jira.transition_issue(issue, tran['id'],
                                               {'resolution': {
                                                   'id': '1'
                                               }})
                #some close transitions don't have a resolution screen
                except:  #open ended, but the JIRAError exception is broken.
                    self.jira.transition_issue(issue, tran['id'])
                success_flag = True
        return success_flag

    def clear_auto_close_label(self):
        """
        Clears the auto-close label from issues that have been re-opened
        since the auto-close reminder was posted.

        """
        issues = self.jira.search_issues(
            'status in ("Quality Control", Reopened, Merged, open) \
            AND labels in (auto-close-24-hours)')
        for issue in issues:
            label_list = issue.fields.labels
            watch_list = self.toggle_watchers("remove", issue)
            label_list.remove(self.ac_label)
            issue.update(fields={"labels": label_list})
            self.toggle_watchers("add", issue, watch_list)

    def toggle_watchers(self, action, issue, watch_list=[]):
        """
        Internal method that either adds or removes the watchers of an issue. If
        it removes them,it returns a list of users that were removed. If it 
        adds, it returns an updated list of watchers.
        
        Inputs:
        :action: String "add"|"remove". The action to take
        :issue:  Issue whose watchers list is being modified
        :watch_list: list of users. Optional for remove. Required for add.
        
        Returns:
        :issue_watcher: List of users who are or were watching the issue.
        
        """
        if action == "remove":
            issue_watchers = self.jira.watchers(issue).watchers
            for issue_watcher in issue_watchers:
                self.jira.remove_watcher(issue, issue_watcher.name)
        else:
            for old_watcher in watch_list:
                self.jira.add_watcher(issue, old_watcher.name)
            issue_watchers = self.jira.watchers(issue).watchers
        return issue_watchers

    def label_contains(self, issue, search_string):
        """
        Internal method that searches the labels of an issue for a given string
        value. It allows filtering that is roughly "labels ~ 'string'", which
        is not supported by JQL.

        Inputs:
        :issue: Jira issue object that is being checked
        :search_string: the string value being checked for

        Returns:
        True|False  True if search_string exists in any label.

        """
        return any(search_string in label for label in issue.fields.labels)

    def user_with_fewest_issues(self, group, query):
        """
        Given a query, return the username of the use with the fewest assigned
        issues in the result set.
        
        Inputs:
        group: the group of users for which to count issues.
        query: the issues to lookup. Should be a JQL string.
        
        """
        members = self.get_group_members(group)

        issues = self.jira.search_issues(query)

        member_count = {}

        for member in members:
            member_count[member] = 0

        # perform the count anew for each ticket
        for issue in issues:
            if issue.fields.assignee:
                assignee = issue.fields.assignee.key
            else:
                assignee = None
            if assignee in members and not self.label_contains(issue, "wait"):
                member_count[assignee] = member_count[assignee] + 1

        #sort the list so that the user with the lowest count is first
        member_count_sorted = sorted(member_count.items(),
                                     key=operator.itemgetter(1))
        # return the username of the user
        return str(member_count_sorted[0][0])
コード例 #14
0
ファイル: rt2jira.py プロジェクト: alcamie101/rt2jira
                if new_issue_label != "None":
                    logger.debug('Adding label (' + new_issue_label + ') to (' + jira_issue.key + ')')
                    syslog.syslog(syslog.LOG_DEBUG, 'Adding label (' + new_issue_label + ') to (' + jira_issue.key + ')')
                    jira_issue.fields.labels.append(new_issue_label)
                    jira_issue.update(fields={"labels": jira_issue.fields.labels})

                # Once the JIRA ticket is created, should the security level be modified?
                if config.getboolean('jira', 'modify_security_level'):
                    original_security_level = config.get('jira', 'original_security_level')
                    original_security_level_mentioned = False
                    if original_security_level != "None":
                        if original_security_level in jira_issue.fields.description:
                            original_security_level_mentioned = True
                        else: 
                            # Obtain all current comments on the JIRA ticket. 
                            jira_comments = jira.comments(jira_issue)
                            for existing_comment in jira_comments:
                                if original_security_level in existing_comment.body:
                                    original_security_level_mentioned = True
                                    break 

                    if original_security_level_mentioned:
                        logger.debug('Original security level for JIRA ticket (' + jira_issue.key + ') mentioned')
                        syslog.syslog(syslog.LOG_DEBUG, 'Original security level for JIRA ticket (' + jira_issue.key + ') mentioned')

                    # If checks passed, then modify the security level.
                    if not original_security_level_mentioned:
                        target_security_level = config.get('jira', 'target_security_level')
                        if target_security_level == "None":
                            logger.info('Removing security level for JIRA ticket (' + jira_issue.key + ')')
                            syslog.syslog(syslog.LOG_INFO, 'Removing security level for JIRA ticket (' + jira_issue.key + ')')
コード例 #15
0
ファイル: jira.py プロジェクト: Liudvikas/bugwarrior
class JiraService(IssueService):
    # A map of jira priorities to taskwarrior priorities
    priorities = {
        'Trivial': 'L',
        'Minor': 'L',
        'Major': 'M',
        'Critical': 'H',
        'Blocker': 'H',
    }

    def __init__(self, *args, **kw):
        super(JiraService, self).__init__(*args, **kw)
        self.username = self.config.get(self.target, 'jira.username')
        self.url = self.config.get(self.target, 'jira.base_uri')
        password = self.config.get(self.target, 'jira.password')
        if not password or password.startswith("@oracle:"):
            service = "jira://%s@%s" % (self.username, self.url)
            password = get_service_password(service, self.username,
                                            oracle=password,
                                            interactive=self.config.interactive)

        default_query = 'assignee=' + self.username + \
            ' AND status != closed and status != resolved'
        self.query = self.config.get(self.target, 'jira.query', default_query)
        self.project_prefix = self.config.get(
            self.target, 'jira.project_prefix', '')
        self.jira = JIRA(
            options={
                'server': self.config.get(self.target, 'jira.base_uri'),
                'rest_api_version': 'latest',
            },
            basic_auth=(self.username, password)
        )

    @classmethod
    def validate_config(cls, config, target):
        for option in ['jira.username', 'jira.password', 'jira.base_uri']:
            if not config.has_option(target, option):
                die("[%s] has no '%s'" % (target, option))

        IssueService.validate_config(config, target)

    def get_owner(self, issue):
        return True

    def annotations(self,  issue):

        annotations = []

        comments = self.jira.comments(issue)

        if comments is []:
            pass
        else:
            for comment in comments:
                created = date.fromtimestamp(time.mktime(time.strptime(
                    comment.created[0:10], '%Y-%m-%d')))

                annotations.append(self.format_annotation(
                    created, comment.author.name, comment.body))

        return dict(annotations)

    def __convert_for_jira4(self, issue):
        print(issue.key)

        class IssueWrapper:
            pass
        #print(self.jira.issue(issue.key).fields.summary.value)
        #print(self.jira.issue(issue.key).fields.summary)
        new_issue = self.jira.issue(issue.key)
        result = IssueWrapper()
        fields = IssueWrapper()
        fields.__dict__ = {
            'summary': new_issue.fields.summary.value,
            'priority': new_issue.fields.priority.name,
        }
        result.__dict__ = {
            'key': issue.key,
            'fields': fields,
        }
        return result

    def __issue(self, case, jira_version):
        result = dict(
            description=self.description(
                title=case.fields.summary,
                url=self.url + '/browse/' + case.key,
                number=case.key.rsplit('-', 1)[1],
                cls="issue"),
            project=self.project_prefix + case.key.rsplit('-', 1)[0],
            priority=self.priorities.get(
                get_priority(case.fields.priority),
                self.default_priority,
            )
        )
        if jira_version != 4:
            result.update(self.annotations(case.key))
        return result

    def issues(self):
        cases = self.jira.search_issues(self.query, maxResults=-1)

        jira_version = 5  # Default version number
        if self.config.has_option(self.target, 'jira.version'):
            jira_version = self.config.getint(self.target, 'jira.version')
        if jira_version == 4:
            # Convert for older jira versions that don't support the new API
            cases = [self.__convert_for_jira4(case) for case in cases]

        log.name(self.target).debug(" Found {0} total.", len(cases))
        return [self.__issue(case, jira_version) for case in cases]
コード例 #16
0
ファイル: client.py プロジェクト: zbqyexingkong/docs-tools
class JeerahClient(object):
    def __init__(self, conf):
        self.conf = conf
        self.credentials = CredentialsConfig(self.conf.site.credentials)
        self.c = None
        self.issues_created = []
        self.abort_on_error = True
        self.results_format = 'list'
        self.versions_cache = {}

    def connect(self):
        if self.c is None:
            self.c = JIRA(options={'server': self.conf.site.url},
                          basic_auth=(self.credentials.jira.username, self.credentials.jira.password))
            logger.debug('created jira connection')
        else:
            logger.debug('jira connection exists')

        logger.debug('configured user: '******'actual user: '******'{0}' to version cache".format(ver.name))

            if project not in self.versions_cache:
                self.versions_cache[project] = { }

            self.versions_cache[project][ver.name] = ver.id

    def create_issue(self, title, text, assignee, project, reporter=None, tags=None, version=None, uid=None):
        issue = {'project': {'key': project},
                 'issuetype': {'name': 'Task'},
                 'summary': title,
                 'description': text,
                 'assignee': {'name': assignee }}

        if report is not None:
            issue['reporter'] = { 'name': reporter }
        if tags is not None:
            issue['labels'] = tags
        if version is not None:
            if project not in self.versions_cache:
                logger.debug("updating version cache to include {0} versions".format(project))
                self.update_version_cache(project)

            if version not in self.versions_cache[project]:
                logger.error("version {0} doesn't exist in {1} project".format(version, project))
            else:
                issue['fixVersions'] = [ { 'id': self.versions_cache[project][version] } ]
                logger.debug('adding version to issue: {0}'.format(issue['fixVersions']))

        new_issue = self.c.create_issue(fields=issue)

        logger.debug('created new issue {0}'.format(new_issue.key))
        self.issues_created.append( { 'key': new_issue.key,
                                      'uid': uid,
                                      'title': title})

    def query(self, query_string):
        logger.info('running query for: {0}'.format(query_string))
        try:
            query_results = self.c.search_issues(jql_str=query_string,
                                                 maxResults=200)
        except Exception as e:
            logger.warning(query_string)
            logger.error(e)
            if self.abort_on_error is True:
                raise SystemExit(e)

        if self.results_format == 'dict':
            return { issue.key: issue for issue in query_results }
        elif self.results_format == 'list':
            return [ issue for issue in query_results ]

    def versions(self, project, released=False, archived=False):
        return [ v
                 for v in self.c.project_versions(project)
                 if v.released is released and v.archived is archived
               ]

    def release_version(self, version):
        if not isinstance(version, Version):
            logger.error('{0} is not a jira version.'.format(version))
        else:
            logger.info('releasing version {0}'.format(version.name))
            version.update(released=True)

    def archive_version(self, version):
        if not isinstance(version, Version):
            logger.error('{0} is not a jira version.'.format(version))
        else:
            logger.info('archiving version {0}'.format(version.name))
            version.update(archived=True)

    def create_version(self, project, name, description='', release=None):
        if release is None:
            release = str(datetime.date.today() + datetime.timedelta(days=14))
        elif release is False:
            release = None
        elif isinstance(release, datetime.date):
            release = str(release)

        self.c.create_version(name=name,
                              project=project,
                              description=description,
                              releaseDate=release)

        logger.debug('created version {0} in project {0}'.format(name, project))
コード例 #17
0
ファイル: client.py プロジェクト: isabella232/docs-tools-1
class JeerahClient(object):
    def __init__(self, conf):
        self.conf = conf
        self.credentials = giza.config.credentials.CredentialsConfig(
            self.conf.system.files.data.jira.site.credentials).jira
        self.c = None
        self.issues_created = []
        self.abort_on_error = True
        self._results_format = 'list'
        self.versions_cache = {}

    @property
    def results_format(self):
        return self._results_format

    @results_format.setter
    def results_format(self, value):
        if value not in ("list", "dict"):
            m = "{0} is not in '{1}'", "value", ', '.join("list", "dict")
            logger.error(m)
            raise TypeError(m)
        else:
            self._results_format = value

    def connect(self):
        credentials = {}
        if self.credentials.access_token is not None:
            logger.debug('using OAuth')
            credentials['oauth'] = {
                'access_token': self.credentials.access_token,
                'access_token_secret': self.credentials.access_token_secret,
                'consumer_key': self.credentials.consumer_key,
                'key_cert': self.credentials.key_cert
            }
        elif self.credentials.username is not None:
            logger.debug('using basic authentication')
            credentials['basic_auth'] = (self.credentials.username,
                                         self.credentials.password)

        if self.c is None:
            self.c = JIRA(options={'server': self.credentials.url},
                          validate=True,
                          **credentials)
            logger.debug('created jira connection')
        else:
            logger.debug('jira connection exists')

    def connect_unauthenticated(self):
        if self.c is None:
            self.c = JIRA(options={'server': self.conf.site.url})

        logger.info("creating an unauthenticated jira connection.")

    def comments(self, issue):
        return self.c.comments(issue)

    def update_version_cache(self, project):
        versions = self.c.project_versions(project)

        for ver in versions:
            logger.debug("adding '{0}' to version cache".format(ver.name))

            if project not in self.versions_cache:
                self.versions_cache[project] = {}

            self.versions_cache[project][ver.name] = ver.id

    def create_issue(self,
                     title,
                     text,
                     assignee,
                     project,
                     reporter=None,
                     tags=None,
                     version=None,
                     uid=None):
        issue = {
            'project': {
                'key': project
            },
            'issuetype': {
                'name': 'Task'
            },
            'summary': title,
            'description': text,
            'assignee': {
                'name': assignee
            }
        }

        if reporter is not None:
            issue['reporter'] = {'name': reporter}
        if tags is not None:
            issue['labels'] = [tags]
        if version is not None:
            if project not in self.versions_cache:
                logger.debug(
                    "updating version cache to include {0} versions".format(
                        project))
                self.update_version_cache(project)

            if version not in self.versions_cache[project]:
                logger.error("version {0} doesn't exist in {1} project".format(
                    version, project))
            else:
                issue['fixVersions'] = [{
                    'id':
                    self.versions_cache[project][version]
                }]
                logger.debug('adding version to issue: {0}'.format(
                    issue['fixVersions']))

        new_issue = self.c.create_issue(fields=issue)

        logger.debug('created new issue {0}'.format(new_issue.key))
        self.issues_created.append({
            'key': new_issue.key,
            'uid': uid,
            'title': title
        })

    def query(self, query_string):
        logger.info('running query for: {0}'.format(query_string))
        try:
            query_results = self.c.search_issues(jql_str=query_string,
                                                 maxResults=200)
        except Exception as e:
            logger.warning(query_string)
            logger.error(e)
            if self.abort_on_error is True:
                raise SystemExit(e)

        if self.results_format == 'dict':
            return {issue.key: issue for issue in query_results}
        elif self.results_format == 'list':
            return [issue for issue in query_results]

    def components(self, project):
        return self.c.project_components(project)

    def versions(self, project, released=False, archived=False):
        return [
            v for v in self.c.project_versions(project)
            if v.released is released and v.archived is archived
        ]

    def release_version(self, version):
        if not isinstance(version, Version):
            logger.error('{0} is not a jira version.'.format(version))
        else:
            logger.info('releasing version {0}'.format(version.name))
            version.update(released=True)

    def archive_version(self, version):
        if not isinstance(version, Version):
            logger.error('{0} is not a jira version.'.format(version))
        else:
            logger.info('archiving version {0}'.format(version.name))
            version.update(archived=True)

    def create_version(self, project, name, description='', release=None):
        if release is None:
            release = str(datetime.date.today() + datetime.timedelta(days=14))
        elif release is False:
            release = None
        elif isinstance(release, datetime.date):
            release = str(release)

        self.c.create_version(name=name,
                              project=project,
                              description=description,
                              releaseDate=release)

        logger.debug('created version {0} in project {0}'.format(
            name, project))
コード例 #18
0
ファイル: rest.py プロジェクト: johnbeard/jira-cli
class JiraRestBridge(JiraBridge):
    def __init__(self, base_url, config, persist=False):
        super(JiraRestBridge, self).__init__(base_url, config, persist)
        self.jira = None

    @cached('resolutions')
    def get_resolutions(self):
        return dict((r.name, rest_recursive_dict(r.raw)) for r in self.jira.resolutions())

    @cached('filters')
    def get_filters(self):
        filters = dict((f.name, rest_recursive_dict(f.raw)) for f in self.jira.favourite_filters())
        return filters

    def clean_issue(self, issue):
        _issue = {}
        for k,v in issue.fields.__dict__.items():
            if isinstance(v, Resource):
                _issue[k] = map_rest_resource(v)
            elif v is not None:
                _issue[k] = v
        _issue['key'] = issue.key
        _issue['type'] = map_rest_resource(_issue['issuetype'])
        return _issue

    def get_issue(self, issue_id):
        try:
            return self.clean_issue(self.jira.issue(issue_id))
        except:
            return None

    def search_issues(self, free_text, project=None, limit=100):
        query = '(summary~"%s" or description~"%s")' % (free_text, free_text)
        if project:
            query += ' and project=%s' % project
        query += ' order by key'
        return [self.clean_issue(issue) for issue in self.jira.search_issues(query,maxResults=100)]

    def search_issues_jql(self, query, limit=100, project=None):
        return [self.clean_issue(issue) for issue in self.jira.search_issues(query, maxResults=100)]

    def get_issues_by_filter(self, *filters):
        return self.search_issues_jql(
            "filter in (%s)" % ",".join(['"%s"' % f for f in filters])
        )

    def add_comment(self, issue, comment):
        self.jira.add_comment(issue, comment)


    def transition_issue(self, issue, transition, comment=""):
        transitions = self.get_available_transitions(issue)
        try:
            return self.jira.transition_issue(issue, transitions[transition]['id'])
        except KeyError:
            raise JiraCliError("Invalid transition '%s'. Use one of [%s]" % (transition, ",".join(transitions)))

    def ping(self):
        return False

    def create_issue(self, project, type='bug', summary="", description="", priority="minor", parent=None):
        issue = {
            "project": {'key':project.upper()},
            "summary": summary,
            "description": description,
            "priority": {'id':self.get_priorities()[priority.lower()]["id"]}
        }
        if type.lower() == 'epic':
            issue['customfield_11401'] = summary
        if parent:
            issue['issuetype'] = {'id':self.get_subtask_issue_types()[type.lower()]['id']}
            issue['parent'] = {'key':parent}
        else:
            issue['issuetype'] = {'id':self.get_issue_types()[type.lower()]['id']}
        return self.clean_issue(self.jira.create_issue(issue))

    def login(self, username, password):
        try:
            self.jira = JIRA(options={'server': self.base_url},
                         basic_auth=(username, password), validate=True
            )
        except JIRAError:
            raise JiraAuthenticationError('failure to authenticate')
        except RequestException:
            raise JiraInitializationError('failure to communicate with jira')

    def get_available_transitions(self, issue):
        return dict((t['name'].lower(), t) for t in self.jira.transitions(issue))

    @cached('issue_types')
    def get_issue_types(self):
        types = dict((k.name.lower(), k.raw) for k in self.jira.issue_types() if not k.subtask)
        for k in types:
            if k=='id':
                types['id'] = types['id'][0]
        return types

    @cached('subtask_types')
    def get_subtask_issue_types(self):
        types = dict((k.name.lower(), k.raw) for k in self.jira.issue_types() if k.subtask)
        for k in types:
            if k=='id':
                types['id'] = types['id'][0]
        return types

    def update_issue(self, issue_id, **kwargs):
        pass

    @cached('projects')
    def get_projects(self):
        return dict((k.name.lower(), k.raw) for k in self.jira.projects())

    @cached('priorities')
    def get_priorities(self):
        return dict((k.name.lower(), dict(k.raw)) for k in self.jira.priorities())

    @cached('components')
    def get_components(self, project):
        return dict((k.name.lower(), dict(k.raw)) for k in self.jira.project_components(project))

    @cached('statuses')
    def get_statuses(self):
        return dict((k.name.lower(), dict(k.raw)) for k in self.jira.statuses())

    def get_issue_comments(self, issue):
        return [
            dict(author=comment.author.name
                 , body=comment.body
                 , created=comment.created
            )
            for comment in self.jira.comments(issue)
        ]
コード例 #19
0
ファイル: rest.py プロジェクト: alisaifee/jira-cli
class JiraRestBridge(JiraBridge):

    def __init__(self, base_url, config, persist=False):
        super(JiraRestBridge, self).__init__(base_url, config, persist)
        self.jira = None

    @cached('resolutions')
    def get_resolutions(self):
        return dict((r.name.lower(), rest_recursive_dict(r.raw)) for r in self.jira.resolutions())

    @cached('filters')
    def get_filters(self):
        filters = dict((f.name, rest_recursive_dict(f.raw)) for f in self.jira.favourite_filters())
        return filters

    def clean_issue(self, issue):
        _issue = {}
        for k,v in issue.fields.__dict__.items():
            if isinstance(v, Resource):
                _issue[k] = map_rest_resource(v)
            elif v is not None:
                _issue[k] = v
        _issue['key'] = issue.key
        _issue['type'] = map_rest_resource(_issue['issuetype'])
        return _issue

    def get_issue(self, issue_id):
        try:
            return self.clean_issue(self.jira.issue(issue_id))
        except:
            return None

    def search_issues(self, free_text, project=None, limit=100):
        query = '(summary~"%s" or description~"%s")' % (free_text, free_text)
        if project:
            query += ' and project=%s' % project
        query += ' order by key'
        return [self.clean_issue(issue) for issue in self.jira.search_issues(query,maxResults=100)]

    def search_issues_jql(self, query, limit=100, project=None):
        return [self.clean_issue(issue) for issue in self.jira.search_issues(query, maxResults=100)]

    def get_issues_by_filter(self, *filters):
        return self.search_issues_jql(
            "filter in (%s)" % ",".join(['"%s"' % f for f in filters])
        )

    def add_comment(self, issue, comment):
        self.jira.add_comment(issue, comment)


    def transition_issue(self, issue, transition, resolution):
        transitions = self.get_available_transitions(issue)
        fields = {}
        if resolution:
            fields["resolution"] = self.get_resolutions()[resolution.lower()]
        try:
            return self.jira.transition_issue(issue, transitions[transition]['id'], fields=fields)
        except KeyError:
            raise JiraCliError("Invalid transition '%s'. Use one of [%s]" % (transition, ",".join(transitions)))

    def ping(self):
        return False

    def create_issue(self, project, type='bug', summary="", description="",
                     priority="minor", parent=None, assignee="", reporter="",
                     labels=[], components={}, **extras):
        issue = {
            "project": {'key':project.upper()},
            "summary": summary,
            "description": description,
            "priority": {'id':self.get_priorities()[priority.lower()]["id"]},
            "components": [{"name": k} for k in components.keys()]
        }
        if labels:
            issue['labels'] = labels
        if not issue["components"]:
            issue.pop("components")
        if type.lower() == 'epic':
            issue['customfield_11401'] = summary
        if parent:
            issue['issuetype'] = {'id':self.get_subtask_issue_types()[type.lower()]['id']}
            issue['parent'] = {'key':parent}
        else:
            issue['issuetype'] = {'id':self.get_issue_types()[type.lower()]['id']}
        if extras:
            issue.update(extras)
        issue = self.jira.create_issue(issue)
        if not (assignee or reporter):
            return self.clean_issue(issue)
        else:
            key = issue.key
            if assignee:
                issue = self.clean_issue(self.assign_issue(key, assignee))
            if reporter:
                issue = self.clean_issue(self.change_reporter(key, reporter))
            return issue

    def login(self, username, password):
        try:
            self.jira = JIRA(options={'server': self.base_url, 'check_update': False},
                         basic_auth=(username, password), get_server_info=False, validate=False
            )
        except JIRAError:
            raise JiraAuthenticationError('failure to authenticate')
        except RequestException:
            raise JiraInitializationError('failure to communicate with jira')

    def get_available_transitions(self, issue):
        return dict((t['name'].lower(), t) for t in self.jira.transitions(issue))

    @cached('issue_types')
    def get_issue_types(self):
        types = dict((k.name.lower(), k.raw) for k in self.jira.issue_types() if not k.subtask)
        for k in types:
            if k=='id':
                types['id'] = types['id'][0]
        return types

    @cached('subtask_types')
    def get_subtask_issue_types(self):
        types = dict((k.name.lower(), k.raw) for k in self.jira.issue_types() if k.subtask)
        for k in types:
            if k=='id':
                types['id'] = types['id'][0]
        return types

    def update_issue(self, issue_id, update={}, **kwargs):
        issue = self.jira.issue(issue_id)
        issue.update(update=update, **kwargs)
        return self.jira.issue(issue_id)

    def assign_issue(self, issue_id, assignee):
        return self.update_issue(
            issue_id, assignee={"name": assignee}
        )

    def change_reporter(self, issue_id, reporter):
        return self.update_issue(
            issue_id, reporter={"name": reporter}
        )

    def add_labels(self, issue_id, labels, merge=False):
        issue = self.jira.issue(issue_id)
        issue.fields.labels.extend(labels)
        return issue.update(fields={"labels": issue.fields.labels})

    @cached('projects')
    def get_projects(self):
        return dict((k.name.lower(), k.raw) for k in self.jira.projects())

    @cached('priorities')
    def get_priorities(self):
        return dict((k.name.lower(), dict(k.raw)) for k in self.jira.priorities())

    @cached('components')
    def get_components(self, project):
        return [k.raw for k in self.jira.project_components(project)]

    def list_versions(self, project):
        versions = self.jira.project(project).versions
        versions.sort(cmp=lambda l, r: cmp(l.id, l.id))
        return [k.raw for k in versions]

    @cached('statuses')
    def get_statuses(self):
        return dict((k.name.lower(), dict(k.raw)) for k in self.jira.statuses())

    def get_issue_comments(self, issue):
        return [
            dict(author=comment.author.name
                 , body=comment.body
                 , created=comment.created
            )
            for comment in self.jira.comments(issue)
        ]

    def add_versions(self, issue, versions, type):
        args = {}

        if type == 'fix':
            args = {'fixVersions': [{"add": {"name": v}} for v in versions]}
        elif type == 'affects':
            args = {'versions': [{"add": {"name": v}} for v in versions]}

        return self.update_issue(issue, **args)

    def remove_versions(self, issue, versions, type):
        args = {}

        if type == 'fix':
            args = {'fixVersions': [{"remove": {"name": v}} for v in versions]}
        elif type == 'affects':
            args = {'versions': [{"remove": {"name": v}} for v in versions]}

        return self.update_issue(issue, **args)
コード例 #20
0
ファイル: rest.py プロジェクト: cenekzach/jira-cli
class JiraRestBridge(JiraBridge):
    def __init__(self, base_url, config, persist=False):
        super(JiraRestBridge, self).__init__(base_url, config, persist)
        self.jira = None

    @cached('resolutions')
    def get_resolutions(self):
        return dict((r.name, rest_recursive_dict(r.raw))
                    for r in self.jira.resolutions())

    @cached('filters')
    def get_filters(self):
        filters = dict((f.name, rest_recursive_dict(f.raw))
                       for f in self.jira.favourite_filters())
        return filters

    def clean_issue(self, issue):
        _issue = {}
        for k, v in issue.fields.__dict__.items():
            if isinstance(v, Resource):
                _issue[k] = map_rest_resource(v)
            elif v is not None:
                _issue[k] = v
        _issue['key'] = issue.key
        _issue['type'] = map_rest_resource(_issue['issuetype'])
        return _issue

    def get_issue(self, issue_id):
        try:
            return self.clean_issue(self.jira.issue(issue_id))
        except:
            return None

    def search_issues(self, free_text, project=None, limit=100):
        query = '(summary~"%s" or description~"%s")' % (free_text, free_text)
        if project:
            query += ' and project=%s' % project
        query += ' order by key'
        return [
            self.clean_issue(issue)
            for issue in self.jira.search_issues(query, maxResults=100)
        ]

    def search_issues_jql(self, query, limit=100, project=None):
        return [
            self.clean_issue(issue)
            for issue in self.jira.search_issues(query, maxResults=100)
        ]

    def get_issues_by_filter(self, *filters):
        return self.search_issues_jql("filter in (%s)" %
                                      ",".join(['"%s"' % f for f in filters]))

    def add_comment(self, issue, comment):
        self.jira.add_comment(issue, comment)

    def transition_issue(self, issue, transition, comment=""):
        transitions = self.get_available_transitions(issue)
        try:
            return self.jira.transition_issue(issue,
                                              transitions[transition]['id'])
        except KeyError:
            raise JiraCliError("Invalid transition '%s'. Use one of [%s]" %
                               (transition, ",".join(transitions)))

    def ping(self):
        return False

    def create_issue(self,
                     project,
                     type='bug',
                     summary="",
                     description="",
                     priority="minor",
                     parent=None,
                     assignee="",
                     reporter="",
                     labels=[],
                     components={}):
        issue = {
            "project": {
                'key': project.upper()
            },
            "summary": summary,
            "description": description,
            "priority": {
                'id': self.get_priorities()[priority.lower()]["id"]
            },
            "labels": labels,
            "components": [{
                "name": k
            } for k in components.keys()]
        }
        if not issue["components"]:
            issue.pop("components")
        if type.lower() == 'epic':
            issue['customfield_11401'] = summary
        if parent:
            issue['issuetype'] = {
                'id': self.get_subtask_issue_types()[type.lower()]['id']
            }
            issue['parent'] = {'key': parent}
        else:
            issue['issuetype'] = {
                'id': self.get_issue_types()[type.lower()]['id']
            }
        issue = self.jira.create_issue(issue)
        if not (assignee or reporter):
            return self.clean_issue(issue)
        else:
            key = issue.key
            if assignee:
                issue = self.clean_issue(self.assign_issue(key, assignee))
            if reporter:
                issue = self.clean_issue(self.change_reporter(key, reporter))
            return issue

    def login(self, username, password):
        try:
            self.jira = JIRA(options={'server': self.base_url},
                             basic_auth=(username, password),
                             validate=True)
        except JIRAError:
            raise JiraAuthenticationError('failure to authenticate')
        except RequestException:
            raise JiraInitializationError('failure to communicate with jira')

    def get_available_transitions(self, issue):
        return dict(
            (t['name'].lower(), t) for t in self.jira.transitions(issue))

    @cached('issue_types')
    def get_issue_types(self):
        types = dict((k.name.lower(), k.raw) for k in self.jira.issue_types()
                     if not k.subtask)
        for k in types:
            if k == 'id':
                types['id'] = types['id'][0]
        return types

    @cached('subtask_types')
    def get_subtask_issue_types(self):
        types = dict((k.name.lower(), k.raw) for k in self.jira.issue_types()
                     if k.subtask)
        for k in types:
            if k == 'id':
                types['id'] = types['id'][0]
        return types

    def update_issue(self, issue_id, update={}, **kwargs):
        issue = self.jira.issue(issue_id)
        issue.update(update=update, **kwargs)
        return self.jira.issue(issue_id)

    def assign_issue(self, issue_id, assignee):
        return self.update_issue(issue_id, assignee={"name": assignee})

    def change_reporter(self, issue_id, reporter):
        return self.update_issue(issue_id, reporter={"name": reporter})

    def add_labels(self, issue_id, labels, merge=False):
        old_labels = []
        if merge:
            issue = self.get_issue(issue_id)
            old_labels = issue.get("labels", [])
        return self.update_issue(issue_id, labels=old_labels + labels)

    @cached('projects')
    def get_projects(self):
        return dict((k.name.lower(), k.raw) for k in self.jira.projects())

    @cached('priorities')
    def get_priorities(self):
        return dict(
            (k.name.lower(), dict(k.raw)) for k in self.jira.priorities())

    @cached('components')
    def get_components(self, project):
        return [k.raw for k in self.jira.project_components(project)]

    @cached('statuses')
    def get_statuses(self):
        return dict(
            (k.name.lower(), dict(k.raw)) for k in self.jira.statuses())

    def get_issue_comments(self, issue):
        return [
            dict(author=comment.author.name,
                 body=comment.body,
                 created=comment.created)
            for comment in self.jira.comments(issue)
        ]

    def add_versions(self, issue, versions, type):
        args = {}

        if type == 'fix':
            args = {'fixVersions': [{"add": {"name": v}} for v in versions]}
        elif type == 'affects':
            args = {'versions': [{"add": {"name": v}} for v in versions]}

        return self.update_issue(issue, **args)

    def remove_versions(self, issue, versions, type):
        args = {}

        if type == 'fix':
            args = {'fixVersions': [{"remove": {"name": v}} for v in versions]}
        elif type == 'affects':
            args = {'versions': [{"remove": {"name": v}} for v in versions]}

        return self.update_issue(issue, **args)
コード例 #21
0
class jira_api():

    def __init__(self, server, user, pwd):
        """
        :param server: jira域名
        :param user:jira用户
        :param pwd:jira账户密码
        """
        self.server = server;
        self.basic_auth = (user, pwd)
        self.jiraClient = None

    def login(self):
        self.jiraClient = JIRA(server=self.server, basic_auth=self.basic_auth)
        if self.jiraClient is not None:
            return True
        else:
            return False


    def get_issue_list(self,jql_str=None,page=0,limit=10,fields=None):
        """
        查询issue集合
        :param jql_str: 查询语句
        :param page: 分页参数 开始
        :param limit: 分页参数 结束
        :param fields: 查询字段
        :return:issue: 列表
        """
        if self.jiraClient is None:
            self.login()
        if jql_str is None and jql_str == "":
            return []
        return self.jiraClient.search_issues(jql_str=jql_str, startAt=page, maxResults=limit, fields=fields)

    def get_issue_info(self,id_or_key,fields=None):
        """
        根据id或key查询issue信息
        :param id_or_key: issue id或key
        :param fields: 查询字段
        :return: issue详细信息
        """
        if self.jiraClient is None:
            self.login()
        if id_or_key is None:
            return {}
        return self.jiraClient.issue(id_or_key, fields=fields)

    def get_comments_from_issue(self,issue):
        """
        获取issue中所有comment集合
        :param issue:
        :return:
        """
        if self.jiraClient is None:
            self.login()
        if issue is None:
            return []
        return self.jiraClient.comments(issue)

    def get_comment_from_issue(self,issue,comment_id):
        if self.jiraClient is None:
            self.login()
        if issue is None:
            return {}
        if comment_id is None:
            return {}
        return self.jiraClient.comment(issue, comment_id)

    def add_comment_to_issue(self,issue_id,comment_str):
        if self.jiraClient is None:
            self.login()
        if issue_id is None or issue_id == "":
            return False
        if comment_str is None or comment_str == "":
            return False
        return self.jiraClient.add_comment(issue_id, comment_str)

    def update_comment(self,issue_id_or_key,comment_id,comment_str):
        if self.jiraClient is None:
            self.login()
        if issue_id_or_key is None or issue_id_or_key == "":
            return False
        if comment_id is None or comment_id == "":
            return False
        comment = self.get_comment_from_issue(self.get_issue_info(issue_id_or_key),comment_id)
        if comment is None:
            return False
        return comment.update(body=comment_str)

    def get_transitions_from_issue(self,issue):
        if self.jiraClient is None:
            self.login()
        if issue is None:
            return {}
        return self.jiraClient.transitions(issue)

    def assign_issue_to_user(self,issue_id,assignee):
        if self.jiraClient is None:
            self.login()
        if issue_id is None:
            return False
        if assignee is None:
            return False
        return self.jiraClient.assign_issue(issue_id, assignee)

    def transition_issues(self,issue_id,transition_id,assignee=None,comment=""):
        if self.jiraClient is None:
            self.login()
        if(issue_id is None):
            return False
        if(transition_id is None):
            return False
        if(comment is None):
            return False
        fields = None
        if assignee is not None and assignee != "":
            fields = {'assignee': {'name': assignee}}
        return self.jiraClient.transition_issue(issue=issue_id,fields=fields,transition=transition_id,comment=comment)

    def group_members(self,group_name):
        if self.jiraClient is None:
            self.login()
        if group_name is None:
            return []
        return self.jiraClient.group_members(group_name)

    def get_project_by_key(self,key):
        if self.jiraClient is None:
            self.login()
        if key is None:
            return ""
        return self.jiraClient.project(key)

    def get_custom_field_option(self,id):
        if self.jiraClient is None:
            self.login()
        if id is None:
            return ""
        return self.jiraClient.custom_field_option(id)