def __init__(self, project_name): self.mongo_db = mongodb_class.mongoDB(project_name) self.jira = JIRA('http://172.16.60.13:8080', basic_auth=('shenwei','sw64419')) self.gh = GreenHopper({'server': 'http://172.16.60.13:8080'}, basic_auth=('shenwei', 'sw64419')) self.name = project_name self.project = self.jira.project(self.name) self.pj_name = u"%s" % self.project.name self.pj_manager = u"%s" % self.project.lead.displayName """获取项目版本信息 """ _versions = self.jira.project_versions(self.name) self.version = {} self.sprints = self._get_sprints() for _v in _versions: _key = (u"%s" % _v).replace('.', '^') if not self.version.has_key(_key): self.version[_key] = {} self.version[_key][u"id"] = _v.id self.version[_key]['startDate'] = "" self.version[_key]['releaseDate'] = "" if 'startDate' in dir(_v): self.version[_key]['startDate'] = _v.startDate if 'releaseDate' in dir(_v): self.version[_key]['releaseDate'] = _v.releaseDate self.mongo_db.handler("project", "update", {"version": _key}, dict({"version": _key}, **self.version[_key])) self.issue = None
def __init__(self, project_name): global config self.mongo_db = mongodb_class.mongoDB(project_name) """将工作日志独立记录""" self.mongo_db_worklogs = mongodb_class.mongoDB("WORK_LOGS") self.jira = JIRA(config.get('JIRA', 'url'), basic_auth=(config.get('JIRA', 'user'), config.get('JIRA', 'password'))) self.gh = GreenHopper({'server': config.get('JIRA', 'url')}, basic_auth=(config.get('JIRA', 'user'), config.get('JIRA', 'password'))) self.name = project_name self.project = self.jira.project(self.name) self.pj_name = u"%s" % self.project.name self.pj_manager = u"%s" % self.project.lead.displayName """获取项目版本信息 """ _versions = self.jira.project_versions(self.name) self.version = {} self.sprints = self._get_sprints() for _v in _versions: _key = (u"%s" % _v).replace('.', '^') if not self.version.has_key(_key): self.version[_key] = {} self.version[_key][u"id"] = _v.id self.version[_key]['startDate'] = "" self.version[_key]['releaseDate'] = "" if 'startDate' in dir(_v): self.version[_key]['startDate'] = _v.startDate if 'releaseDate' in dir(_v): self.version[_key]['releaseDate'] = _v.releaseDate self.mongo_db.handler("project", "update", {"version": _key}, dict({"version": _key}, **self.version[_key])) self.issue = None
def update_task_effort_left(self, user, password, key, effort_left_d): # TODO: See if we need to log in each time gh = GreenHopper( basic_auth=(user, password), options={"server": "https://jira01.corp.linkedin.com:8443", "verify": False} ) issue = gh.issue(key) gh.add_worklog(issue, adjustEstimate="new", timeSpent="1h", newEstimate="%.1fd" % effort_left_d) return
def startSession(self): #now make the connection options = {'server': self.__jiraURL, 'verify': False} self.__jira = JIRA(options, basic_auth=(self.__jiraUser, self.__jiraPassword)) self.__greenhopper = GreenHopper(options, basic_auth=(self.__jiraUser, self.__jiraPassword))
def getJiraClient(): jira=GreenHopper(options={'server': JIRA_SERVER}, oauth={ 'access_token': con_credentials.get('ACCESS_TOKEN'), 'access_token_secret': con_credentials.get('ACCESS_TOKEN_SECRET'), 'consumer_key': CONSUMER_KEY, 'key_cert': RSA_KEY }) return jira
def startSession(self): #now make the connection options = { 'server':self.__jiraURL, 'verify':False } self.__jira = JIRA(options, basic_auth=(self.__jiraUser, self.__jiraPassword)) self.__greenhopper = GreenHopper(options, basic_auth=(self.__jiraUser, self.__jiraPassword))
def jira_login(): username = raw_input("Jira username: "******"Jira password: "******"Company (as used in Jira URL): ") jira_url = "https://" + company + ".jira.com" options = {'server': jira_url} gh = GreenHopper(options, basic_auth=(username, password)) return gh
def _jira_login(self, endpoint: str, user: str, password: str) -> GreenHopper: try: basic_auth = (user, password) jira = GreenHopper({'server': endpoint}, basic_auth=basic_auth) # pylint: disable=protected-access if "JSESSIONID" in jira._session.cookies: # drop basic auth if we have a cookie (for performance) jira._session.auth = None return jira except JIRAError as e: raise e
def jira_login(endpoint, user=None): try: basic_auth = None if user: if sys.stdin.isatty(): password = getpass.getpass(stream=sys.stderr) else: password = sys.stdin.readline().rstrip() basic_auth = (user, password) try: jira = GreenHopper({'server': endpoint}, basic_auth=basic_auth) except JIRAError: sys.stderr.write("warn: Autentication to JIRA failed," + " continuing unauthenticated\n") jira = GreenHopper({'server': endpoint}) # pylint: disable=protected-access if "JSESSIONID" in jira._session.cookies: # drop basic auth if we have a cookie (for performance) jira._session.auth = None return jira except JIRAError as exn: sys.stderr.write("Connection to JIRA failed: %s: %s\n" % (exn.response.status_code, exn.response.reason)) exit(4)
def greenhopper(self): return GreenHopper(options={ 'server': self.get('root'), 'verify': False }, oauth={ 'access_token': self.get('access_token'), 'access_token_secret': self.get('access_token_secret'), 'consumer_key': self.get('consumer_key'), 'key_cert': self.rsa_key })
class IssueManager: ## The constructor # @param self The object pointer # @param jiraURL The url of the JIRA instance # @param jiraUser The user name to use # @param jiraPassword The user password to use def __init__(self, jiraURL, jiraUser, jiraPassword): self.__jiraUser = jiraUser self.__jiraPassword = jiraPassword self.__jiraURL = jiraURL self.startSession() ## Create the connection # @param self Teh object pointer def startSession(self): #now make the connection options = {'server': self.__jiraURL, 'verify': False} self.__jira = JIRA(options, basic_auth=(self.__jiraUser, self.__jiraPassword)) self.__greenhopper = GreenHopper(options, basic_auth=(self.__jiraUser, self.__jiraPassword)) ## Kill the jira connection # @param self The object pointer def killSession(self): self.__jira.kill_session() self.__greenhopper.kill_session() ## Add the epic link to an issue # @param self The object pointer # @param issue The issue ID # @param epic The epic ID def attachEpic(self, issue, epic): #attach the epic self.__greenhopper.add_issues_to_epic(epic, [issue]) ## Create an issue set by calling the createIssue and createSubtask methods # @param self The object pointer # @param project The project to add the issue to # @param name The summary of the issue # @param priority The priority of the issue # @param product The software product the requirement belongs to (e.g. Device, Apollo, Cloud) # @param components The components to add to the issues # @returns A dictionary of issues that were created def createIssueSet(self, project, name, priority, product, components): #dictionary to store jira issues issues = {} #set up the description description = '<h3>User Experience</h3><h3>Acceptance Criteria</h3><ul><li></li></ul>' #if we list more than one product that set the product flag to multiple productLabel = product if (';' in product): productLabel = 'Multiple' #create the parent issue parentID = self.createIssue(project, name, description, 'Story', priority, productLabel, components) issues[parentID] = '%s\t%s\t%s' % (parentID, 'Story', name) #create the tasks for development and testing depending on the product for specificProduct in product.split(';'): issue1 = self.createIssue( project, 'Implementation (%s): %s' % (specificProduct, name), '', 'Implement', priority, productLabel, components) issues[issue1] = '%s\t%s\t%s' % (issue1, 'Implement', name) issue2 = self.createIssue( project, 'Create Unit Tests (%s): %s' % (specificProduct, name), '', 'Unit Test', priority, productLabel, components) issues[issue2] = '%s\t%s\t%s' % (issue2, 'Unit Test', name) issue3 = self.createIssue( project, 'Verification (%s): %s' % (specificProduct, name), '', 'Verification Test', priority, productLabel, components) issues[issue3] = '%s\t%s\t%s' % (issue3, 'Verification Test', name) #create the links self.linkIssues(parentID, issue1, 'Develop') self.linkIssues(parentID, issue2, 'Verify') self.linkIssues(parentID, issue3, 'Verify') #print the ids return (parentID, issues) ## Create a new issue # @param self The object pointer # @param project The project to add the issue to # @param summary The summary of the issue # @param description The description of the issue # @param issueType The type of the issue # @param priority The priority of the issue # @param product The software product the requirement belongs to (e.g. Device, Apollo, Cloud) # @param components The components to add to the issue # @returns The JIRA issue identifier def createIssue(self, project, summary, description, issueType, priority, product, components): #create an issue by setting up the dictionary issueDict = { 'project': { 'key': project }, 'priority': { 'name': priority }, 'summary': summary, 'description': description, 'issuetype': { 'name': issueType }, 'labels': ['AddedViaAPI'], 'customfield_11100': { 'value': product } } #add the components if there are any if (not components == ''): issueDict['components'] = self.addComponents(components) #now create the issue print issueDict newIssue = self.__jira.create_issue(fields=issueDict) #return the id return (newIssue.key) ## Create a new epic # @param self The object pointer # @param project The project to add the issue to # @param summary The summary of the issue # @param description The description of the issue # @param issueType The type of the issue # @param priority The priority of the issue # @param product The software product the requirement belongs to (e.g. Device, Apollo, Cloud) # @param components The components to add to the issue # @returns The JIRA issue identifier def createEpic(self, project, summary, description, issueType, priority, product, components): #create an issue by setting up the dictionary issueDict = { 'project': { 'key': project }, 'priority': { 'name': priority }, 'summary': summary, 'description': description, 'issuetype': { 'name': issueType }, 'labels': ['AddedViaAPI'], 'customfield_11100': { 'value': product }, 'customfield_10401': summary } #add the components if there are any if (not components == ''): issueDict['components'] = self.addComponents(components) #now create the issue print issueDict newIssue = self.__greenhopper.create_issue(fields=issueDict) #return the id return (newIssue.key) ## Create a subtask issue # @param self The object pointer # @param project The project to add the issue to # @param summary The summary of the issue # @param description The description of the issue # @param issueType The type of the issue # @param priority The priority of the issue # @param product The software product the requirement belongs to (e.g. Device, Apollo, Cloud) # @param components The components to add to the task # @returns The JIRA issue identifier def createSubtask(self, project, parentID, summary, description, issueType, priority, product, components): #create an issue by setting up the dictionary issueDict = { 'parent': { 'id': parentID }, 'project': { 'key': project }, 'priority': { 'name': priority }, 'summary': summary, 'description': description, 'issuetype': { 'name': issueType }, 'labels': ['AddedViaAPI'], 'customfield_11100': { 'value': product } } #add the components if there are any if (not components == ''): issueDict['components'] = self.addComponents(components) #now create the issue print issueDict newIssue = self.__jira.create_issue(fields=issueDict) #return the id return (newIssue.key) ## Link two issues # @param self The object pointer # @param jiraID1 The JIRA id of the first issue # @param jiraID2 The JIRA id of the second issue # @param linkType The type of connect def linkIssues(self, jiraID1, jiraID2, linkType): #now link the two issues print "Linking %s and %s" % (jiraID1, jiraID2) self.__jira.create_issue_link(type=linkType, inwardIssue=jiraID2, outwardIssue=jiraID1) ## Create an array from a ";"-separated list, used for populating components # @param self The object pointer # @param componentString The string to be parsed # @returns The array of components def addComponents(self, componentString): tokens = componentString.split(';') components = [] #populate the array for token in tokens: components.append({'name': token}) return (components)
class jira_handler: def __init__(self, project_name): self.mongo_db = mongodb_class.mongoDB() self.jira = JIRA('http://172.16.60.13:8080', basic_auth=('shenwei', 'sw64419')) self.gh = GreenHopper({'server': 'http://172.16.60.13:8080'}, basic_auth=('shenwei', 'sw64419')) self.name = project_name self.project = self.jira.project(self.name) self.pj_name = u"%s" % self.project.name self.pj_manager = u"%s" % self.project.lead.displayName """获取项目版本信息 """ _versions = self.jira.project_versions(self.name) self.version = {} for _v in _versions: _key = (u"%s" % _v).replace('.', '^') if not self.version.has_key(_key): self.version[_key] = {} self.version[_key][u"id"] = _v.id self.version[_key]['startDate'] = "" self.version[_key]['releaseDate'] = "" if 'startDate' in dir(_v): self.version[_key]['startDate'] = _v.startDate if 'releaseDate' in dir(_v): self.version[_key]['releaseDate'] = _v.releaseDate if self.mongo_db.get_count("project", {"version": _key}) > 0: self.mongo_db.handler( "project", "update", {"version": _key}, dict({"version": _key}, **self.version[_key])) else: _val = dict({"version": _key}, **self.version[_key]) print _val self.mongo_db.handler("project", "insert", _val) self.issue = None def _get_board(self): _boards = self.jira.boards() for _b in _boards: if self.name in _b.name: return _b.id return None def get_current_sprint(self): """ 获取本阶段sprint名称 :return: 返回状态为ACTIVE的sprint的名称 """ _b_id = self._get_board() if type(_b_id) is not types.NoneType: _sprints = self.jira.sprints(_b_id) for _s in _sprints: if _s.state == 'ACTIVE': return _s.name return None def get_sprint(self): if "customfield_10501" in self.issue.raw['fields'] and \ type(self.issue.fields.customfield_10501) is not types.NoneType: return u'%s' % self.issue.fields.customfield_10501[0].split( 'name=')[1].split(',')[0] return None def get_versions(self): _v = {} for _k in self.version: _key = (u"%s" % _k).replace('^', '.') _v[_key] = self.version[_k] return _v def get_pj_info(self): return {'pj_name': self.pj_name, 'pj_manager': self.pj_manager} def set_issue_by_name(self, issue_id): self.issue = self.jira.issue(issue_id) def print_green_hopper(self): _f = self.gh.fields() for __f in _f: __cns = __f['clauseNames'] print('-' * 8) for _n in __cns: print u"name: %s" % _n print "id: ", u"%s" % __f['id'] print "name: ", u"%s" % __f['name'] def get_story_point(self): """ 获取Issue(story)的预置成本, 1 point = 4 hours :return: 预置成本 """ if "customfield_10304" in self.issue.raw['fields'] and \ type(self.issue.fields.customfield_10304) is not types.NoneType: return self.issue.fields.customfield_10304 return None def get_task_time(self): return { "agg_time": self.issue.fields.aggregatetimeestimate, "org_time": self.issue.fields.timeoriginalestimate, "spent_time": self.issue.fields.timespent } def get_landmark(self): if len(self.issue.fields.fixVersions) > 0: return u"%s" % self.issue.fields.fixVersions[0] if len(self.issue.fields.versions) > 0: print self.show_name( ), " version: %s" % self.issue.fields.versions[0] return u"%s" % self.issue.fields.versions[0] return "" def get_desc(self): return u"%s" % self.issue.fields.summary def show_name(self): return u"%s" % str(self.issue) def get_type(self): return u"%s" % self.issue.fields.issuetype def get_status(self): return u"%s" % self.issue.fields.status def get_subtasks(self): """ 收集issue的相关子任务的issue :return: 相关issue字典 """ link = {} if not link.has_key(self.show_name()): link[self.show_name()] = [] _task_issues = self.issue.fields.subtasks for _t in _task_issues: link[self.show_name()].append(u"%s" % _t) return link def get_child_requirement(self): link = [] jql = "issue in childrenOfParentRequirement('%s')" % self.show_name() # print jql tot = 0 while True: issues = self.jira.search_issues(jql, maxResults=100, startAt=tot) for issue in issues: link.append(issue.key) if len(issues) == 100: tot += 100 else: break return link def get_link(self): """ 收集issue的相关issue :return: 相关issue字典 """ link = {} if self.show_name() not in link: link[self.show_name()] = [] """兼容以前: 与story相关的task是通过issulelinks关联的""" _task_issues = self.issue.fields.issuelinks for _t in _task_issues: if "outwardIssue" in dir(_t): """该story相关的任务""" link[self.show_name()].append(u"%s" % _t.outwardIssue) if "inwardIssue" in dir(_t): """该story相关的任务""" link[self.show_name()].append(u"%s" % _t.inwardIssue) """采用synapseRT插件后对需求的管理""" _task_issues = self.get_child_requirement() for _t in _task_issues: link[self.show_name()].append(_t) return link def show_issue(self): """ 显示issue信息 :return: """ print("[%s]-%s" % (self.show_name(), self.get_desc())), print u"类型:%s" % self.get_type(), print(u'状态:%s' % self.get_status()), print u"里程碑:%s" % self.get_landmark(), _time = self.get_task_time() """ if type(_time['agg_time']) is types.NoneType: _time['agg_time'] = "" if type(_time['org_time']) is types.NoneType: _time['org_time'] = "" if type(_time["spent_time"]) is types.NoneType: _time["spent_time"] = "" """ if "customfield_11300" in self.issue.raw['fields'] and \ type(self.issue.fields.customfield_11300) is not types.NoneType: _epic_link = self.issue.raw['fields']["customfield_11300"] else: _epic_link = None _issue = { u"%s" % self.show_name(): { "issue_type": self.get_type(), "created": self.issue.fields.created, "updated": self.issue.fields.updated, "lastViewed": self.issue.fields.lastViewed, "users": self.get_users(), "status": self.get_status(), "landmark": self.get_landmark(), "point": self.get_story_point(), "agg_time": _time['agg_time'], "org_time": _time['org_time'], "summary": self.issue.fields.summary, "spent_time": _time['spent_time'], "sprint": self.get_sprint(), "epic_link": _epic_link } } _key = u"%s" % self.show_name() if self.mongo_db.get_count("issue", {"issue": _key}) > 0: self.mongo_db.handler("issue", "update", {"issue": _key}, dict({"issue": _key}, **_issue[_key])) else: self.mongo_db.handler("issue", "insert", dict({"issue": _key}, **_issue[_key])) if self.mongo_db.get_count("issue_link", {"issue": _key}) > 0: self.mongo_db.handler("issue_link", "update", {"issue": _key}, dict({"issue": _key}, **self.get_link())) else: self.mongo_db.handler("issue_link", "insert", dict({"issue": _key}, **self.get_link())) return _issue def get_users(self): """ 获取访问issue的用户 2018.3.1:改为 经办人 assignee :return: watcher = self.jira.watchers(self.issue) _user = u"%s" % (', '.join(watcher.displayName for watcher in watcher.watchers)) """ if type(self.issue.raw['fields']["assignee"]) is types.NoneType: return None return (u"%s" % self.issue.raw['fields']["assignee"]['displayName']).replace( ' ', '') def write_log(self, info): self.mongo_db.handler("log", "insert", info) def write_worklog(self, info): _search = { 'issue': info['issue'], 'author': info['author'], 'updated': info['updated'] } self.mongo_db.handler('worklog', 'update', _searchinfo) def sync_worklog(self): worklogs = self.jira.worklogs(self.show_name()) wl = {} for worklog in worklogs: wl['issue'] = self.show_name() wl['author'] = u'%s' % worklog.author wl['comment'] = u'%s' % worklog.comment wl['timeSpent'] = worklog.timeSpent wl['timeSpentSeconds'] = worklog.timeSpentSeconds wl['updated'] = worklog.updated self.write_worklog(wl) def scan_issue(self, bg_date, keys, version): """ 扫描project收集相关版本的issue信息 :param bg_date: 起始日期,如 2018-1-31 :param keys: 关键字,[u'story', u'故事'] :param version: 版本,里程碑 :return: 按issue类型进行统计值kv_sum,issue链kv_link,相关任务链task_link """ jql_sql = u'project=%s AND created >= %s ORDER BY created DESC' % ( self.name, bg_date) total = 0 kv_sum = {} kv_link = {} task_link = {} while True: issues = self.jira.search_issues(jql_sql, maxResults=100, startAt=total) for issue in issues: if (u"%s" % issue.fields.issuetype) not in keys: continue self.issue = issue if ((u"%s" % issue.fields.issuetype) == 'story' and (self.get_landmark() == version or u'入侵' in self.issue.fields.summary)) or \ (u"%s" % issue.fields.issuetype) in [u'epic', u'improvement', u'New Feature', u'改进', u'新功能']: """收集story相关的任务""" task_link.update(self.get_link()) _type = self.get_type() _status = self.get_status() if not kv_sum.has_key(_type): kv_sum[_type] = 0 kv_link[_type] = {} kv_sum[_type] += 1 if not (kv_link[_type]).has_key(_status): (kv_link[_type])[_status] = [] (kv_link[_type])[_status].append(self.show_name()) if len(issues) == 100: total += 100 else: break return kv_sum, kv_link, task_link
# This script shows how to use the client in anonymous mode # against jira.atlassian.com. from six import print_ as print from jira.client import GreenHopper # By default, the client will connect to a JIRA instance started from the Atlassian Plugin SDK # (see https://developer.atlassian.com/display/DOCS/Installing+the+Atlassian+Plugin+SDK for details). # Override this with the options parameter. # GreenHopper is a plugin in a JIRA instance options = { 'server': 'https://jira.atlassian.com' } gh = GreenHopper(options) # Get all boards viewable by anonymous users. boards = gh.boards() # Get the sprints in a specific board board_id = 441 print("GreenHopper board: %s (%s)" % (boards[0].name, board_id)) sprints = gh.sprints(board_id) # List the incomplete issues in each sprint for sprint in sprints: sprint_id = sprint.id print("Sprint: %s" % sprint.name) incompleted_issues = gh.incompleted_issues(board_id, sprint_id) print("Incomplete issues: %s" % ', '.join(issue.key for issue in incompleted_issues))
filename=file_path) jira_config = get_jira_config() jira_pwd = (base64.b64decode(jira_config['pwd'])).decode('utf-8') myjira = JIRA( jira_config['url'], basic_auth=(jira_config['user'], (base64.b64decode(jira_config['pwd'])).decode('utf-8')), logging=True, validate=True, async_=True, async_workers=20, options={'verify': False}, ) greenhopper = GreenHopper( options={'server': CI_JIRA_URL, 'verify': False}, basic_auth=(jira_config['user'], jira_pwd) ) class Testclient(object): """ testrail client, send request, and get info and data """ def __init__(self, base_url): self.user = xxxxxxxxx self.password = xxxxxxxxx if not base_url.endswith('/'): base_url += '/' self.__url = base_url + 'index.php?/api/v2/' def send_get(self, uri, filepath=None):
issue.estimateStatistic.statFieldValue.value) if hasattr(issue, 'issue.trackingStatistic.statFieldValue.text'): fh.write("remainingtime: %s\n", issue.trackingStatistic.statFieldValue.text) fh.write("\n#####################################\n") #print issue.assignee, ":", get_email_addr(issue.key), "," # By default, the client will connect to a JIRA instance started from the Atlassian Plugin SDK # (see https://developer.atlassian.com/display/DOCS/Installing+the+Atlassian+Plugin+SDK for details). # Override this with the options parameter. # GreenHopper is a plugin in a JIRA instance options = {'server': 'https://issues.citrite.net'} jira = JIRA(options) gh = GreenHopper(options) # Get all boards viewable by anonymous users. # boards = gh.boards() board_id = "" board_name = "" # for board in boards: # if 'scrum-hyd' in board.name.lower(): # board_id = board.id # board_name = board.name # break # Get the sprints in a specific board board_id = 440 board_name = 'CCP QA Scrum' sprint_id = 1578 print("GreenHopper board: %s (%s)" % (board_name, board_id))
# This script shows how to use the client in anonymous mode # against jira.atlassian.com. from jira.client import GreenHopper # By default, the client will connect to a Jira instance started from the Atlassian Plugin SDK # (see https://developer.atlassian.com/display/DOCS/Installing+the+Atlassian+Plugin+SDK for details). # Override this with the options parameter. # GreenHopper is a plugin in a Jira instance options = {"server": "https://jira.atlassian.com"} gh = GreenHopper(options) # Get all boards viewable by anonymous users. boards = gh.boards() # Get the sprints in a specific board board_id = 441 print(f"GreenHopper board: {boards[0].name} ({board_id})") sprints = gh.sprints(board_id)
# This script shows how to use the client in anonymous mode # against jira.atlassian.com. from six import print_ as print from jira.client import GreenHopper # By default, the client will connect to a JIRA instance started from the Atlassian Plugin SDK # (see https://developer.atlassian.com/display/DOCS/Installing+the+Atlassian+Plugin+SDK for details). # Override this with the options parameter. # GreenHopper is a plugin in a JIRA instance options = {'server': 'https://jira.atlassian.com'} gh = GreenHopper(options) # Get all boards viewable by anonymous users. boards = gh.boards() # Get the sprints in a specific board board_id = 441 print("GreenHopper board: %s (%s)" % (boards[0].name, board_id)) sprints = gh.sprints(board_id) # List the incomplete issues in each sprint for sprint in sprints: sprint_id = sprint.id print("Sprint: %s" % sprint.name) incompleted_issues = gh.incompleted_issues(board_id, sprint_id) print("Incomplete issues: %s" % ', '.join(issue.key for issue in incompleted_issues))
# Gets velocity and related stuff #import imp import sys sys.path.append('/Users/jmcbride/Code/jira-python') #print sys.path from jira.client import GreenHopper from jira.exceptions import * options = { 'server': 'https://jira.rax.io' } gh = GreenHopper(options) e = JIRAError() # Get all boards available boards = gh.boards # Get the DNS board board_id = 41 #print("Story board: %s (%s)" % (boards[0].name, board_id)) #print("Story board: %s" % (boards[0].name)) #print("Story board: %s" % (board_id), boards) sprints = gh.sprints(board_id) # List all sprints print("%s Sprints for this board:") % (len(sprints))
def connect(self): self._jira_client = GreenHopper(basic_auth=(self._username, self._password), options={'server': self._server_url})
if hasattr(issue, 'issue.trackingStatistic.statFieldValue.text'): fh.write("remainingtime: %s\n", issue.trackingStatistic.statFieldValue.text) fh.write("\n#####################################\n") #print issue.assignee, ":", get_email_addr(issue.key), "," # By default, the client will connect to a JIRA instance started from the Atlassian Plugin SDK # (see https://developer.atlassian.com/display/DOCS/Installing+the+Atlassian+Plugin+SDK for details). # Override this with the options parameter. # GreenHopper is a plugin in a JIRA instance options = { 'server': 'https://issues.citrite.net' } jira = JIRA(options) gh = GreenHopper(options) # Get all boards viewable by anonymous users. # boards = gh.boards() board_id = "" board_name = "" # for board in boards: # if 'scrum-hyd' in board.name.lower(): # board_id = board.id # board_name = board.name # break # Get the sprints in a specific board board_id = 440 board_name = 'CCP QA Scrum' sprint_id = 1578 print("GreenHopper board: %s (%s)" % (board_name, board_id))
class IssueManager: ## The constructor # @param self The object pointer # @param options The option hash def __init__(self, options): self.__jiraUser = options.user self.__jiraPassword = options.password self.__jiraURL = options.site self.startSession() ## Create the connection # @param self Teh object pointer def startSession(self): #now make the connection options = { 'server':self.__jiraURL, 'verify':False } self.__jira = JIRA(options, basic_auth=(self.__jiraUser, self.__jiraPassword)) self.__greenhopper = GreenHopper(options, basic_auth=(self.__jiraUser, self.__jiraPassword)) ## Kill the jira connection # @param self The object pointer def killSession(self): self.__jira.kill_session() self.__greenhopper.kill_session() ## Create a new epic # @param self The object pointer # @param options The option dictionary # @returns The JIRA issue identifier def createEpic(self, options): #create an epic by setting up the dictionary issueDict = { #'assignee': {'name':'Unassigned'}, 'project': {'key':options.project}, 'priority' : {'name':options.priority}, 'summary': options.epic, 'customfield_10401': options.epic, 'description': options.description, 'issuetype' : {'name':'Epic'}, 'labels':[ 'AddedViaAPI', 'APISetFixVersion' ], } #set up software / hardware product type #if we list more than one product that set the product flag to multiple productLabel = options.product if(';' in options.product): productLabel = 'Multiple' #see if we are hardware of software if options.type == 'Hardware': #hardware product issueDict['customfield_11200'] = {'value':productLabel} else: #software product issueDict['customfield_11100'] = {'value':productLabel} #add the components if there are any if(not options.components == ''): issueDict['components'] = self.addComponents(options.components) #now create the issue #print issueDict newIssue = self.__greenhopper.create_issue(fields=issueDict) #return the id return(newIssue.key) ## Link two issues # @param self The object pointer # @param jiraID1 The JIRA id of the first issue # @param jiraID2 The JIRA id of the second issue # @param linkType The type of connect def linkIssues(self, jiraID1, jiraID2, linkType): #now link the two issues print "Linking %s and %s" % (jiraID1, jiraID2) self.__jira.create_issue_link(type=linkType, inwardIssue=jiraID2, outwardIssue=jiraID1) ## Create an array from a ";"-separated list, used for populating components # @param self The object pointer # @param componentString The string to be parsed # @returns The array of components def addComponents(self, componentString): tokens = componentString.split(';') components = [] #populate the array for token in tokens: components.append( {'name':token} ) return(components)
def jgreencon(jira_param): """ This is the methos used as jira connector """ jcon = GreenHopper(options={'server': jira_param[0]}, basic_auth=(jira_param[1], jira_param[2])) return jcon
# =============================== # from jira import JIRA from jira.client import GreenHopper import io import types, json import sys options = { 'server': 'http://jira.chinacloud.com.cn', } jira = JIRA('http://jira.chinacloud.com.cn', basic_auth=('shenwei', 'sw64419')) gh = GreenHopper(options, basic_auth=('shenwei', 'sw64419')) fd = io.open('date_desc.txt', 'w+', encoding='utf-8') def _print(s): global fd fd.write(u"%s" % s) fd.write(u"\n") print(s) def test(custom_name): # Fetch all fields allfields = jira.fields() # Make a map from field name -> field id
def GreenHopper_connect(self, username, password, options): print('authenticating with JIRA Greenhopper...') gh = GreenHopper(basic_auth=(username, password), options = options) print('authenticated with GreenHopper. ') return gh
class jira_handler: """ Jira处理类。 【备注】:目前存在Jira方法与Issue对象混合在一起的不足。 【改进】:将Jira方法与Issue对象实体分离,各自进行类定义。 """ def __init__(self, project_name): self.mongo_db = mongodb_class.mongoDB(project_name) self.jira = JIRA('http://172.16.60.13:8080', basic_auth=('shenwei','sw64419')) self.gh = GreenHopper({'server': 'http://172.16.60.13:8080'}, basic_auth=('shenwei', 'sw64419')) self.name = project_name self.project = self.jira.project(self.name) self.pj_name = u"%s" % self.project.name self.pj_manager = u"%s" % self.project.lead.displayName """获取项目版本信息 """ _versions = self.jira.project_versions(self.name) self.version = {} self.sprints = self._get_sprints() for _v in _versions: _key = (u"%s" % _v).replace('.', '^') if not self.version.has_key(_key): self.version[_key] = {} self.version[_key][u"id"] = _v.id self.version[_key]['startDate'] = "" self.version[_key]['releaseDate'] = "" if 'startDate' in dir(_v): self.version[_key]['startDate'] = _v.startDate if 'releaseDate' in dir(_v): self.version[_key]['releaseDate'] = _v.releaseDate self.mongo_db.handler("project", "update", {"version": _key}, dict({"version": _key}, **self.version[_key])) self.issue = None def _get_board(self): _boards = self.jira.boards() for _b in _boards: if self.name.upper() in _b.name.upper(): return _b.id return None def transDate(self, str): print("---> transDate [%s] <---" % str) if str != None and str != u'无': _s = str.\ replace(u'十一月', '11').\ replace(u'十二月', '12').\ replace(u'一月', '1').\ replace(u'二月', '2').\ replace(u'三月', '3').\ replace(u'四月', '4').\ replace(u'五月', '5').\ replace(u'六月', '6').\ replace(u'七月', '7').\ replace(u'八月', '8').\ replace(u'九月', '9').\ replace(u'十月', '10') _time = time.strptime(_s, '%d/%m/%y') return time.strftime('%Y-%m-%d', _time) else: return "" def _get_sprints(self): """ 获取看板内sprint列表 :return: sprint列表 [ name, startDate, endDate, state ] """ _list = [] _b_id = self._get_board() if type(_b_id) is not types.NoneType: _sprints = self.jira.sprints(_b_id) for _s in _sprints: _sprint = self.jira.sprint(_s.id) _data = {'name': _s.name, 'startDate': self.transDate(_sprint.startDate.split(' ')[0]), 'endDate': self.transDate(_sprint.endDate.split(' ')[0]), 'state': _s.state } _list.append(_data) return _list return None def get_sprints(self): return self.sprints def get_current_sprint(self): """ 获取本阶段sprint名称 :return: 返回状态为ACTIVE的sprint的名称 """ if type(self.sprints) is not types.NoneType: for _s in self.sprints: if _s['state'] == 'ACTIVE': return (_s['name'], _s['startDate'], _s['endDate']), _next _next = (_s['name'], _s['startDate'], _s['endDate']) return None def get_sprint(self): """ 获取当前Issue的sprint定义 :return: sprint定义 """ if "customfield_10501" in self.issue.raw['fields'] and \ type(self.issue.fields.customfield_10501) is not types.NoneType: return u'%s' % (",".join(item.split('name=')[1].split(',')[0] for item in self.issue.fields.customfield_10501)) # return u'%s' % self.issue.fields.customfield_10501[0].split('name=')[1].split(',')[0] return None def get_versions(self): _v = {} for _k in self.version: _key = (u"%s" % _k).replace('^', '.') _v[_key] = self.version[_k] return _v def get_pj_info(self): return {'pj_name': self.pj_name, 'pj_manager': self.pj_manager} def set_issue_by_name(self, issue_id): self.issue = self.jira.issue(issue_id) def print_green_hopper(self): _f = self.gh.fields() for __f in _f: __cns = __f['clauseNames'] print('-' * 8) for _n in __cns: print u"name: %s" % _n print "id: ", u"%s" % __f['id'] print "name: ", u"%s" % __f['name'] def get_story_point(self): """ 获取Issue(story)的预置成本, 1 point = 4 hours :return: 预置成本 """ if "customfield_10304" in self.issue.raw['fields'] and \ type(self.issue.fields.customfield_10304) is not types.NoneType: return self.issue.fields.customfield_10304 return None def get_task_time(self): return {"agg_time": self.issue.fields.aggregatetimeestimate, "org_time": self.issue.fields.timeoriginalestimate, "spent_time": self.issue.fields.timespent} def get_landmark(self): if len(self.issue.fields.fixVersions) > 0: return u"%s" % self.issue.fields.fixVersions[0] if len(self.issue.fields.versions) > 0: # print self.show_name(), " version: %s" % self.issue.fields.versions[0] return u"%s" % self.issue.fields.versions[0] return "" def get_desc(self): return self.issue.fields.summary def show_name(self): return str(self.issue) def get_type(self): return u"%s" % self.issue.fields.issuetype def get_status(self): """ 获取Issue的状态,待办、处理中、待测试、测试中、完成 :return: """ return u"%s" % self.issue.fields.status def get_subtasks(self): """ 收集issue的相关子任务的issue :return: 相关issue字典 """ link = {} if not link.has_key(self.show_name()): link[self.show_name()] = [] _task_issues = self.issue.fields.subtasks for _t in _task_issues: link[self.show_name()].append(u"%s" % _t) return link def get_child_requirement(self): link = [] jql = "issue in childrenOfParentRequirement('%s')" % self.show_name() # print jql tot = 0 while True: issues = self.jira.search_issues(jql, maxResults=100, startAt=tot) for issue in issues: link.append(issue.key) if len(issues) == 100: tot += 100 else: break return link def get_epic_link(self, jql): print(">>> get_epic_link<%s>" % jql) total = 0 _issue_name = self.show_name() task_link = {_issue_name: []} while True: issues = self.jira.search_issues(jql, maxResults=100, startAt=total) for issue in issues: self.issue = issue self.sync_issue() """收集epic相关的story和任务""" task_link[_issue_name].append(self.show_name()) if len(issues) == 100: total += 100 else: break print task_link self.set_issue_by_name(_issue_name) return task_link def get_link(self): """ 收集issue的相关issue :return: 相关issue字典 """ link = {self.show_name(): []} """兼容以前: 与story相关的task是通过issulelinks关联的""" _task_issues = self.issue.fields.issuelinks for _t in _task_issues: if "outwardIssue" in dir(_t): """该story相关的任务""" link[self.show_name()].append(u"%s" % _t.outwardIssue) if "inwardIssue" in dir(_t): """该story相关的任务""" link[self.show_name()].append(u"%s" % _t.inwardIssue) """采用synapseRT插件后对需求的管理""" _task_issues = self.get_child_requirement() for _t in _task_issues: link[self.show_name()].append(_t) return link def sync_issue(self): """ 同步issue数据,同时完成重要参量的变更日志。 :return: """ _components = u"%s" % (', '.join(comp.name for comp in self.issue.fields.components)) _key = u"%s" % self.show_name() _time = self.get_task_time() _epic_link = None if "customfield_11300" in self.issue.raw['fields'] and \ type(self.issue.fields.customfield_11300) is not types.NoneType: _epic_link = self.issue.raw['fields']["customfield_11300"] _issue = {u"%s" % self.show_name(): { "issue_type": self.get_type(), "created": self.issue.fields.created, "updated": self.issue.fields.updated, "lastViewed": self.issue.fields.lastViewed, "users": self.get_users(), "status": self.get_status(), "landmark": self.get_landmark(), "point": self.get_story_point(), "agg_time": _time['agg_time'], "org_time": _time['org_time'], "summary": self.issue.fields.summary, "spent_time": _time['spent_time'], "sprint": self.get_sprint(), "epic_link": _epic_link, "components": _components }} _old_issue = self.mongo_db.handler("issue", "find_one", {"issue": _key}) if _old_issue is None: self.mongo_db.handler("issue", "update", {"issue": _key}, dict({"issue": _key}, **_issue[_key])) else: _change = False for _item in ['issue_type','created','updated','users','status', 'landmark','point','agg_time','org_time', 'summary','spent_time','sprint','epic_link']: if _old_issue[_item] != _issue[_key][_item]: _log = {"issue_id": _key, "key": _item, "old": _old_issue[_item], "new": _issue[_key][_item]} self.write_log(_log) _change = True _change = True if _change: self.mongo_db.handler("issue", "update", {"issue": _key}, dict({"issue": _key}, **_issue[_key])) def get_issue_link(self): _link = self.get_link() print "---> get_issue_link: ", _link return _link def sync_issue_link(self): _key = u"%s" % self.show_name() _link = self.get_link() print "sync_issue_link: ", _link self.mongo_db.handler("issue_link", "update", {"issue": _key}, dict({"issue": _key}, **_link)) return _link def show_issue(self): """ 显示issue信息 :return: """ print(u"[%s]" % self.show_name()), print u"类型:%s" % self.get_type(), print(u'状态:%s' % self.get_status()), print u"里程碑:%s" % self.get_landmark() def get_users(self): """ 获取访问issue的用户 2018.3.1:改为 经办人 assignee :return: watcher = self.jira.watchers(self.issue) _user = u"%s" % (', '.join(watcher.displayName for watcher in watcher.watchers)) """ if type(self.issue.raw['fields']["assignee"]) is types.NoneType: return None return (u"%s" % self.issue.raw['fields']["assignee"]['displayName']).replace(' ', '') def write_log(self, info): print "---<write_log>---: ",info self.mongo_db.handler("log", "insert", info) def write_worklog(self, info): """ 写入或更新 工作日志记录。 :param info: 新的日志数据 :return: """ if not info.has_key("comment"): return _search = {'issue': info['issue'], 'id': info['id']} self.mongo_db.handler('worklog', 'update', _search, info) def clear_worklog(self, worklog_id): """ 清除"不存在"的记录! :param worklog_id: 存在的worklog_id :return: """ _set = {'timeSpentSeconds': 0} if len(worklog_id) > 0: _search = {"issue": self.show_name(), "id": {"$not": {"$in": worklog_id}}} else: _search = {"issue": self.show_name()} _one = self.mongo_db.handler('worklog', 'find_one', _search) if _one is None: return # self.mongo_db.handler('worklog', 'remove', _search) """保留原记录,将其用时值设置为0,以便事后跟踪 """ self.mongo_db.handler('worklog', 'update', _search, _set) def sync_worklog(self): """ 获取指定 issue 的工作日志记录。 - 2018.4.2:针对以前有的,但现在没有的 日志记录 的处理?!清除其spent时间 :return: """ worklogs = self.jira.worklogs(self.show_name()) wl = {} _id = [] for worklog in worklogs: wl['issue'] = self.show_name() wl['author'] = u'%s' % worklog.author wl['comment'] = u'%s' % worklog.comment wl['timeSpent'] = worklog.timeSpent wl['timeSpentSeconds'] = worklog.timeSpentSeconds wl['updated'] = worklog.updated wl['created'] = worklog.created wl['started'] = worklog.started wl['id'] = worklog.id _id.append(worklog.id) self.write_worklog(wl) """同时同步Issue的变动日志""" """ 因worklog可随意更改或删除, 有必要实时清除多余的记录!""" self.clear_worklog(_id) self.sync_changelog() def scan_task_by_sprint(self, sprint): """ 通过sprint获取Issue,以便获取它们的 工作日志 :param sprint: 当前的sprint名称 :return: Issue列表 """ jql_sql = u'project=%s AND Sprint = "%s" ORDER BY created DESC' %\ (self.name, sprint) total = 0 tasks = [] while True: issues = self.jira.search_issues(jql_sql, maxResults=100, startAt=total) for issue in issues: self.issue = issue """同步issue""" self.sync_issue() self.sync_worklog() self.sync_issue_link() tasks.append(self.show_name()) if len(issues) == 100: total += 100 else: break return tasks def scan_epic(self, bg_date): """ 扫描project收集epic信息 :param bg_date: 起始日期,如 2018-1-31 :param issue_type:指定issue类型 :return: 按issue类型进行统计值kv_sum,issue链kv_link,相关任务链task_link """ jql_sql = u'project=%s AND issuetype=epic AND created >= %s ORDER BY created DESC' % ( self.name, bg_date) total = 0 story_link = [] while True: issues = self.jira.search_issues(jql_sql, maxResults=100, startAt=total) for issue in issues: self.issue = issue """同步issue""" # self.show_issue() self.sync_issue() """收集epic相关的story和任务""" _jql = u'project=%s AND "Epic Link"=%s AND created >= %s ORDER BY created DESC' % \ (self.name, self.show_name(), bg_date) _link = self.get_epic_link(_jql) """同步epic的link""" # print "--> epic link: ", dict({"issue": self.show_name()}, **_link) self.mongo_db.handler("issue_link", "update", {"issue": self.show_name()}, dict({"issue": self.show_name()}, **_link)) story_link += _link[self.show_name()] if len(issues) == 100: total += 100 else: break return story_link def scan_story(self, bg_date): """ 按 project 获取其下所有 story 数据。 :param bg_date: 开始搜索的日期 :return: """ jql_sql = u'project=%s AND issuetype=story AND created >= %s ORDER BY created DESC' % ( self.name, bg_date) total = 0 task_link = [] while True: issues = self.jira.search_issues(jql_sql, maxResults=100, startAt=total) for issue in issues: self.issue = issue """同步issue""" # self.show_issue() self.sync_issue() """收集story相关的任务""" _link = self.sync_issue_link() """同步epic的link""" self.mongo_db.handler("issue_link", "update", {"issue": self.show_name()}, dict({"issue": self.show_name()}, **_link)) task_link += _link[self.show_name()] if len(issues) == 100: total += 100 else: break return task_link def scan_task(self, bg_date): """ 按 project 获取其下所有与执行相关的 issue 数据。 :param bg_date: 开始搜索的日期 :return: """ jql_sql = u'project=%s AND ( issuetype=task OR' \ u' issuetype=任务 OR' \ u' issuetype=故障 OR' \ u' issuetype=Bug OR' \ u' issuetype=Sub-task OR' \ u' issuetype=子任务 ) AND' \ u' created >= %s ORDER BY created DESC' % (self.name, bg_date) print jql_sql total = 0 task_link = [] while True: issues = self.jira.search_issues(jql_sql, maxResults=100, startAt=total) for issue in issues: self.issue = issue """同步issue""" self.show_issue() self.sync_issue() # self.sync_changelog() self.sync_worklog() task_link.append(self.show_name()) if len(issues) == 100: total += 100 else: break return task_link def sync_changelog(self): """ 获取指定 issue 的 变更日志记录 :return: """ issue = self.jira.issue(self.show_name(), expand='changelog') changelog = issue.changelog for history in changelog.histories: for item in history.items: _data = {'issue': self.show_name(), 'field': item.field, 'author': u"%s" % history.author, 'date': history.created, 'old': getattr(item, 'fromString'), 'new': getattr(item, 'toString') } if self.mongo_db.handler('changelog', 'find_one', _data) is None: self.mongo_db.handler('changelog', 'insert', _data)
class IssueManager: ## The constructor # @param self The object pointer # @param jiraURL The url of the JIRA instance # @param jiraUser The user name to use # @param jiraPassword The user password to use def __init__(self, jiraURL, jiraUser, jiraPassword): self.__jiraUser = jiraUser self.__jiraPassword = jiraPassword self.__jiraURL = jiraURL self.startSession() ## Create the connection # @param self Teh object pointer def startSession(self): #now make the connection options = { 'server':self.__jiraURL, 'verify':False } self.__jira = JIRA(options, basic_auth=(self.__jiraUser, self.__jiraPassword)) self.__greenhopper = GreenHopper(options, basic_auth=(self.__jiraUser, self.__jiraPassword)) ## Kill the jira connection # @param self The object pointer def killSession(self): self.__jira.kill_session() self.__greenhopper.kill_session() ## Add the epic link to an issue # @param self The object pointer # @param issue The issue ID # @param epic The epic ID def attachEpic(self, issue, epic): #attach the epic self.__greenhopper.add_issues_to_epic(epic, [issue]) ## Create an issue set by calling the createIssue and createSubtask methods # @param self The object pointer # @param project The project to add the issue to # @param name The summary of the issue # @param priority The priority of the issue # @param product The software product the requirement belongs to (e.g. Device, Apollo, Cloud) # @param components The components to add to the issues # @param version The fix version # @returns A dictionary of issues that were created def createIssueSet(self, project, name, priority, product, components, version): #dictionary to store jira issues issues = {} #set up the description description = '<h3>User Experience</h3><h3>Acceptance Criteria</h3><ul><li></li></ul>' #if we list more than one product that set the product flag to multiple productLabel = product if(';' in product): productLabel = 'Multiple' #create the parent issue parentID = self.createIssue(project, name, description, 'Story', priority, productLabel, components, version) issues[parentID] = '%s\t%s\t%s' % (parentID, 'Story', name) #create the tasks for development and testing depending on the product for specificProduct in product.split(';'): issue1 = self.createIssue(project, 'Implementation (%s): %s' % (specificProduct, name), '', 'Implement', priority, productLabel, components, version) issues[issue1] = '%s\t%s\t%s' % (issue1, 'Implement', name) issue2 = self.createIssue(project, 'Create Unit Tests (%s): %s' % (specificProduct, name), '', 'Unit Test', priority, productLabel, components, version) issues[issue2] = '%s\t%s\t%s' % (issue2, 'Unit Test', name) issue3 = self.createIssue(project, 'Verification (%s): %s' % (specificProduct, name), '', 'Verification Test', priority, productLabel, components, version) issues[issue3] = '%s\t%s\t%s' % (issue3, 'Verification Test', name) #create the links self.linkIssues(parentID, issue1, 'Develop') self.linkIssues(parentID, issue2, 'Verify') self.linkIssues(parentID, issue3, 'Verify') #print the ids return(parentID, issues) ## Create a new issue # @param self The object pointer # @param project The project to add the issue to # @param summary The summary of the issue # @param description The description of the issue # @param issueType The type of the issue # @param priority The priority of the issue # @param product The software product the requirement belongs to (e.g. Device, Apollo, Cloud) # @param components The components to add to the issue # @param version The fix version # @returns The JIRA issue identifier def createIssue(self, project, summary, description, issueType, priority, product, components, version): #create an issue by setting up the dictionary issueDict = { #'assignee': {'name':'Unassigned'}, 'project': {'key':project}, 'priority' : {'name':priority}, 'summary': summary, 'description': description, 'issuetype' : {'name':issueType}, 'labels':[ 'AddedViaAPI', 'APISetFixVersion' ], 'customfield_11100': {'value':product} } #if it is a story type then we want ot add a label for acceptance criteria too if issueType == 'Story': issueDict['labels'].append('NeedAcceptanceCriteria') #add the components if there are any if(not components == ''): issueDict['components'] = self.addComponents(components) #now create the issue print issueDict newIssue = self.__jira.create_issue(fields=issueDict) #return the id return(newIssue.key) ## Create a new epic # @param self The object pointer # @param project The project to add the issue to # @param summary The summary of the issue # @param description The description of the issue # @param issueType The type of the issue # @param priority The priority of the issue # @param product The software product the requirement belongs to (e.g. Device, Apollo, Cloud) # @param components The components to add to the issue # @param version The fix version # @returns The JIRA issue identifier def createEpic(self, project, summary, description, issueType, priority, product, components, version): #create an issue by setting up the dictionary issueDict = { #'project': {'key':project}, 'priority' : {'name':priority}, 'summary': summary, 'description': description, 'issuetype' : {'name':issueType}, 'labels':[ 'AddedViaAPI', 'APISetFixVersion' ], 'customfield_11100': {'value':product}, 'customfield_10401': summary } #add the components if there are any if(not components == ''): issueDict['components'] = self.addComponents(components) #now create the issue print issueDict newIssue = self.__greenhopper.create_issue(fields=issueDict) #return the id return(newIssue.key) ## Create a subtask issue # @param self The object pointer # @param project The project to add the issue to # @param summary The summary of the issue # @param description The description of the issue # @param issueType The type of the issue # @param priority The priority of the issue # @param product The software product the requirement belongs to (e.g. Device, Apollo, Cloud) # @param components The components to add to the task # @param version The fixVersion # @returns The JIRA issue identifier def createSubtask(self, project, parentID, summary, description, issueType, priority, product, components): #create an issue by setting up the dictionary issueDict = { 'parent':{'id':parentID}, 'project': {'key':project}, 'priority' : {'name':priority}, 'summary': summary, 'description': description, 'issuetype' : {'name':issueType}, 'labels':[ 'AddedViaAPI', 'APISetFixVersion' ], 'customfield_11100': {'value':product} } #add the components if there are any if(not components == ''): issueDict['components'] = self.addComponents(components) #now create the issue print issueDict newIssue = self.__jira.create_issue(fields=issueDict) #return the id return(newIssue.key) ## Link two issues # @param self The object pointer # @param jiraID1 The JIRA id of the first issue # @param jiraID2 The JIRA id of the second issue # @param linkType The type of connect def linkIssues(self, jiraID1, jiraID2, linkType): #now link the two issues print "Linking %s and %s" % (jiraID1, jiraID2) self.__jira.create_issue_link(type=linkType, inwardIssue=jiraID2, outwardIssue=jiraID1) ## Create an array from a ";"-separated list, used for populating components # @param self The object pointer # @param componentString The string to be parsed # @returns The array of components def addComponents(self, componentString): tokens = componentString.split(';') components = [] #populate the array for token in tokens: components.append( {'name':token} ) return(components)
class JiraAgent: def __init__(self, server_url, username, password): self._server_url = server_url self._username = username self._password = password self._jira_client = None def connect(self): self._jira_client = GreenHopper(basic_auth=(self._username, self._password), options={'server': self._server_url}) def retrieve_sprint_id(self, board_id, sprint_name): sprints_dto = self._jira_client.sprints(board_id) for sprint_dto in sprints_dto: if sprint_dto.name.strip() == sprint_name: return sprint_dto.id return '' def retrieve_sprint_info(self, sprint_id): response = requests.get( F'{self._server_url}/rest/agile/1.0/sprint/{sprint_id}', auth=(self._username, self._password)) response_json = response.json() start_date = response_json['startDate'][:10] end_date = response_json['completeDate'][:10] return SprintInfo(start_date, end_date) def download_work_log_of_sprint(self, sprint_id): query = F'sprint={sprint_id} AND (type="Task" OR type="Sub-task" OR type="Bug") AND status="Done"' issues_dto = self._jira_client.search_issues(query, maxResults=160) return self._download_details_of_issues(issues_dto) def _download_details_of_issues(self, issues_dto): issues = [] progress_bar = tqdm(total=issues_dto.total) for issue_dto in issues_dto: detailed_issue_dto = self._jira_client.issue(issue_dto.key) work_log = self._collect_work_log( detailed_issue_dto.fields.worklog.worklogs) time_data = IssueTimeData( issue_dto.fields.timeoriginalestimate or 0, issue_dto.fields.aggregatetimespent or 0, work_log) issue = Issue(issue_dto.key, issue_dto.fields.summary, issue_dto.permalink(), time_data) issues.append(issue) progress_bar.update(1) progress_bar.close() return issues def _collect_work_log(self, work_logs_dto): work_log_items = [] for work_log_dto in work_logs_dto: author = work_log_dto.author.displayName comment = work_log_dto.comment creation_date = work_log_dto.created spent_time = work_log_dto.timeSpentSeconds work_log_item = WorkLogItem(author, creation_date, spent_time, comment) work_log_items.append(work_log_item) return work_log_items