def createcomponent(): print "start to create component in jira" fname = "demo3.xls" bk = xlrd.open_workbook(fname) #获取用户名密码 try: login = bk.sheet_by_name("login") except: print "no sheet in %s named login , can't login jira without it" % fname rowl = login.row_values(1) jira = JIRA('http://172.16.3.139:80/', basic_auth=('admin', 'admin')) project = 'PSR' #获取模块信息 try: sh = bk.sheet_by_name("info") except: print "no sheet in %s named info" % fname nrows = sh.nrows ncols = sh.ncols print "nrows %d, ncols %d" % (nrows, ncols) #创建模块 for i in range(1, nrows): rowd = sh.row_values(i) try: new_component = jira.create_component( rowd[0], project, description=rowd[1], leadUserName=rowd[2], assigneeType='COMPONENT_LEAD', isAssigneeTypeValid=True) print new_component except BaseException, e: print "create error in %d" % (i + 1) e = str(e).split(":")[-1].strip() cmd = "cat %s |tail -1" % (e) faillog = commands.getoutput(cmd) print faillog
if month in [1, 3, 5, 7, 8, 10, 12]: days = 31 if month == 2: days = 29 prefix = "" if month < 10: prefix = "0" monthStr = "{0}{1}".format(prefix, month) version = "{0}.{1}".format(year,monthStr) endDate = "{0}{1}-{2}-{3}".format("20", year, monthStr, days) createVersions and jira.create_version(version, projectKey, releaseDate=endDate) # Creacion de componentes for component in componentsToCreate(projectKey, componentsList): createComponents and jira.create_component(component, projectKey) print("------------>> " + component) # Eliminacion de componentes for myComponent in gettingComponentes(projectKey): deleteComponents and jira.delete_component(myComponent.id) # Obtener descripcion aleatoria def getIssueDescription(): return " ".join([random.choice(i) for i in l]) # Obtener array de componentes def getComponents(): numberOfComponents = random.sample([1, 1, 2], 1)[0] myComponents = random.sample(["db", "redis", "tutorial", "result", "vote", "worker"], numberOfComponents) arrayComponents = []
class JiraTool(): def __init__(self, server, username, password, maxResults = 500): self.server = server self.basic_auth = (username, password) # issues查询的最大值 self.maxResults = maxResults def login(self): self.jira = JIRA(server=self.server, basic_auth=self.basic_auth) if self.jira == None: print('连接失败') sys.exit(-1) def get_projects(self): """ 获得jira 的所有项目 :return: """ return [(p.key, p.name, p.id) for p in self.jira.projects()] def get_components(self, project): """ 获得某项目的所有模块 :param project: :return: """ return [(c.name, c.id) for c in self.jira.project_components(self.jira.project(project))] def create_component(self, project, compoment, description, leadUserName=None, assigneeType=None, isAssigneeTypeValid=False): """ # 创建项目模块 :param project: 模块所属项目 :param compoment:模块名称 :param description:模块描述 :param leadUserName: :param assigneeType: :param isAssigneeTypeValid: :return: """ components = self.jira.project_components(self.jira.project(project)) if compoment not in [c.name for c in components]: self.jira.create_component(compoment, project, description=description, leadUserName=leadUserName, assigneeType=assigneeType, isAssigneeTypeValid=isAssigneeTypeValid) def create_issue(self, project, compoment, summary, description, assignee, issuetype, priority='Medium'): """ 创建提交bug :param project: 项目 :param issuetype: 问题类型,Task :param summary: 主题 :param compoment: 模块 :param description: 描述 :param assignee: 经办人 :param priority: 优先级 :return: """ issue_dict = { 'project': {'key': project}, 'issuetype': {'id': issuetype}, 'summary': summary, 'components': [{'name': compoment}], 'description': description, 'assignee': {'name': assignee}, 'priority': {'name': priority}, } return self.jira.create_issue(issue_dict) def delete_issue(self, issue): """ 删除bug :param issue: :return: """ issue.delete() def update_issue_content(self, issue, issue_dict): """ 更新bug内容 :param issue: :param issue_dict: issue_dict = { 'project': {'key': project}, 'issuetype': {'id': issuetype}, 'summary': summary, 'components': [{'name': compoment}], 'description': description, 'assignee': {'name': assignee}, 'priority': {'name': priority}, } :return: """ issue.update(fields=issue_dict) def update_issue_issuetype(self, issue, issuetype): """ 更新bug 状态 :param issue: :param issuetype: 可以为id值如11,可以为值如'恢复开启问题' :return: """ transitions = self.jira.transitions(issue) # print([(t['id'], t['name']) for t in transitions]) self.jira.transition_issue(issue, issuetype) def search_issues(self, jql): """ 查询bug :param jql: 查询语句, 如"project=项目key AND component = 模块 AND status=closed AND summary ~标题 AND description ~描述" :return: """ try: # maxResults参数是设置返回数据的最大值,默认是50。 issues = self.jira.search_issues(jql, maxResults=self.maxResults) except Exception as e: print(e) sys.exit(-1) return issues def search_issue_content(self, issue, content_type): """ 获取bug 的相关信息 :param issue: :param content_type:项目project; 模块名称components; 标题summary; 缺陷类型issuetype; 具体描述内容description; 经办人assignee; 报告人reporter; 解决结果resolution; bug状态status; 优先级priority; 创建时间created; 更新时间updated; 评论comments :return: """ # 评论 if content_type == 'comments': return [c.body for c in self.jira.comments(issue)] if hasattr(issue.fields, content_type): result = getattr(issue.fields, content_type) if isinstance(result, list): return [c.name for c in result if hasattr(c, 'name')] return result def collect_bug_report(self, project, root_path): """ 搜集bug报告上模块信息 :param project: :param root_path: :return: """ import os statistics_path = root_path + '/BugReport/statistics/' reports_path = root_path + '/BugReport/reports/' + project + '/' if not os.path.exists(statistics_path): os.makedirs(statistics_path) if not os.path.exists(reports_path): os.makedirs(reports_path) text = 'id, Key, Type, Status, Resolution, Priority, components, AffectsVersions, FixedVersions, Reporter, ' \ 'Creator, Assignee, CreatedDate, ResolutionDate, UpdatedDate, Summary\n' search_str = 'project = ' + project.upper() + ' AND issuetype = Bug AND status in (Resolved, Closed) AND ' \ 'resolution in (Fixed, Resolved) ORDER BY key ASC' start = 0 max_results_each_search = 1000 while True: try: # maxResults参数是设置返回数据的最大值,默认是50。 issues = self.jira.search_issues(search_str, startAt=start, maxResults=max_results_each_search) except Exception as e: print(e) sys.exit(-1) for issue in issues: text += issue.id + ',' text += issue.key + ',' text += issue.fields.issuetype.name + ',' text += issue.fields.status.name + ',' text += issue.fields.resolution.name + ',' priority_name = 'Unassigned' if issue.fields.priority is None else issue.fields.priority.name text += priority_name + ',' components = issue.fields.components if len(components) > 0: for component in components: text += component.name + '|' text += ',' if hasattr(issue.fields, 'versions'): for version in issue.fields.versions: text += version.name + '|' text += ',' for version in issue.fields.fixVersions: text += version.name + '|' text += ',' reporterName = 'Unassigned' if issue.fields.reporter is None else issue.fields.reporter.displayName creatorName = 'Unassigned' if issue.fields.creator is None else issue.fields.creator.displayName assigneeName = 'Unassigned' if issue.fields.assignee is None else issue.fields.assignee.displayName text += reporterName + ',' text += creatorName + ',' text += assigneeName + ',' text += issue.fields.created + ',' text += issue.fields.resolutiondate + ',' text += issue.fields.updated + ',' text += issue.fields.summary + ',' # 输出报告内容 with open(reports_path + issue.key + '.txt', 'w', encoding='utf-8') as fw: description = 'Summary:\n' + issue.fields.summary + '\nDescription:\n' description += '' if issue.fields.description is None else issue.fields.description fw.write(description) fw.close() text += '\n' start += max_results_each_search print(start) if start > issues.total: break # 输出项目统计信息 with open(statistics_path + project + '.csv', 'w', encoding='utf-8') as fw: fw.write(text) fw.close() print('The collection for bug reports of Project ' + project + ' has finished!')
###Get 'raw' lists (Jira classes are not so handy) listSrc = [] listDst = [] for i in projSrc: if cMatch.match(str(i)): listSrc.append(str(i)) for i in projDst: if cMatch.match(str(i)): listDst.append(str(i)) ###Get absent strings toAdd = [] for item in listSrc: if item not in listDst: toAdd.append(item) ####Add absent strings to project components. if toAdd != []: for item in toAdd: print("Adding absent component:", item) jira.create_component(name=item, project=args.dest, description='Business Component', assigneeType='UNASSIGNED') else: print("Nothing to add.")
class Strategy: def __init__(self, account_name, user_name, user_password): self._account = account_name self._user = user_name self._password = user_password self._server = 'https://{}.atlassian.net'.format(self._account) self._jira_connection = JIRA(server=self._server, basic_auth=(self._user, self._password)) self._makelog = makelog.Makelog('output', 'errorlog') def execute(self, key): if key == 1: self._doreporting() elif key == 2: self._domailing() elif key == 3: self._dogenerating() else: return False def _doreporting(self): data_peruser = {} data_percomponent = {} # getting all users users_all = self._jira_connection.search_users('%', maxResults=False, includeInactive=True) for user in users_all: data_peruser[user.name] = { 'time_total': 0, 'time_perissue': {}, 'actual_name': user.displayName, 'components': set() } # getting all components components_all = set() projects_all = self._jira_connection.projects() for project in projects_all: try: comps = self._jira_connection.project_components(project) components_all.update(comps) except: outstr = "Unexpected error with getting components from project: {}\n".format( project.key) self._makelog.putto_console(outstr) self._makelog.putto_errorlog(outstr, traceback.format_exc()) for comp in components_all: try: component_data = self._jira_connection.component(comp.id) data_percomponent[component_data.id] = { 'name': component_data.name, 'projectkey': component_data.project, 'time_total': 0, 'time_perissue': {}, 'lead': '' if not hasattr(component_data, 'lead') else component_data.lead.name } if hasattr(component_data, 'lead'): data_peruser[component_data.lead.name]['components'].add( component_data.id) except: outstr = "Unexpected error with getting data of component id: {}\n".format( comp.id) self._makelog.putto_console(outstr) self._makelog.putto_errorlog(outstr, traceback.format_exc()) # counting hours logic issues_all = self._jira_connection.search_issues('', maxResults=False) for iss in issues_all: try: iss_works = self._jira_connection.worklogs(iss) for work in iss_works: # per user data_peruser[work.author. name]['time_total'] += work.timeSpentSeconds if iss.key not in data_peruser[ work.author.name]['time_perissue']: data_peruser[work.author.name]['time_perissue'][ iss.key] = 0 data_peruser[work.author.name]['time_perissue'][ iss.key] += work.timeSpentSeconds # per valid component (with lead) for comp in iss.fields.components: if data_percomponent[ comp.id]['lead'] == work.author.name: data_percomponent[ comp.id]['time_total'] += work.timeSpentSeconds if iss.key not in data_percomponent[ comp.id]['time_perissue']: data_percomponent[comp.id]['time_perissue'][ iss.key] = 0 data_percomponent[comp.id]['time_perissue'][ iss.key] += work.timeSpentSeconds except: outstr = "Unexpected error counting hours with issue: {}\n".format( iss.key) self._makelog.putto_console(outstr) self._makelog.putto_errorlog(outstr, traceback.format_exc()) # outputting data outstr = "" outstr += "\t\t\tReport on the spent hours:\n" outstr += "\n\t\tPer programmer:\n\n" for user_name, user_dat in data_peruser.iteritems(): outstr += "-> Name: {} ({})\n".format(user_dat['actual_name'], user_name) outstr += " Total time: {} hour(s)\n".format( str(user_dat['time_total'] / 3600)) outstr += " Time per issue:\n" for iss_key, time_val in user_dat['time_perissue'].iteritems(): outstr += "\t{} is: {} hour(s)\n".format( iss_key, str(time_val / 3600)) outstr += "\n" outstr += "\n\t\tPer component (with lead only):\n\n" for comp_id, comp_dat in data_percomponent.iteritems(): outstr += "-> Name: {} ({})\n".format(comp_dat['name'], comp_dat['projectkey']) outstr += " Lead: {}\n".format(comp_dat['lead']) outstr += " Total time: {} hour(s)\n".format( str(comp_dat['time_total'] / 3600)) outstr += " Time per issue:\n" for iss_key, time_val in comp_dat['time_perissue'].iteritems(): outstr += "\t{} is: {} hour(s)\n".format( iss_key, str(time_val / 3600)) outstr += "\n" outstr += "\n-----> END REPORT <-----\n\n" self._makelog.putto_console(outstr, iscln=True) self._makelog.putto_file(outstr) def _domailing(self): issues_tonotify = [] issues_all = self._jira_connection.search_issues('', maxResults=False) for iss in issues_all: try: iss_data = self._jira_connection.issue(iss) if (iss_data.fields.timeestimate is None) or (len( iss_data.fields.components) == 0): issues_tonotify.append({ 'name': iss_data.fields.assignee.name, 'dispname': iss_data.fields.assignee.displayName, 'email': iss_data.fields.assignee.emailAddress, 'isskey': iss.key }) except: outstr = "Unexpected error with getting issue: {}\n".format( iss.key) self._makelog.putto_console(outstr) self._makelog.putto_errorlog(outstr, traceback.format_exc()) for data in issues_tonotify: try: url = "{}/rest/api/2/issue/{}/notify".format( self._server, data['isskey']) notify_data = { "subject": "You have some incomplete fields in issue {}".format( data['isskey']), "textBody": "Your got this notification because have one or couple incomplete fields in {} issue. Note, that 'estimates' \ and 'component' fields are mandatory. Please, check this fields and fill its in if need." .format(data['isskey']), "to": { "users": [{ "name": data['name'] }] }, } requests.post(url, auth=(self._user, self._password), json=notify_data) outstr = "Successfully sending notification to:\n-> {} {} about incomplete fields in {} issue\n".format( data['dispname'], data['email'], data['isskey']) self._makelog.putto_console(outstr) self._makelog.putto_file(outstr) except: outstr = "Unexpected error with sending notification to:\n-> {} {} about: {}\n".format( data['dispname'], data['email'], data['isskey']) self._makelog.putto_console(outstr) self._makelog.putto_errorlog(outstr, traceback.format_exc()) if len(issues_tonotify) == 0: self._makelog.putto_console( "All tested issues were filed in correct") def _dogenerating(self): names_base = namebase.Namebase() maxlen_projname = 10 content_count = { 'project': 1, 'user': 1, 'component': 2, 'issue': 10, 'worklog': 20 } # making projects for i in xrange(content_count['project']): newname = names_base.getname_project() parts = newname.split()[::2] newkey = string.join( (parts[0][:(maxlen_projname - len(parts[1]))], parts[1]), '') try: self._jira_connection.create_project(newkey, name=newname) outstr = "Project {} was successfully created\n".format(newkey) self._makelog.putto_console(outstr) self._makelog.putto_file(outstr) except: outstr = "Some problem with project {} creation\n".format( newkey) self._makelog.putto_console(outstr) self._makelog.putto_errorlog(outstr, traceback.format_exc()) # making users for i in xrange(content_count['user']): newname = names_base.getname_user() try: self._jira_connection.add_user(newname, "{}@mail.net".format(newname),\ fullname="Name {}{}".format(string.upper(newname[:1]), newname[1:])) outstr = "User {} was successfully created\n".format(newname) self._makelog.putto_console(outstr) self._makelog.putto_file(outstr) except: outstr = "Some problem with user {} creation\n".format(newname) self._makelog.putto_console(outstr) self._makelog.putto_errorlog(outstr, traceback.format_exc()) # getting all valid project keys projects_keys = [] projects_all = self._jira_connection.projects() for project in projects_all: projects_keys.append(project.key) # getting all valid user names users_keys = [] users_all = self._jira_connection.search_users('%', maxResults=False, includeInactive=True) for user in users_all: users_keys.append(user.name) # making components for i in xrange(content_count['component']): newname = names_base.getname_component() try: self._jira_connection.create_component( newname, random.choice(projects_keys), leadUserName=random.choice(users_keys)) outstr = "Component {} was successfully created\n".format( newname) self._makelog.putto_console(outstr) self._makelog.putto_file(outstr) except: outstr = "Some problem with component {} creation\n".format( newname) self._makelog.putto_console(outstr) self._makelog.putto_errorlog(outstr, traceback.format_exc()) # making issues for i in xrange(content_count['issue']): newname = names_base.getname_issue() fields = { "project": { "key": random.choice(projects_keys) }, "summary": "Here should be some random text summary for issue {}".format( newname), "description": "Here should be some random text description for issue {}". format(newname), "issuetype": { "name": random.choice( ("Bug", "Improvement", "Task", "Epic", "New Feature")) }, "assignee": { "name": random.choice(users_keys) }, "timetracking": { "originalEstimate": "{}w {}d {}h".format(random.randint(1, 3), random.randint(1, 4), random.randint(1, 7)), "remainingEstimate": "{}d {}h".format(random.randint(1, 4), random.randint(1, 7)) } } try: self._jira_connection.create_issue(fields=fields) outstr = "Issue {} was successfully created\n".format(newname) self._makelog.putto_console(outstr) self._makelog.putto_file(outstr) except: outstr = "Some problem with issue {} creation\n".format( newname) self._makelog.putto_console(outstr) self._makelog.putto_errorlog(outstr, traceback.format_exc()) # making worklogs issues_all = self._jira_connection.search_issues('', maxResults=False) for i in xrange(content_count['worklog']): iss = random.choice(issues_all) try: self._jira_connection.add_worklog(iss, timeSpent="{}h".format(random.randint(1, 3)), user=random.choice(users_keys),\ comment="Here should be some random text about work on this issue") outstr = "Worklog for issue {} was successfully created\n".format( iss.key) self._makelog.putto_console(outstr) self._makelog.putto_file(outstr) except: outstr = "Some problem with worklog creation for issue {}\n".format( iss.key) self._makelog.putto_console(outstr) self._makelog.putto_errorlog(outstr, traceback.format_exc())
class JiraTool(object): def __init__(self, server, username, password, maxResults=50): self.server = server self.basic_auth = (username, password) # issues查询的最大值 self.maxResults = maxResults def login(self): self.jira = JIRA(server=self.server, basic_auth=self.basic_auth) if self.jira == None: print('连接失败') sys.exit(-1) def get_projects(self): """ 获得jira 的所有项目 :return: """ return [(p.key, p.name, p.id) for p in self.jira.projects()] # def get_components(self, project): # """ # 获得某项目的所有模块 # :param project: # :return: # """ # return [(c.name, c.id) for c in self.jira.project_components(self.jira.project(project))] def create_component(self, project, compoment, description, leadUserName=None, assigneeType=None, isAssigneeTypeValid=False): """ # 创建项目模块 :param project: 模块所属项目 :param compoment:模块名称 :param description:模块描述 :param leadUserName: :param assigneeType: :param isAssigneeTypeValid: :return: """ components = self.jira.project_components(self.jira.project(project)) if compoment not in [c.name for c in components]: self.jira.create_component(compoment, project, description=description, leadUserName=leadUserName, assigneeType=assigneeType, isAssigneeTypeValid=isAssigneeTypeValid) def create_issue(self, project, compoment, summary, description, assignee, issuetype, priority='Medium'): """ 创建提交issue :param project: 项目 :param issuetype: 问题类型,Task :param summary: 主题 :param compoment: 模块 :param description: 描述 :param assignee: 经办人 :param priority: 优先级 :return: """ issue_dict = { 'project': { 'key': project }, 'issuetype': { 'id': issuetype }, 'summary': summary, 'components': [{ 'name': compoment }], 'description': description, 'assignee': { 'name': assignee }, 'priority': { 'name': priority }, } return self.jira.create_issue(issue_dict) def delete_issue(self, issue): """ 删除issue :param issue: :return: """ issue.delete() def update_issue_content(self, issue, issue_dict): """ 更新issue内容 :param issue: :param issue_dict: issue_dict = { 'project': {'key': project}, 'issuetype': {'id': issuetype}, 'summary': summary, 'components': [{'name': compoment}], 'description': description, 'assignee': {'name': assignee}, 'priority': {'name': priority}, } :return: """ issue.update(fields=issue_dict) def update_issue_issuetype(self, issue, issuetype): """ 更新bug 状态 :param issue: :param issuetype: 可以为id值如11,可以为值如'恢复开启问题' :return: """ transitions = self.jira.transitions(issue) # print([(t['id'], t['name']) for t in transitions]) self.jira.transition_issue(issue, issuetype) def search_all_issue(self, jql): block_size = 100 block_num = 0 issues = [] while True: start_idx = block_num * block_size part_issues = self.jira.search_issues(jql, start_idx, block_size) if len(part_issues) == 0: break block_num += 1 issues.extend(part_issues) return issues # def search_issues(self, jql): # """ # 查询bug # :param jql: 查询语句,如"project=项目key AND component = 模块 AND status=closed AND summary ~标题 AND description ~描述" # :return: # """ # try: # # maxResults参数是设置返回数据的最大值,默认是50。 # issues = self.jira.search_issues(jql, maxResults=self.maxResults) # except Exception as e: # print(e) # sys.exit(-1) # return issues def search_issue_content(self, issue, content_type): """ 获取issue 的相关信息 :param issue: :param content_type:项目project; 模块名称components; 标题summary; 缺陷类型issuetype; 具体描述内容description; 经办人assignee; 报告人reporter; 解决结果resolution; bug状态status; 优先级priority; 创建时间created; 更新时间updated; 评论comments :return: """ # 评论 if content_type == 'comments': return [c.body for c in self.jira.comments(issue)] if hasattr(issue.fields, content_type): result = getattr(issue.fields, content_type) if isinstance(result, list): return [c.name for c in result if hasattr(c, 'name')] return result def get_issue_types(self): """ 获取所有issues类型 :return: """ issue_type_name = [] issue_types = self.jira.issue_types() for issue_type in issue_types: issue_type_name.append(issue_type.name) return issue_type_name def get_components(self, issue): """ 获取组件字段 :param issue: 每个issue :return: """ for i in issue.fields.components: components = i.name return components def get_epic_link(self, issue): """ 获取epic_link字段 :param issue: 每个issue :return: """ for i in issue.fields.customfield_10900: epic_link = i.name return epic_link def make_cse_env_num(self, cse_num): try: res = cse_num.split('-') num = int(res[1]) + 1 cse_env_nu = 'CSE-' + str(num) except Exception as e: cse_env_nu = '' return cse_env_nu def get_result(self, issue): """ 获取处理结果的字段 如 产品问题、环境问题 :param issue: :return: """ if issue.fields.customfield_11100: result = issue.fields.customfield_11100.value else: result = '' return result def get_resolve_time(self, issue): try: resolve_time = issue.fields.resolutiondate[0:19] except Exception as e: print(e) resolve_time = '' print(resolve_time) return resolve_time
class ViraAPI(): ''' This class gets imported by __init__.py ''' def __init__(self): ''' Initialize vira ''' # Load user-defined config files file_servers = vim.eval('g:vira_config_file_servers') file_projects = vim.eval('g:vira_config_file_projects') try: self.vira_servers = load_config(file_servers) self.vira_projects = load_config(file_projects) except: print(f'Could not load {file_servers} or {file_projects}') self.userconfig_filter_default = { 'assignee': '', 'component': '', 'fixVersion': '', 'issuetype': '', 'priority': '', 'project': '', 'reporter': '', 'status': '', "'Epic Link'": '', 'statusCategory': ['To Do', 'In Progress'], 'text': '' } self.reset_filters() self.userconfig_newissue = { 'assignee': '', 'component': '', 'fixVersion': '', 'issuetype': 'Bug', 'priority': '', 'epics': '', 'status': '', } self.users = set() self.versions = set() self.servers = set() self.users_type = '' self.async_count = 0 self.versions_hide(True) def _async(self, func): try: func() except: pass def _async_vim(self): # TODO: VIRA-247 [210223] - Clean-up vim variables in python _async try: if len(vim.eval('s:versions')) == 0: vim.command('let s:projects = s:projects[1:]') if len(vim.eval('s:projects')) == 0: # TODO: VIRA-247 [210223] - Check for new projects and versions and start PRIORITY ranking for updates vim.command('let s:vira_async_timer = g:vira_async_timer') self.get_projects() self.get_versions() else: # self.version_percent(str(vim.eval('s:projects[0]')), str(vim.eval('s:versions[0]'))) vim.command('let s:versions = s:versions[1:]') if self.async_count == 0 and vim.eval( 's:vira_async_timer') == 10000: self.users = self.get_users() self.async_count = 1000 self.async_count -= 1 except: pass def create_issue(self, input_stripped): ''' Create new issue in jira ''' section = { 'summary': parse_prompt_text(input_stripped, '*Summary*', 'Description'), 'description': parse_prompt_text(input_stripped, 'Description', '*Project*'), 'project': parse_prompt_text(input_stripped, '*Project*', '*IssueType*'), 'issuetype': parse_prompt_text(input_stripped, '*IssueType*', 'Status'), 'status': parse_prompt_text(input_stripped, 'Status', 'Priority'), 'priority': parse_prompt_text(input_stripped, 'Priority', 'Component'), 'components': parse_prompt_text(input_stripped, 'Component', 'Version'), 'fixVersions': parse_prompt_text(input_stripped, 'Version', 'Assignee'), 'assignee': parse_prompt_text(input_stripped, 'Assignee'), } # Check if required fields was entered by user if section['summary'] == '' or section['project'] == '' or section[ 'issuetype'] == '': return issue_kwargs = { 'project': section['project'], 'summary': section['summary'], 'description': section['description'], 'issuetype': { 'name': section['issuetype'] }, 'priority': { 'name': section['priority'] }, 'components': [{ 'name': section['components'] }], 'fixVersions': [{ 'name': section['fixVersions'] }], 'assignee': { 'name': section['assignee'] }, } # Jira API doesn't accept empty fields for certain keys for key in issue_kwargs.copy().keys(): if section[key] == '': issue_kwargs.pop(key) # Create issue and transition issue_key = self.jira.create_issue(**issue_kwargs) if section['status'] != '': self.jira.transition_issue(issue_key, section['status']) jira_server = vim.eval('g:vira_serv') print(f'Added {jira_server}/browse/{issue_key}') def add_worklog(self, issue, timeSpentSeconds, comment): ''' Calculate the offset for the start time of the time tracking ''' earlier = datetime.now() - datetime.timedelta(seconds=timeSpentSeconds) self.jira.add_worklog(issue=issue, timeSpentSeconds=timeSpentSeconds, comment=comment, started=earlier) def connect(self, server): ''' Connect to Jira server with supplied auth details ''' self.users = set() self.versions = set() self.users_type = '' try: # Specify whether the server's TLS certificate needs to be verified if self.vira_servers[server].get('skip_cert_verify'): urllib3.disable_warnings( urllib3.exceptions.InsecureRequestWarning) cert_verify = False else: cert_verify = True # Get auth for current server username = self.vira_servers[server].get('username') password_cmd = self.vira_servers[server].get('password_cmd') if password_cmd: password = run_command(password_cmd)['stdout'].strip().split( '\n')[0] else: password = self.vira_servers[server]['password'] except: cert_verify = True server = vim.eval('input("server: ")') vim.command('let g:vira_serv = "' + server + '"') username = vim.eval('input("username: "******"password: "******"' + server + '"') # Authorize self.jira = JIRA(options={ 'server': server, 'verify': cert_verify, }, basic_auth=(username, password), timeout=2, async_=True, max_retries=2) # Initial list updates self.users = self.get_users() self.get_projects() self.get_versions() vim.command('echo "Connection to jira server was successful"') except JIRAError as e: if 'CAPTCHA' in str(e): vim.command( 'echo "Could not log into jira! Check authentication details and log in from web browser to enter mandatory CAPTCHA."' ) else: # vim.command('echo "' + str(e) + '"') vim.command('let g:vira_serv = ""') # raise e except: vim.command('let g:vira_serv = ""') vim.command( 'echo "Could not log into jira! See the README for vira_server.json information"' ) def filter_str(self, filterType): ''' Build a filter string to add to a JQL query The string will look similar to one of these: AND status in ('In Progress') AND status in ('In Progress', 'To Do') ''' if self.userconfig_filter.get(filterType, '') == '': return selection = str( self.userconfig_filter[filterType]).strip('[]') if type( self.userconfig_filter[filterType] ) == list else self.userconfig_filter[filterType] if type( self.userconfig_filter[filterType] ) == tuple else "'" + self.userconfig_filter[filterType] + "'" return str(f"{filterType} in ({selection})").replace( "'None'", "Null").replace("'Unassigned'", "Null").replace( "'currentUser'", "currentUser()").replace( "'currentUser()'", "currentUser()").replace( "'currentuser'", "currentUser()").replace( "'currentuser()'", "currentUser()").replace("'null'", "Null").replace( f"text in ({selection})", f"text ~ {selection}") def get_assign_issue(self): ''' Menu to select users ''' self.print_users() def get_assignees(self): ''' Get my issues with JQL ''' self.print_users() def get_comments(self, issue): ''' Get all the comments for an issue ''' # Get the issue requested issues = self.jira.search_issues('issue = "' + issue.key + '"', fields='summary,comment', json_result='True') # Loop through all of the comments comments = '' for comment in issues["issues"][0]["fields"]["comment"]["comments"]: comments += (f"{comment['author']['displayName']}" + ' | ', f"{comment['updated'][0:10]}" + ' @ ', f"{comment['updated'][11:16]}" + ' | ', f"{comment['body']} + '\n'") return comments def get_components(self): ''' Build a vim pop-up menu for a list of components ''' for component in self.jira.project_components( self.userconfig_filter['project']): print(component.name) print('None') def get_component(self): ''' Build a vim pop-up menu for a list of components ''' self.get_components() def get_epic(self): self.get_epics() def get_epics(self): ''' Get my issues with JQL ''' hold = dict(self.userconfig_filter) project = self.userconfig_filter['project'] self.reset_filters() self.userconfig_filter["issuetype"] = "Epic" self.userconfig_filter["project"] = project self.get_issues() print('None') self.userconfig_filter = hold def get_issue(self, issue): ''' Get single issue by issue id ''' return self.jira.issue(issue) def get_issues(self): ''' Get my issues with JQL ''' issues = [] key_length = 0 summary_length = 0 issuetype_length = 0 status_length = 4 user_length = 0 for issue in self.query_issues(): fields = issue['fields'] user = str(fields['assignee']['displayName']) if type( fields['assignee']) == dict else 'Unassigned' user_length = len(user) if len(user) > user_length else user_length key_length = len( issue['key']) if len(issue['key']) > key_length else key_length summary = fields['summary'] summary_length = len( summary) if len(summary) > summary_length else summary_length issuetype = fields['issuetype']['name'] issuetype_length = len(issuetype) if len( issuetype) > issuetype_length else issuetype_length status = fields['status']['name'] status_length = len( status) if len(status) > status_length else status_length issues.append([ issue['key'], fields['summary'], fields['issuetype']['name'], fields['status']['name'], user ]) # Add min/max limits on summary length columns = vim.eval("&columns") min_summary_length = 25 max_summary_length = int( columns) - key_length - issuetype_length - status_length - 28 summary_length = min_summary_length if max_summary_length < min_summary_length else max_summary_length if summary_length > max_summary_length else summary_length for issue in issues: print(('{: <' + str(key_length) + '}').format(issue[0]) + " │ " + ('{: <' + str(summary_length) + '}').format(issue[1][:summary_length]) + " │ " + ('{: <' + str(issuetype_length) + '}').format(issue[2]) + " │ " + ('{: <' + str(status_length) + '}').format(issue[3]) + ' │ ' + issue[4]) def get_issuetypes(self): ''' Get my issues with JQL ''' for issuetype in self.jira.issue_types(): print(issuetype) def get_issuetype(self): ''' Get my issues with JQL ''' for issuetype in self.jira.issue_types(): print(issuetype) def get_priorities(self): ''' Get my issues with JQL ''' for priority in self.jira.priorities(): print(priority) def print_projects(self): ''' Build a vim pop-up menu for a list of projects ''' all_projects = self.get_projects() batch_size = 10 project_batches = [ all_projects[i:i + batch_size] for i in range(0, len(all_projects), batch_size) ] for batch in project_batches: projects = self.jira.createmeta(projectKeys=','.join(batch), expand='projects')['projects'] [print(p['key'] + ' ~ ' + p['name']) for p in projects] def get_projects(self): ''' Build a vim pop-up menu for a list of projects ''' # Project filter for version list self.projects = [] for project in self.jira.projects(): self.projects.append(str(project)) vim.command('let s:projects = ' + str(self.projects)) return self.projects def get_priority(self): ''' Build a vim pop-up menu for a list of projects ''' self.get_priorities() def get_prompt_text(self, prompt_type, comment_id=None): ''' Get prompt text used for inputting text into jira ''' self.prompt_type = prompt_type # Edit filters if prompt_type == 'edit_filter': self.prompt_text_commented = '\n# Edit all filters in JSON format' self.prompt_text = json.dumps( self.userconfig_filter, indent=True) + self.prompt_text_commented return self.prompt_text # Edit summary active_issue = vim.eval("g:vira_active_issue") if prompt_type == 'summary': self.prompt_text_commented = '\n# Edit issue summary' summary = self.jira.search_issues( 'issue = "' + active_issue + '"', fields=','.join(['summary']), json_result='True')['issues'][0]['fields']['summary'] self.prompt_text = summary + self.prompt_text_commented return self.prompt_text # Edit description if prompt_type == 'description': self.prompt_text_commented = '\n# Edit issue description' description = self.jira.search_issues( 'issue = "' + active_issue + '"', fields=','.join(['description']), json_result='True')['issues'][0]['fields'].get('description') if description: description = description.replace('\r\n', '\n') else: description = '' self.prompt_text = description + self.prompt_text_commented return self.prompt_text self.prompt_text_commented = ''' # --------------------------------- # Please enter text above this line # An empty message will abort the operation. # # Below is a list of acceptable values for each input field. # # Users:''' for user in self.users: user = user.split(' ~ ') name = user[0] id = user[1] if self.users_type == 'accountId': self.prompt_text_commented += f''' # [{name}|~accountid:{id}]''' else: self.prompt_text_commented += f''' # [~{id}]''' # Add comment if self.prompt_type == 'add_comment': self.prompt_text = self.prompt_text_commented return self.prompt_text # Edit comment if self.prompt_type == 'edit_comment': self.active_comment = self.jira.comment(active_issue, comment_id) self.prompt_text = self.active_comment.body + self.prompt_text_commented return self.prompt_text statuses = [x.name for x in self.jira.statuses()] issuetypes = [x.name for x in self.jira.issue_types()] priorities = [x.name for x in self.jira.priorities()] components = [ x.name for x in self.jira.project_components( self.userconfig_filter['project']) ] if self.userconfig_filter['project'] != '' else '' versions = [ x.name for x in self.jira.project_versions( self.userconfig_filter['project']) ] if self.userconfig_filter['project'] != '' else '' projects = [x.key for x in self.jira.projects()] # Extra info for prompt_type == 'issue' self.prompt_text_commented += f''' # # Projects: {projects} # IssueTypes: {issuetypes} # Statuses: {statuses} # Priorities: {priorities} # Components in {self.userconfig_filter["project"]} Project: {components} # Versions in {self.userconfig_filter["project"]} Project: {versions}''' self.prompt_text = f'''[*Summary*] [Description] [*Project*] {self.userconfig_filter["project"]} [*IssueType*] {self.userconfig_newissue["issuetype"]} [Status] {self.userconfig_newissue["status"]} [Priority] {self.userconfig_newissue["priority"]} [Component] {self.userconfig_newissue["component"]} [Version] {self.userconfig_newissue["fixVersion"]} [Assignee] {self.userconfig_newissue["assignee"]} {self.prompt_text_commented}''' return self.prompt_text def format_date(self, date): time = datetime.now().strptime(date, '%Y-%m-%dT%H:%M:%S.%f%z').astimezone() return str(time)[0:10] + ' ' + str(time)[11:16] def get_report(self): ''' Print a report for the given issue ''' for customfield in self.jira.fields(): if customfield['name'] == 'Epic Link': epicID = customfield['id'] # Get passed issue content active_issue = vim.eval("g:vira_active_issue") issues = self.jira.search_issues( 'issue = "' + active_issue + '"', fields=','.join([ 'project', 'summary', 'comment', 'component', 'description', 'issuetype', 'priority', 'status', 'created', 'updated', 'assignee', 'reporter', 'fixVersion', 'customfield_10106', 'labels', epicID ]), json_result='True') issue = issues['issues'][0]['fields'] # Prepare report data open_fold = '{{{' close_fold = '}}}' summary = issue['summary'] story_points = str(issue.get('customfield_10106', '')) created = self.format_date(issue['created']) updated = self.format_date(issue['updated']) issuetype = issue['issuetype']['name'] status = issue['status']['name'] priority = issue['priority']['name'] assignee = issue['assignee']['displayName'] if type( issue['assignee']) == dict else 'Unassigned' reporter = issue['reporter']['displayName'] component = ', '.join([c['name'] for c in issue['components']]) version = ', '.join([v['name'] for v in issue['fixVersions']]) epics = str(issue.get(epicID)) vim.command(f'let s:vira_epic_field = "{epicID}"') description = str(issue.get('description')) # Version percent for single version attacted # if len(issue['fixVersions']) == 1 and version != '': # version += ' | ' + self.version_percent( # str(issue['project']['key']), version) + '%' comments = '' idx = 0 for idx, comment in enumerate((issue['comment']['comments'])): comments += ''.join([ comment['author']['displayName'] + ' @ ' + self.format_date(comment['updated']) + ' {{' + '{2\n' + comment['body'] + '\n}}}\n' ]) old_count = idx - 3 old_comment = 'Comment' if old_count == 1 else 'Comments' comments = ''.join( [str(old_count) + ' Older ' + old_comment + ' {{{1\n']) + comments if old_count >= 1 else comments comments = comments.replace('}}}', '}}}}}}', idx - 3) comments = comments.replace('}}}}}}', '}}}', idx - 4) # Find the length of the longest word [-1] words = [ created, updated, issuetype, status, story_points, priority, component, version, assignee, reporter, epics ] wordslength = sorted(words, key=len)[-1] s = '─' dashlength = s.join([char * len(wordslength) for char in s]) active_issue_spacing = int((16 + len(dashlength)) / 2 - len(active_issue) / 2) active_issue_spaces = ' '.join( [char * (active_issue_spacing) for char in ' ']) active_issue_space = ' '.join([ char * ((len(active_issue) + len(dashlength)) % 2) for char in ' ' ]) created_spaces = ' '.join( [char * (len(dashlength) - len(created)) for char in ' ']) updated_spaces = ' '.join( [char * (len(dashlength) - len(updated)) for char in ' ']) task_type_spaces = ' '.join( [char * (len(dashlength) - len(issuetype)) for char in ' ']) status_spaces = ' '.join( [char * (len(dashlength) - len(status)) for char in ' ']) story_points_spaces = ''.join( [char * (len(dashlength) - len(story_points)) for char in ' ']) priority_spaces = ''.join( [char * (len(dashlength) - len(priority)) for char in ' ']) component_spaces = ''.join( [char * (len(dashlength) - len(component)) for char in ' ']) version_spaces = ''.join( [char * (len(dashlength) - len(version)) for char in ' ']) assignee_spaces = ''.join( [char * (len(dashlength) - len(assignee)) for char in ' ']) reporter_spaces = ''.join( [char * (len(dashlength) - len(reporter)) for char in ' ']) epics_spaces = ''.join( [char * (len(dashlength) - len(epics)) for char in ' ']) # Create report template and fill with data report = '''┌────────────────{dashlength}─┐ │{active_issue_spaces}{active_issue}{active_issue_spaces}{active_issue_space} │ ├──────────────┬─{dashlength}─┤ │ Created │ {created}{created_spaces} │ │ Updated │ {updated}{updated_spaces} │ │ Type │ {issuetype}{task_type_spaces} │ │ Status │ {status}{status_spaces} │ │ Story Points │ {story_points}{story_points_spaces} │ │ Priority │ {priority}{priority_spaces} │ │ Epic Link │ {epics}{epics_spaces} │ │ Component(s) │ {component}{component_spaces} │ │ Version(s) │ {version}{version_spaces} │ │ Assignee │ {assignee}{assignee_spaces} │ │ Reporter │ {reporter}{reporter_spaces} │ └──────────────┴─{dashlength}─┘ ┌──────────────┐ │ Summary │ └──────────────┘ {summary} ┌──────────────┐ │ Description │ └──────────────┘ {description} ┌──────────────┐ │ Comments │ └──────────────┘ {comments}''' self.set_report_lines(report, description, issue) self.prompt_text = self.report_users(report.format(**locals())) return self.prompt_text def report_users(self, report): ''' Replace report accountid with names ''' for user in self.users: user = user.split(' ~ ') if user[0] != "Unassigned": report = report.replace('accountid:', '').replace('[~' + user[1] + ']', '[~' + user[0] + ']') return report def get_reporters(self): ''' Get my issues with JQL ''' self.print_users() def get_servers(self): ''' Get list of servers ''' try: for server in self.vira_servers.keys(): print(server) print('Null') except: self.connect('') def get_statuses(self): ''' Get my issues with JQL ''' statuses = [] for status in self.jira.statuses(): if str(status) not in statuses: statuses.append(str(status)) print(str(status)) def get_set_status(self): ''' Get my issues with JQL ''' self.get_statuses() def get_version(self): ''' Get my issues with JQL ''' self.print_versions() def new_component(self, name, project): ''' New component added to project ''' self.jira.create_component(name=name, project=project, description=name) def new_version(self, name, project, description): ''' Get my issues with JQL ''' self.jira.create_version(name=name, project=project, description=description) def print_users(self): ''' Print users ''' print(self.get_current_user() + ' ~ currentUser') for user in self.users: print(user) print('Unassigned') def get_users(self): ''' Get my issues with JQL ''' query = 'ORDER BY updated DESC' issues = self.jira.search_issues(query, fields='assignee, reporter', json_result='True', maxResults=-1) # Determine cloud/server jira self.users_type = 'accountId' if issues['issues'][0]['fields'][ 'reporter'].get('accountId') else 'name' for issue in issues['issues']: user = str(issue['fields']['reporter']['displayName'] ) + ' ~ ' + issue['fields']['reporter'][self.users_type] self.users.add(user) if type(issue['fields']['assignee']) == dict: user = str( issue['fields']['assignee']['displayName'] ) + ' ~ ' + issue['fields']['assignee'][self.users_type] self.users.add(user) return sorted(self.users) def get_current_user(self): query = 'reporter = currentUser() or assignee = currentUser()' issues = self.jira.search_issues(query, fields='assignee, reporter', json_result='True', maxResults=-1) issue = issues['issues'][0]['fields'] return str(issue['assignee'][self.users_type] if type( issue['assignee']) == dict else issue['reporter'][self.users_type] if type(issue['reporter']) == dict else 'Unassigned') def print_versions(self): ''' Print version list with project filters ''' try: versions = sorted(self.versions) wordslength = sorted(versions, key=len)[-1] s = ' ' dashlength = s.join([char * len(wordslength) for char in s]) for version in versions: print( version.split('|')[0] + ''.join([ char * (len(dashlength) - len(version)) for char in ' ' ]) + ' ' + version.split('|')[1] + ' ' + version.split('|')[2]) except: pass print('None') def version_percent(self, project, fixVersion): project = str(project) fixVersion = str(fixVersion) if str(project) != '[]' and str(project) != '' and str( fixVersion) != '[]' and str(fixVersion) != '': query = 'fixVersion = ' + fixVersion + ' AND project = "' + project + '"' issues = self.jira.search_issues(query, fields='fixVersion', json_result='True', maxResults=1) try: issue = issues['issues'][0]['fields']['fixVersions'][0] idx = issue['id'] total = self.jira.version_count_related_issues( idx)['issuesFixedCount'] pending = self.jira.version_count_unresolved_issues(idx) fixed = total - pending percent = str(round(fixed / total * 100, 1)) if total != 0 else 1 space = ''.join([char * (5 - len(percent)) for char in ' ']) name = fixVersion try: description = issue['description'] except: description = 'None' pass except: total = 0 pending = 0 fixed = total - pending percent = "0" space = ''.join([char * (5 - len(percent)) for char in ' ']) name = fixVersion description = '' pass version = str( str(name) + ' ~ ' + str(description) + '|' + str(fixed) + '/' + str(total) + space + '|' + str(percent) + '%') self.versions_hide = vim.eval('g:vira_version_hide') if fixed != total or total == 0 or not int( self.versions_hide) == 1: self.versions.add( str(project) + ' ~ ' + str(version.replace('\'', ''))) else: percent = 0 return percent def get_versions(self): ''' Build a vim pop-up menu for a list of versions with project filters ''' # Loop through each project and all versions within try: for v in reversed( self.jira.project_versions(vim.eval('s:projects[0]'))): vim.command('let s:versions = add(s:versions,\"' + str(v) + '\")') except: vim.command('let s:versions = []') def load_project_config(self, repo): ''' Load project configuration for the current git repo The current repo can either be determined by current files path or by the user setting g:vira_repo (part of :ViraLoadProject) For example, an entry in projects.yaml may be: vira: server: https://jira.tgall.ca project_name: VIRA ''' # Only proceed if projects file parsed successfully if not getattr(self, 'vira_projects', None): return # If current repo/folder doesn't exist, use __default__ project config if it exists if repo == '': repo = run_command( 'git rev-parse --show-toplevel')['stdout'].strip() if not self.vira_projects.get(repo): repo = repo.split('/')[-1] if not self.vira_projects.get(repo): repo = run_command('pwd')['stdout'].strip() if not self.vira_projects.get(repo): repo = repo.split('/')[-1] if not self.vira_projects.get(repo): repo = '__default__' if not self.vira_projects.get('__default__'): return # Set server server = self.vira_projects.get(repo, {}).get('server') if server: vim.command(f'let g:vira_serv = "{server}"') # Set user-defined filters for current project for key in self.userconfig_filter.keys(): value = self.vira_projects.get(repo, {}).get('filter', {}).get(key) if value: self.userconfig_filter[key] = value # Set user-defined new-issue defaults for current project for key in self.userconfig_newissue.keys(): value = self.vira_projects.get(repo, {}).get('newissue', {}).get(key) if value: self.userconfig_newissue[key] = value # Set user-defined issue sort options sort_order = self.vira_projects.get(repo, {}).get('issuesort', 'updated DESC') self.userconfig_issuesort = ', '.join(sort_order) if type( sort_order) == list else sort_order def query_issues(self): ''' Query issues based on current filters ''' q = [] for filterType in self.userconfig_filter.keys(): filter_str = self.filter_str(filterType) if filter_str: q.append(filter_str) query = ' AND '.join(q) + ' ORDER BY ' + self.userconfig_issuesort issues = self.jira.search_issues( query, fields='summary,comment,status,statusCategory,issuetype,assignee', json_result='True', maxResults=vim.eval('g:vira_issue_limit')) return issues['issues'] def reset_filters(self): ''' Reset filters to their default values ''' self.userconfig_filter = dict(self.userconfig_filter_default) def set_report_lines(self, report, description, issue): ''' Create dictionary for vira report that shows relationship between line numbers and fields to be edited ''' writable_fields = { 'Assignee': 'ViraSetAssignee', 'Component': 'ViraSetComponent', 'Priority': 'ViraSetPriority', 'Epic Link': 'ViraSetEpic', 'Status': 'ViraSetStatus', 'Type': 'ViraSetType', 'Version': 'ViraSetVersion', } self.report_lines = {} for idx, line in enumerate(report.split('\n')): for field, command in writable_fields.items(): if field in line: self.report_lines[idx + 1] = command continue for x in range(16, 21): self.report_lines[x] = 'ViraEditSummary' description_len = description.count('\n') + 3 for x in range(21, 23 + description_len): self.report_lines[x] = 'ViraEditDescription' offset = 2 if len(issue['comment']['comments']) > 4 else 1 comment_line = 25 + description_len + offset for comment in issue['comment']['comments']: comment_len = comment['body'].count('\n') + 3 for x in range(comment_line, comment_line + comment_len): self.report_lines[x] = 'ViraEditComment ' + comment['id'] comment_line = comment_line + comment_len def set_prompt_text(self): ''' Take the user prompt text and perform an action Usually, this involves writing to the jira server ''' # User input issue = vim.eval('g:vira_active_issue') userinput = vim.eval('g:vira_input_text') input_stripped = userinput.replace(self.prompt_text_commented.strip(), '').strip() # Check if anything was actually entered by user if input_stripped == '' or userinput.strip() == self.prompt_text.strip( ): print("No vira actions performed") return if self.prompt_type == 'edit_filter': self.userconfig_filter = json.loads(input_stripped) elif self.prompt_type == 'add_comment': self.jira.add_comment(issue, input_stripped) elif self.prompt_type == 'edit_comment': self.active_comment.update(body=input_stripped) elif self.prompt_type == 'summary': self.jira.issue(issue).update(summary=input_stripped) elif self.prompt_type == 'description': self.jira.issue(issue).update(description=input_stripped) elif self.prompt_type == 'issue': self.create_issue(input_stripped) def versions_hide(self, state): ''' Display and hide complete versions ''' if state is True or 1 or 'ture' or 'True': self.version_hide = True elif state is False or 0 or 'false' or 'False': self.version_hide = False else: self.version_hide = not self.version_hide
###Component match cMatch=re.compile('^BFB.*') ###Get 'raw' lists (Jira classes are not so handy) listSrc=[] listDst=[] for i in projSrc: if cMatch.match(str(i)): listSrc.append(str(i)) for i in projDst: if cMatch.match(str(i)): listDst.append(str(i)) ###Get absent strings toAdd=[] for item in listSrc: if item not in listDst: toAdd.append(item) ####Add absent strings to project components. if toAdd != []: for item in toAdd: print ("Adding absent component:", item) jira.create_component(name=item,project=args.dest,description='Business Component',assigneeType='UNASSIGNED') else: print ("Nothing to add.")
if not version_exists: print "The version %s does NOT exist. Adding..." % version jira.create_version(version, app.upper()) # Make sure Components exists in JIRA Project components = jira.project_components(project) print "Checking for Components..." for c in components: if c.name == "webapp": webapp = True if c.name == "database": database = True if not webapp: print "webapp component does not exist. Adding..." jira.create_component("webapp", app.upper(), None, None, "UNASSIGNED", False) if not database: print "database component does not exist. Adding..." jira.create_component("database", app.upper(), None, None, "UNASSIGNED", False) print "Checking for Developer Group... %s" % devgroup groups = jira.groups(query=devgroup) for g in groups: if g == devgroup: group = True if not group: print "Developer group does not exist. Adding %s" % devgroup jira.add_group(devgroup) # Populate the KV pairs for the RFD
class JiraWrapper: """ Setting for connecting to JIRA """ def __init__(self, params): # general settings self.params = params self.jira = None # Proxy settings if "HTTP_PROXY" not in os.environ: if params.get_http_proxy_url: os.environ["HTTP_PROXY"] = params.get_http_proxy_url if "HTTPS_PROXY" not in os.environ: if params.get_https_proxy_url: os.environ["HTTPS_PROXY"] = params.get_https_proxy_url # jira settings self.jira_url = self.params.get_jira_url self.jira_url = self.jira_url.strip("/") # see if we need to use a certificate if params.get_use_cert: self.jira_options = { "server": self.jira_url, "verify": True, "client_cert": ( "mycert-dn.crt", "mycert-dn.key", ) } else: self.jira_options = {"server": self.jira_url} # noinspection PyAttributeOutsideInit def connect(self, username, password: str): """ Trigger the connection to jira :param username: :param password: user password for the connection :return: True on success """ try: print('Connecting to Jira') self.jira = JIRA(options=self.jira_options, basic_auth=(username, password), max_retries=1) print('Connected') return True, None except Exception as e: print('Unable to connect') print(str(e)) pass return False, e @property def get_components(self) -> list: """ Get list of components available on the server :return: available compnents """ components = self.jira.project_components(self.params.get_project) component_list = [] for i in components: component_list.append(i.name) return component_list def create_components(self, components: list) -> list: """ Create components if the do not already exist :param components: components to create :return: list of created components """ components_avail = self.get_components components_todo = set(components) - set(components_avail) for i in components_todo: self.jira.create_component(i, self.params.get_project) return list(components_todo) def create_task_subtask(self, task_name: str, comment: str, reporter: str, parent_assignee: str, sub_assignees: list, duedate: str, labels: [str], priority: str): """ Creat a task and all subtasks for the users :param reporter: who reports the bug :param priority: :param comment: :param task_name: Name of the task :param parent_assignee: Who is the owner of the parent task :param sub_assignees: List of people getting the subtasks. Needs to be jira userids :param duedate: what is the duedate for the parent task and the subtasks :param labels: Labels the tasks get added :return: url to the jira task for the webbrowser """ # return values parent_url = None error_msg = None parent_component = None parent_assignee_id = None for i in sub_assignees: if parent_assignee == i[0]: parent_assignee_id = i[1] parent_component = i[2] break assert parent_assignee_id # TODO this only works if the reporter is part of the assignee list. reporter_id = '' for i in sub_assignees: if reporter == i[0]: reporter_id = i[1] break assert reporter_id # sub_assignees = self.get_users(sub_assignees) issue_dict = { 'project': self.params.get_project, 'summary': task_name, 'description': comment, 'issuetype': { 'name': 'Task' }, 'assignee': { 'name': parent_assignee_id }, 'duedate': duedate, 'labels': labels, 'priority': { 'name': priority } } if parent_component: issue_dict['components'] = [{'name': parent_component}] # TODO allow change reporter if self.params.can_change_reporter: issue_dict['reporter'] = {'name': reporter_id} # print("Creating task") # print(sub_assignees) # print(issue_dict['assignee']) # for debugging set to false to no swap jira create = True parent_issue = None if create: try: parent_issue = self.jira.create_issue(fields=issue_dict) print("Created task: %s" % parent_issue.key) except Exception as e: print(e) parent_url = None error_msg = e.text pass # we were not able to create a issue. Connection error? if not parent_issue: return parent_url, error_msg # does your handle bulk create? can_bulk = True # now create the sub-task, linked to the parent task for all users child_issue_dict = [] for val in sub_assignees: # val[3] is the bool if the task has to be created if val[3]: print('Creating Sub-tak for:', val[0]) cur_issue_dict = copy.deepcopy(issue_dict) cur_issue_dict['summary'] = '(Subtask) ' + task_name cur_issue_dict['issuetype'] = {'name': 'Sub-task'} if create: # noinspection PyUnresolvedReferences # cur_issue_dict['parent'] = {"id": parent_issue.key} cur_issue_dict['parent'] = {"id": parent_issue.id} cur_issue_dict['assignee'] = {'name': val[1]} # if componets for the user, add it if val[2]: cur_issue_dict['components'] = [{'name': val[2]}] else: cur_issue_dict['components'] = [] if not can_bulk: print('Slow call of Sub-task creation') self.jira.create_issue(cur_issue_dict) else: child_issue_dict.append(cur_issue_dict) if create and len(child_issue_dict) > 0: try: self.jira.create_issues(child_issue_dict) except Exception as e: print('Error creating Sub-Tasks. Message:') print(e) pass assert parent_issue parent_url = '%s/%s/%s' % (self.jira_url, 'browse', parent_issue.key) print(parent_url) return parent_url, error_msg def get_users(self, userlist): """ For all the users in the self.full_assignee_list get the user ids from JIRA :return: """ for i, val in enumerate(userlist): tmp = self.jira.search_assignable_users_for_projects( val[0], self.params.get_project) if not tmp: raise Exception('Could not find userid for %s' % val[0]) userlist[i][1] = tmp[0].key return userlist