class TestComponents(unittest.TestCase): def setUp(self): self.jira = JIRA(options=dict(server=TEST_URL, verify=False), basic_auth=(TEST_USERNAME, TEST_PASSWORD)) self.purge_test_components() def tearDown(self): self.purge_test_components() def get_component(self, name): components = [ _ for _ in self.jira.project_components('KB') if _.name == 'test-1' ] return components[0] if len(components) == 1 else None def purge_test_components(self): for _ in self.jira.project_components('KB'): if _.name.startswith('test-'): _.delete() def test_create_component(self): result = CliRunner().invoke( topcli, ['component', 'create', 'KB', 'test-1', '-d', 'Some description']) self.assertEqual(result.exit_code, 0) def test_delete_component(self): result = CliRunner().invoke(topcli, ['component', 'create', 'KB', 'test-1']) self.assertEqual(result.exit_code, 0) self.assertIsNotNone(self.get_component('test-1')) result = CliRunner().invoke(topcli, ['component', 'delete', 'KB', 'test-1']) self.assertEqual(result.exit_code, 0) self.assertIsNone(self.get_component('test-1')) def test_update_component(self): result = CliRunner().invoke( topcli, ['component', 'create', 'KB', 'test-1', '-d', 'before']) before = self.get_component('test-1') self.assertEqual(before.description, 'before') result = CliRunner().invoke( topcli, ['component', 'update', 'KB', 'test-1', '-d', 'after']) self.assertEqual(result.exit_code, 0) after = self.get_component('test-1') self.assertEqual(after.description, 'after') def test_search_component(self): result = CliRunner().invoke( topcli, ['component', 'create', 'KB', 'test-1', '-d', 'Some description']) result = CliRunner().invoke(topcli, ['component', 'search', 'KB']) self.assertEqual(result.exit_code, 0) self.assertIn('test-1', result.output) self.assertIn('Some description', result.output)
class TestComponents(unittest.TestCase): def setUp(self): self.jira = JIRA(options=dict(server=TEST_URL, verify=False), basic_auth=(TEST_USERNAME, TEST_PASSWORD)) self.purge_test_components() def tearDown(self): self.purge_test_components() def get_component(self, name): components = [_ for _ in self.jira.project_components('KB') if _.name == 'test-1'] return components[0] if len(components) == 1 else None def purge_test_components(self): for _ in self.jira.project_components('KB'): if _.name.startswith('test-'): _.delete() def test_create_component(self): result = CliRunner().invoke(topcli, ['component', 'create', 'KB', 'test-1', '-d', 'Some description']) self.assertEqual(result.exit_code, 0) def test_delete_component(self): result = CliRunner().invoke(topcli, ['component', 'create', 'KB', 'test-1']) self.assertEqual(result.exit_code, 0) self.assertIsNotNone(self.get_component('test-1')) result = CliRunner().invoke(topcli, ['component', 'delete', 'KB', 'test-1']) self.assertEqual(result.exit_code, 0) self.assertIsNone(self.get_component('test-1')) def test_update_component(self): result = CliRunner().invoke(topcli, ['component', 'create', 'KB', 'test-1', '-d', 'before']) before = self.get_component('test-1') self.assertEqual(before.description, 'before') result = CliRunner().invoke(topcli, ['component', 'update', 'KB', 'test-1', '-d', 'after']) self.assertEqual(result.exit_code, 0) after = self.get_component('test-1') self.assertEqual(after.description, 'after') def test_search_component(self): result = CliRunner().invoke(topcli, ['component', 'create', 'KB', 'test-1', '-d', 'Some description']) result = CliRunner().invoke(topcli, ['component', 'search', 'KB']) self.assertEqual(result.exit_code, 0) self.assertIn('test-1', result.output) self.assertIn('Some description', result.output)
class JiraInit(object): @except_decorate("获取jira服务器错误,请确认能在网页中正确登录JIRA服务") def __init__(self,jurl,juser,jpasswd): self.jurl = jurl self.juser = juser self.jpasswd = jpasswd self.nowtime = datetime.datetime.now().strftime('%Y年%m月%d日') self.jr = JIRA(self.jurl,basic_auth=(self.juser,self.jpasswd)) @except_decorate("用户错误") def authuser(self): name = self.jr.current_user() logger().info(u"当前登录用户:%s" % name) # 查询项目是否存在 @except_decorate("未查询到项目") def ExistProject(self, jproject): projects = str(self.jr.projects()) if jproject in projects: logger().info("存在项目%s" % jproject) else: logger().error("不存在项目%s" % jproject) exit(2) @staticmethod def JqlProjrct(jproject): jproject_ql = "project = %s "%jproject return jproject_ql @staticmethod def JqlText(jversion): jtext_ql = "AND text ~ %s "%jversion return jtext_ql @staticmethod def JqlBugType(jbugtype): jbugtype_ql = "AND bug类型 = %s "%jbugtype return jbugtype_ql @staticmethod def JqlModel(jmodel): jmodel_ql = "AND component = %s "%jmodel return jmodel_ql #获取全部模块类型 def GetComponents(self,project): components = self.jr.project_components(project) return components
print("%s assigned these issues." % assignee) print(top_ten) while top_ten: item = top_ten.pop() prj_name = item[0] issue_count = item[1] print("Project name : {name}, Assigned Count : {count}".format( name=prj_name, count=issue_count)) #project 상세 정보 표출 if prj_name == "BTVD": prj = jira.project(prj_name) print("=" * 30) print("Project name : %s" % prj.name) print("Project leader name : %s" % prj.lead.displayName) components = jira.project_components(prj) print("Components : " + str([c.name for c in components])) print("Roles : " + str(jira.project_roles(prj))) versions = jira.project_versions(prj) print("Versions : " + str([v.name for v in reversed(versions)])) print("=" * 30) else: continue break_loop = False for issue in issues: print("Issue summary : " + issue.fields.summary) watcher = jira.watchers(issue) if watcher.watchCount > 1: print("Issue has {} watchers(s)".format(watcher.watchCount)) for watcher in watcher.watchers:
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': '', 'statusCategory': ['To Do', 'In Progress'], 'text': '' } self.reset_filters() self.userconfig_newissue = { 'assignee': '', 'component': '', 'fixVersion': '', 'issuetype': 'Bug', 'priority': '', 'status': '', } 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.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 ''' # 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() else: password = self.vira_servers[server]['password'] # Connect to jira server try: self.jira = JIRA(options={ 'server': server, 'verify': cert_verify, }, basic_auth=(username, password), timeout=5) 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: raise e 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( "'null'", "Null").replace("'Unassigned'", "Null").replace(f"text in ({selection})", f"text ~ {selection}") def get_assign_issue(self): ''' Menu to select users ''' self.get_users() def get_assignees(self): ''' Get my issues with JQL ''' self.get_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 popup menu for a list of components ''' for component in self.jira.project_components( self.userconfig_filter['project']): print(component.name) def get_component(self): ''' Build a vim popup menu for a list of components ''' self.get_components() def get_epics(self): ''' Get my issues with JQL ''' for issue in self.query_issues(issuetypes="Epic"): print(issue["key"] + ' - ' + issue["fields"]['summary']) def get_issue(self, issue): ''' Get single issue by isuue 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 get_projects(self): ''' Build a vim popup menu for a list of projects ''' for project in self.jira.projects(): print(project) def get_priority(self): ''' Build a vim popup 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 ''' # Edit summary self.prompt_type = prompt_type 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'] return summary + self.prompt_text_commented # 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 = '' return description + self.prompt_text_commented # Prepare dynamic variables for prompt text query = 'ORDER BY updated DESC' issues = self.jira.search_issues(query, fields='assignee, reporter', json_result='True', maxResults=-1) # Determine cloud/server jira id = 'accountId' if issues['issues'][0]['fields']['reporter'].get( 'accountId') else 'name' users = set() for issue in issues['issues']: user = str(issue['fields']['reporter']['displayName'] ) + ' ~ ' + issue['fields']['reporter'][id] users.add(user) if type(issue['fields']['assignee']) == dict: user = str(issue['fields']['assignee']['displayName'] ) + ' ~ ' + issue['fields']['assignee'][id] users.add(user) self.prompt_text_commented = f''' # --------------------------------- # 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: {users} ''' # Add comment if self.prompt_type == 'add_comment': return self.prompt_text_commented # Edit comment if self.prompt_type == 'edit_comment': self.active_comment = self.jira.comment(active_issue, comment_id) return self.active_comment.body + self.prompt_text_commented 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} ''' return 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}''' def get_report(self): ''' Print a report for the given issue ''' # Get passed issue content active_issue = vim.eval("g:vira_active_issue") issues = self.jira.search_issues( 'issue = "' + active_issue + '"', # fields='*', fields=','.join([ 'summary', 'comment', 'component', 'description', 'issuetype', 'priority', 'status', 'created', 'updated', 'assignee', 'reporter', 'fixVersion', 'customfield_10106' ]), 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 = issue['created'][0:10] + ' ' + issues['issues'][0]['fields'][ 'created'][11:16] updated = issue['updated'][0:10] + ' ' + issues['issues'][0]['fields'][ 'updated'][11:16] 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']]) description = str(issue.get('description')) comments = '' idx = 0 for idx, comment in enumerate((issue['comment']['comments'])): comments += ''.join([ comment['author']['displayName'] + ' @ ' + # comment['body'] + '\n}}}\n' comment['updated'][0:10] + ' ' + comment['updated'][11:16] + ' {{{2\n' + comment['body'] + '\n}}}\n' ]) comments = ''.join(['Old Comments {{{1\n' ]) + comments if idx > 3 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 ] 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) % 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 ' ']) # 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} │ │ Component │ {component}{component_spaces} │ │ Version │ {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) return report.format(**locals()) def get_reporters(self): ''' Get my issues with JQL ''' self.get_users() def get_servers(self): ''' Get list of servers ''' for server in self.vira_servers.keys(): print(server) 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.get_versions() 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) users = [] for issue in issues["issues"]: id = str(issue['fields']['reporter']['self']).split("=")[1] user = issue['fields']['reporter']['displayName'] if user + ' ~ ' + id not in users: users.append(user + ' ~ ' + str(id)) for user in sorted(users): print(user) print('Unassigned') def get_versions(self): ''' Build a vim popup menu for a list of versions ''' for version in self.jira.project_versions( self.userconfig_filter['project']): print(version.name) print('null') def load_project_config(self): ''' Load project configuration for the current git repo 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 repo = run_command( 'git rev-parse --show-toplevel')['stdout'].strip().split('/')[-1] # If curren't repo doesn't exist, use __default__ project config if it exists if not self.vira_projects.get(repo): if self.vira_projects.get('__default__'): repo = '__default__' else: 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 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 updated DESC' 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', 'Status': 'ViraSetStatus', 'Type': 'ViraSetType', 'Version': 'ViraSetVersion', 'Summary': 'ViraEditSummary', } 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 if field == 'Summary': self.report_lines[idx + 2] = command self.report_lines[idx + 3] = command continue description_len = description.count('\n') + 3 for x in range(18, 18 + description_len): self.report_lines[x] = 'ViraEditDescription' offset = 2 if len(issue['comment']['comments']) > 4 else 1 comment_line = 18 + 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 write_jira(self): ''' Write to jira Can be issue name, description, comment, etc... ''' # User input issue = vim.eval('g:vira_active_issue') input_stripped = vim.eval('g:vira_input_text').replace( self.prompt_text_commented.strip(), '').strip() # Check if anything was actually entered by user if input_stripped == '': print("No vira actions performed") return if self.prompt_type == 'add_comment': return self.jira.add_comment(issue, input_stripped) if self.prompt_type == 'edit_comment': return self.active_comment.update(body=input_stripped) elif self.prompt_type == 'summary': return self.jira.issue(issue).update(summary=input_stripped) elif self.prompt_type == 'description': return self.jira.issue(issue).update(description=input_stripped) elif self.prompt_type == 'issue': return self.create_issue(input_stripped)
def create_bug_issue(self, channel, summary, description, component, version, labels, user=JIRA_USER, passwd=JIRA_PASS, project=JIRA_PROJECT): """ Creates a bug issue on Jira :param channel: The channel to notify :param summary: The title summary :param description: Description field :param component: Component bug affects :param version: Version this bug affects :param labels: Labels to attach to the issue :param user: User to report bug as :param passwd: Password :param project: Jira project """ if user and passwd and project: try: jira = JIRA(server='https://issues.voltdb.com/', basic_auth=(user, passwd)) except: self.logger.exception('Could not connect to Jira') return else: self.logger.error( 'Did not provide either a Jira user, a Jira password or a Jira project' ) return # Check for existing bug with same summary existing = jira.search_issues('summary ~ \'%s\'' % summary) if len(existing) > 0: # Already reported self.logger.info('OLD: Already reported issue with summary "' + summary + '"') return issue_dict = { 'project': project, 'summary': summary, 'description': description, 'issuetype': { 'name': 'Bug' }, 'labels': labels } jira_component = None components = jira.project_components(project) for c in components: if c.name == component: jira_component = {'name': c.name, 'id': c.id} break if jira_component: issue_dict['components'] = [jira_component] else: # Components is still a required field issue_dict['components'] = ['Core'] jira_version = None versions = jira.project_versions(project) for v in versions: if v.name == version: jira_version = {'name': v.name, 'id': v.id} break if jira_version: issue_dict['versions'] = [jira_version] else: # Versions is still a required field issue_dict['versions'] = ['DEPLOY-Integration'] new_issue = jira.create_issue(fields=issue_dict) self.logger.info('NEW: Reported issue with summary "' + summary + '"') if self.connect_to_slack(): self.post_message( channel, 'Opened issue at https://issues.voltdb.com/browse/' + new_issue.key)
if not options.usernameJIRA or not options.passwordJIRA or not options.jiraserver or not options.frombranch or not options.jbidefixversion or not options.jbdsfixversion or not options.taskdescription: parser.error("Must to specify ALL commandline flags") jiraserver = options.jiraserver jbide_fixversion = options.jbidefixversion jbds_fixversion = options.jbdsfixversion from components import checkFixVersionsExist, queryComponentLead, defaultAssignee if checkFixVersionsExist(jbide_fixversion, jbds_fixversion, jiraserver, options.usernameJIRA, options.passwordJIRA) == True: frombranch = options.frombranch jira = JIRA(options={'server': jiraserver}, basic_auth=(options.usernameJIRA, options.passwordJIRA)) CLJBIDE = jira.project_components( jira.project('JBIDE')) # full list of components in JBIDE CLJBDS = jira.project_components( jira.project('JBDS')) # full list of components in JBIDE taskdescription = options.taskdescription taskdescriptionfull = options.taskdescriptionfull if not options.taskdescriptionfull: taskdescriptionfull = options.taskdescription ## The jql query across for all task issues tasksearchquery = '((project in (JBDS) and fixVersion = "' + jbds_fixversion + '") or (project in (JBIDE) and fixVersion = "' + jbide_fixversion + '")) AND labels = task' tasksearch = jiraserver + '/issues/?jql=' + urllib.quote_plus( tasksearchquery) rootJBDS_dict = {
password=creds[1] server=creds[2] ##Get arguments. parser = argparse.ArgumentParser(description='You have to specify 2 arguments that describes source project and destionation peroject.') parser.add_argument('--source', action='store', help='Source Project') parser.add_argument('--dest', action='store', help='Destination Project') args = parser.parse_args() if not args.source or not args.dest: parser.print_help() sys.exit() ##Acess Jira and get info. jira = JIRA(server=server,basic_auth=(username,password)) projSrc = jira.project_components(args.source) projDst = jira.project_components(args.dest) ###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)):
class AntonJira(): def __init__(self, server, email, apitoken): self.jira_instance = JIRA(server=server, basic_auth=(email, apitoken)) def get_project(self, project_key): """ name -- project name lead.displayName -- leader name """ return self.jira_instance.project(project_key) def get_project_components(self, project): """ requires : project instance returns : list of components """ return [ comp.name for comp in self.jira_instance.project_components(project) ] def get_issue_detail(self, issue_key): """ .fields.summary -- summary .fields.votes.votes -- votes .fields.description .fields.comment : [{ .author.displayName .body }] """ return self.jira_instance.issue(issue_key) def get_issues_in_project(self, project_key): """ [ .fields.summary -- summary .fields.votes.votes -- votes .fields.description .fields.comment : [{ .author.displayName .body }] ] """ return self.jira_instance.search_issues(f"project={project_key}") def get_issues_of_current_user_in_project(self, project_key): """ [ .fields.summary -- summary .fields.votes.votes -- votes .fields.description .fields.comment : [{ .author.displayName .body }] ] """ return self.jira_instance.search_issues( f"project={project_key} and assignee=currentuser()") def get_issues_of_current_user(self): """ [ .fields.summary -- summary .fields.votes.votes -- votes .fields.description .fields.comment : [{ .author.displayName .body }] ] """ return self.jira_instance.search_issues(f"assignee=currentuser()") # jira = JIRA('https://anton3.atlassian.net',basic_auth=('*****@*****.**', 'TKLhcdc5zw2anmiX8DQ946BD')) # jra = jira.project('AN') # print(jra.name) # print(jra.lead.displayName) # email:[email protected] # password: pratikbaid@2471 # jira = AntonJira('https://anton3.atlassian.net','*****@*****.**', 'TKLhcdc5zw2anmiX8DQ946BD') # print(jira.get_issues_of_current_user('AN'))
def create_bug_issue(self, channel, summary, description, component, version, labels, user=JIRA_USER, passwd=JIRA_PASS, project=JIRA_PROJECT, DRY_RUN=False): """ Creates a bug issue on Jira :param channel: The channel to notify :param summary: The title summary :param description: Description field :param component: Component bug affects :param version: Version this bug affects :param labels: Labels to attach to the issue :param user: User to report bug as :param passwd: Password :param project: Jira project """ if user and passwd and project: try: jira = JIRA(server='https://issues.voltdb.com/', basic_auth=(user, passwd), options=dict(verify=False)) except: self.logger.exception('Could not connect to Jira') return else: self.logger.error( 'Did not provide either a Jira user, a Jira password or a Jira project' ) return # Check for existing bugs for the same test case, if there are any, suppress filing another test_case = summary.split(' ')[0] existing = jira.search_issues( 'summary ~ \'%s\' and labels = automatic and status != Closed' % test_case) if len(existing) > 0: # Already reported self.logger.info('Found open issue(s) for "' + test_case + '" ' + ' '.join([k.key for k in existing])) return issue_dict = { 'project': project, 'summary': summary, 'description': description, 'issuetype': { 'name': 'Bug' }, 'labels': labels } jira_component = None components = jira.project_components(project) for c in components: if c.name == component: jira_component = {'name': c.name, 'id': c.id} break if jira_component: issue_dict['components'] = [jira_component] else: # Components is still a required field issue_dict['components'] = ['Core'] jira_version = None versions = jira.project_versions(project) version = 'V' + version for v in versions: if str(v.name) == version.strip(): jira_version = {'name': v.name, 'id': v.id} break if jira_version: issue_dict['versions'] = [jira_version] else: # Versions is still a required field issue_dict['versions'] = ['DEPLOY-Integration'] issue_dict['fixVersions'] = [{'name': 'Backlog'}] issue_dict['priority'] = {'name': 'Blocker'} self.logger.info("Filing ticket: %s" % summary) if not DRY_RUN: new_issue = jira.create_issue(fields=issue_dict) #self.logger.info('NEW: Reported issue with summary "' + summary + '"') if self.connect_to_slack(): self.post_message( channel, 'Opened issue at https://issues.voltdb.com/browse/' + new_issue.key) else: new_issue = None return new_issue
def create_bug_issue(self, channel, summary, description, component, version, labels, attachments={}, user=JIRA_USER, passwd=JIRA_PASS, project=JIRA_PROJECT, DRY_RUN=False): """ Creates a bug issue on Jira :param channel: The channel to notify :param summary: The title summary :param description: Description field :param component: Component bug affects :param version: Version this bug affects :param labels: Labels to attach to the issue :param user: User to report bug as :param passwd: Password :param project: Jira project """ def add_attachments(jira, ticketId, attachments): for file in attachments: urlretrieve(attachments[file], file) jira.add_attachment(ticketId, os.getcwd() + '/' + file, file) os.unlink(file) if user and passwd and project: try: jira = JIRA(server='https://issues.voltdb.com/', basic_auth=(user, passwd), options=dict(verify=False)) except: self.logger.exception('Could not connect to Jira') return else: self.logger.error( 'Did not provide either a Jira user, a Jira password or a Jira project' ) return # Check for existing bugs for the same test case, if there are any, suppress filing another test_case = summary.split(' ')[0] existing = jira.search_issues( 'summary ~ \'%s\' and labels = automatic and status != Closed' % test_case) if len(existing) > 0: self.logger.info('Found open issue(s) for "' + test_case + '" ' + ' '.join([k.key for k in existing])) # Check if new failure is on different job than existing ticket, if so comments job = summary.split()[-2] existing_ticket = jira.issue(existing[0].id) if job not in existing_ticket.fields.summary: comments = jira.comments(existing[0].id) for comment in comments: # Check for existing comment for same job, if there are any, suppress commenting another if job in comment.body: self.logger.info('Found existing comment(s) for "' + job + '" on open issue') return self.logger.info( 'Commenting about separate job failure for %s on open issue' % test_case) if not DRY_RUN: jira.add_comment(existing[0].id, summary + '\n\n' + description) add_attachments(jira, existing[0].id, attachments) return issue_dict = { 'project': project, 'summary': summary, 'description': description, 'issuetype': { 'name': 'Bug' }, 'labels': labels } jira_component = None components = jira.project_components(project) for c in components: if c.name == component: jira_component = {'name': c.name, 'id': c.id} break if jira_component: issue_dict['components'] = [jira_component] else: # Components is still a required field issue_dict['components'] = ['Core'] jira_version = None versions = jira.project_versions(project) version = 'V' + version for v in versions: if str(v.name) == version.strip(): jira_version = {'name': v.name, 'id': v.id} break if jira_version: issue_dict['versions'] = [jira_version] else: # Versions is still a required field issue_dict['versions'] = ['DEPLOY-Integration'] issue_dict['fixVersions'] = [{'name': 'Backlog'}] issue_dict['priority'] = {'name': 'Blocker'} self.logger.info("Filing ticket: %s" % summary) if not DRY_RUN: new_issue = jira.create_issue(fields=issue_dict) add_attachments(jira, new_issue.id, attachments) #self.logger.info('NEW: Reported issue with summary "' + summary + '"') if self.connect_to_slack(): self.post_message( channel, 'Opened issue at https://issues.voltdb.com/browse/' + new_issue.key) suite = summary.split('.')[-3] # Find all tickets within same test suite and link them link_tickets = jira.search_issues( 'summary ~ \'%s\' and labels = automatic and status != Closed and reporter in (voltdbci)' % suite) for ticket in link_tickets: jira.create_issue_link('Related', new_issue.key, ticket) else: new_issue = None return new_issue
class JiraReporter: """Main interface for creating and searching issues on JIRA""" JQL_NOT_IN_STATUS = "status != '{status}'" JQL_DESCRIPTION_CONTAINS = "description ~ '{hash_value}'" STATUS_CLOSED = 'Closed' STATUS_OPEN = 'Open' def __init__(self): self._projects = CFG['projects'] self._jira = JIRA(server=CFG['url'], basic_auth=(CFG['user'], CFG['password'])) def existing_issue(self, issue_hash: str): """Checks if JIRA issues with issue_hash in description exists. Searches for 'Open' JIRA issues which contains issue_hash in their description. Args: issue_hash: hash that will be searched in JIRA description field Returns: first JIRA issue which is not in status 'Closed' and contains issue_hash or None """ issues = self._search_for_issues(issue_hash, self.STATUS_CLOSED) if issues: return issues[0] else: return None def create_or_update_issue(self, issue): """Creates an JIRA issue or updates and existing one Creates JIRA issue at project taken from configuration file. Args: issue: an instance of kapajira.jira.Issue class """ existing_issue = self.existing_issue(issue.get_issue_hash()) issue_dict = { 'project': self._projects[0], 'summary': issue.get_summary(), 'description': issue.get_description(), 'issuetype': issue.get_issue_type(), 'labels': issue.get_labels() } if issue.get_component() is not None: for project in self._projects: if self._component_exists(issue.get_component(), project): issue_dict['project'] = project issue_dict['components'] = [{ 'name': issue.get_component() }] break if existing_issue is not None: existing_issue.update( fields={'description': issue_dict['description']}) else: self._jira.create_issue(fields=issue_dict) def _search_for_issues(self, issue_hash, status): jql_query = ' AND '.join([ self.JQL_DESCRIPTION_CONTAINS.format(hash_value=issue_hash), self.JQL_NOT_IN_STATUS.format(status=status) ]) return self._jira.search_issues(jql_query) def _component_exists(self, component_name: str, project: str) -> bool: components_for_project = self._jira.project_components(project=project) for x in components_for_project: if component_name == x.name: return True return False
def create_bug_issue(self, channel, summary, description, component, version, labels, user=JIRA_USER, passwd=JIRA_PASS, project=JIRA_PROJECT): """ Creates a bug issue on Jira :param channel: The channel to notify :param summary: The title summary :param description: Description field :param component: Component bug affects :param version: Version this bug affects :param labels: Labels to attach to the issue :param user: User to report bug as :param passwd: Password :param project: Jira project """ if user and passwd and project: try: jira = JIRA(server='https://issues.voltdb.com/', basic_auth=(user, passwd)) except: self.logger.exception('Could not connect to Jira') return else: self.logger.error('Did not provide either a Jira user, a Jira password or a Jira project') return # Check for existing bug with same summary existing = jira.search_issues('summary ~ \'%s\'' % summary) if len(existing) > 0: # Already reported self.logger.info('OLD: Already reported issue with summary "' + summary + '"') return issue_dict = { 'project': project, 'summary': summary, 'description': description, 'issuetype': { 'name': 'Bug' }, 'labels': labels } jira_component = None components = jira.project_components(project) for c in components: if c.name == component: jira_component = { 'name': c.name, 'id': c.id } break if jira_component: issue_dict['components'] = [jira_component] else: # Components is still a required field issue_dict['components'] = ['Core'] jira_version = None versions = jira.project_versions(project) version = 'V' + version for v in versions: if str(v.name) == version.strip(): jira_version = { 'name': v.name, 'id': v.id } break if jira_version: issue_dict['versions'] = [jira_version] else: # Versions is still a required field issue_dict['versions'] = ['DEPLOY-Integration'] issue_dict['fixVersions'] = [{'name':'Backlog'}] issue_dict['priority'] = {'name': 'Blocker'} new_issue = jira.create_issue(fields=issue_dict) self.logger.info('NEW: Reported issue with summary "' + summary + '"') if self.connect_to_slack(): self.post_message(channel, 'Opened issue at https://issues.voltdb.com/browse/' + new_issue.key)
def create_issues_in_jira(*, issue_dicts, server_url, basic_auth, project_name, board_name, assignee_key, components, epic_link, max_results): jira = JIRA(server=server_url, basic_auth=basic_auth) def get_board(board_name): for board in jira.boards(): if board.name == board_name: return board return None def get_project(project_name): for project in jira.projects(): if project.name == project_name: return project return None project = get_project(project_name) if not project: raise Exception('project not found: \'{}\''.format(board_name)) board = get_board(board_name) if not board: raise Exception('board not found: \'{}\''.format(board_name)) component_ids = None if components is not None: component_objs = jira.project_components(project) def get_component(component_name): for component_obj in component_objs: if component_obj.name == component_name: return component_obj return None component_ids = [] for component in components: component_obj = get_component(component) if component_obj is None: raise Exception( 'component not found: \'{}\''.format(component)) component_ids.append(component_obj.id) if epic_link: search_result = jira.search_issues( 'summary ~ "{}"'.format(f'\\"{epic_link}\\"')) if len(search_result) == 0: raise Exception('epic not found: \'{}\''.format(epic_link)) elif len(search_result) > 1: raise Exception( 'more than one epic found for name \'{}\''.format(epic_link)) epic = search_result[0] else: epic = None create_issues_results = [] def _create_issue(issue_dict, parent=None): fields_list = [{ 'project': { 'key': project.key }, 'summary': issue_dict['summary'], 'description': issue_dict['description'], 'issuetype': { 'name': 'Task' if parent is None else 'Sub Task' }, }] if parent is None: fields_list[0]['assignee'] = { 'name': issue_dict['assignee'] if issue_dict['assignee'] else assignee_key } if component_ids is not None: fields_list[0]['components'] = [{ 'id': component_id } for component_id in component_ids] if parent is not None: fields_list[0]['parent'] = {'key': parent.key} results = jira.create_issues(fields_list) assert len(results) == 1 result = results[0] issue_obj = result['issue'] for sub_issue_dict in issue_dict['sub_issues']: _create_issue(sub_issue_dict, issue_obj) create_issues_results.append( dict(issue_dict=issue_dict, issue_obj=issue_obj)) for issue_dict in issue_dicts: _create_issue(issue_dict) if epic is not None: jira.add_issues_to_epic(epic.id, [ create_issues_result['issue_obj'].key for create_issues_result in create_issues_results if create_issues_result['issue_obj'].fields.issuetype.name == 'Task' ]) issues_to_add_to_sprint = [ create_issues_result['issue_obj'].key for create_issues_result in create_issues_results if create_issues_result['issue_dict'].get('add_to_sprint', False) ] if issues_to_add_to_sprint: sprints = jira.sprints(board.id, extended=['startDate', 'endDate'], maxResults=max_results) if len(sprints) == 0: raise Exception('There\'s no open sprint') last_sprint = sprints[-1] jira.add_issues_to_sprint(last_sprint.id, issues_to_add_to_sprint)
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
def report_issue(self, build): try: jira = JIRA(server='https://issues.voltdb.com/', basic_auth=(JIRA_USER, JIRA_PASS)) except: logging.exception('Could not connect to Jira') return build_report_url = self.jhost + '/job/' + job + '/' + str(build) + '/api/python' build_report = eval(self.read_url(build_report_url)) build_url = build_report.get('url') build_result = build_report.get('result') if build_result == 'SUCCESS': # only generate Jira issue if the test fails print 'No new issue created. Build ' + str(build) + 'resulted in: ' + build_result return summary_url = self.jhost + '/job/' + job + '/' + str(build) + '/artifact/tests/sqlgrammar/summary.out' summary_report = self.read_url(summary_url) pframe_split = summary_report.split('Problematic frame:') pframe_split = pframe_split[1].split('C') pframe_split = pframe_split[1].split(']') pframe_split = pframe_split[1].split('#') pframe = pframe_split[0].strip() summary = job + ':' + str(build) + ' - ' + pframe # search_issues gets a parsing error on (), so escape it. existing = jira.search_issues('summary ~ \'%s\'' % summary.replace('()','\\\\(\\\\)',10)) if len(existing) > 0: print 'No new Jira issue created. Build ' + str(build) + ' has already been reported.' return 'Already reported' old_issue = '' existing = jira.search_issues('summary ~ \'%s\'' % pframe_split[0].strip().replace('()','\\\\(\\\\)',10)) for issue in existing: if str(issue.fields.status) != 'Closed' and u'grammar-gen' in issue.fields.labels: old_issue = issue build_artifacts = build_report.get('artifacts')[0] pid_fileName = build_artifacts['fileName'] pid_url = build_url + 'artifact/' + pid_fileName query_split = summary_report.split('(or it was never started??), after SQL statement:') crash_query = query_split[1] hash_split = summary_report.split('#', 1) hash_split = hash_split[1].split('# See problematic frame for where to report the bug.') sigsegv_message = hash_split[0] + '# See problematic frame for where to report the bug.\n#' description = job + ' build ' + str(build) + ' : ' + str(build_result) + '\n' \ + 'Jenkins build: ' + build_url + ' \n \n' \ + 'DDL: ' + 'https://github.com/VoltDB/voltdb/blob/master/tests/sqlgrammar/DDL.sql' + ' \n \n' \ + 'hs_err_pid: ' + pid_url + ' \n \n' \ + 'SIGSEGV Message: \n' + '#' + sigsegv_message + ' \n \n' \ + 'Query that Caused the Crash: ' + crash_query description = description.replace('#', '\#') labels = ['grammar-gen'] component = 'Core' components = jira.project_components(JIRA_PROJECT) jira_component = {} for c in components: if c.name == component: jira_component = { 'name': c.name, 'id': c.id } break current_version_raw = str(self.read_url('https://raw.githubusercontent.com/VoltDB/voltdb/master/version.txt')) current_version_float = float(current_version_raw) current_version = 'V' + current_version_raw current_version = current_version.strip() next_version = current_version_float + .1 next_version = str(next_version) next_version = 'V' + next_version next_version = next_version[:4] jira_versions = jira.project_versions(JIRA_PROJECT) this_version = {} new_version = {} for v in jira_versions: if str(v.name) == current_version: this_version = { 'name': v.name, 'id': v.id } if str(v.name) == next_version: new_version = { 'name': v.name, 'id': v.id } issue_dict = { 'project': JIRA_PROJECT, 'summary': summary, 'description': description, 'issuetype': {'name': 'Bug'}, 'priority': {'name': 'Blocker'}, 'labels': labels, 'customfield_10430': {'value': 'CORE team'}, 'components': [jira_component] } if new_version: issue_dict['versions'] = [new_version] issue_dict['fixVersions'] = [new_version] elif this_version: issue_dict['versions'] = [this_version] issue_dict['fixVersions'] = [this_version] if old_issue: new_comment = jira.add_comment(old_issue, description) print 'JIRA-action: New comment on issue: ' + str(old_issue) + ' created for failure on build ' + str(build) else: new_issue = jira.create_issue(fields=issue_dict) print 'JIRA-action: New issue ' + new_issue.key + ' created for failure on build ' + str(build)
class Util(object): """ """ def __init__(self, **kwargs): """ """ if 'config' in kwargs: self._config = kwargs['config'] else: logging.critical("config was not defined") raise Exception("config was not defined") if 'username' in kwargs: self._username = kwargs['username'] else: logging.critical("username was not defined") raise Exception("username was not defined") if 'password' in kwargs: self._password = kwargs['password'] else: logging.critical("password was not defined") raise Exception("password was not defined") if 'project' in kwargs: self._project = kwargs['project'] else: if 'project' in self._config: self._project = self._config['project'] logging.info( "project was set to '%s' from the configuration file" % self._project) else: self._project = DEFAULT_PROJECT logging.info("project was set to default '%s'" % self._project) if 'base_url' in kwargs: self._base_url = kwargs['base_url'] else: if 'base_url' in self._config: self._base_url = self._config['base_url'] logging.info( "base_url was set to '%s' from the configuration file" % self._base_url) else: self._base_url = DEFAULT_BASE_URL logging.info("base_url was set to default '%s'" % self._base_url) if 'add_missing_watchers' in kwargs: self._add_missing_watchers = kwargs['add_missing_watchers'] else: if 'add_missing_watchers' in self._config: self._add_missing_watchers = self._config[ 'add_missing_watchers'] logging.info( "add_missing_watchers was set to '%s' from the configuration file" % self._add_missing_watchers) else: self._add_missing_watchers = DEFAULT_ADD_MISSING_WATCHERS logging.info("add_missing_watchers was set to default '%s'" % self._add_missing_watchers) self._jira = None self._jra = None self._initialize() def setProject(self, project): """ :param project: :return: """ self._project = project def setAddMissingWatchers(self, add_missing_watchers): """ :param add_missing_watchers: :return: """ self._add_missing_watchers = add_missing_watchers def _initialize(self): """ :return: """ print("Attempting to connect to JIRA at '%s'" % self._base_url) self._jira = JIRA(self._base_url, basic_auth=(self._username, self._password)) print("Attempting to retrieve info for project '%s'" % self._project) self._jra = self._jira.project(self._project) def getReport(self): """ :return: """ self.report_misc() self.report_components() self.report_roles() self.report_versions() self.report_open_issues() def report_misc(self): """ :return: """ print(Fore.BLUE + "Project name '%s'" % self._jra.name) print(Fore.BLUE + "Project lead '%s'" % self._jra.lead.displayName) print(Style.RESET_ALL) def report_components(self): """ :return: """ components = self._jira.project_components(self._jra) if len(components) > 0: print(Fore.BLUE + "Here are the components") print(Style.RESET_ALL) for c in components: print(c.name) else: print(Fore.RED + "There are no components") print(Style.RESET_ALL) def report_roles(self): """ :return: """ roles = self._jira.project_roles(self._jra) if len(roles) > 0: print(Fore.BLUE + "Here are the roles") print(Style.RESET_ALL) for r in roles: print(r) else: print(Fore.RED + "There are no roles") print(Style.RESET_ALL) def report_versions(self): """ :return: """ versions = self._jira.project_versions(self._jra) if len(versions) > 0: print(Fore.BLUE + "Here are the versions") print(Style.RESET_ALL) for v in reversed(versions): print(v.name) else: print(Fore.RED + "There are no versions") print(Style.RESET_ALL) def report_watchers(self, issue): """ :param issue: :return: """ watcher = self._jira.watchers(issue) print("Issue '%s' has '%d' watcher(s)" % (issue.key, watcher.watchCount)) current_watchers_email = {} for watcher in watcher.watchers: current_watchers_email[watcher.emailAddress] = True print("'%s' - '%s'" % (watcher, watcher.emailAddress)) # watcher is instance of jira.resources.User: # print(watcher.emailAddress) for watcher_email in self._config['members_email_lookup']: if not watcher_email in current_watchers_email: print(Fore.RED + "member '%s' needs to be added as a watcher to '%s'" % (watcher_email, issue.key)) username = self._config['members_email_lookup'][watcher_email] print("Need to add username '%s'" % username) print(Style.RESET_ALL) if self._add_missing_watchers: self._jira.add_watcher(issue, username) print("Exiting") sys.exit(0) print(Style.RESET_ALL) def checkWatchers(self): """ :return: """ issues = self._jira.search_issues('project= LO AND status != Done', maxResults=DEFAULT_MAX_RESULTS) if len(issues) > 0: for issue in issues: self.report_watchers(issue) def report_open_issues(self): issues = self._jira.search_issues('project= LO AND status != Done', maxResults=DEFAULT_MAX_RESULTS) if len(issues) > 0: print(Fore.BLUE + "Found the following '%d' open issues" % len(issues)) print(Style.RESET_ALL) for issue in issues: summary = issue.fields.summary id = issue.id key = issue.key print("id '%s' key '%s' summary : '%s'" % (id, key, summary)) if DEFAULT_REPORT_WATCHERS: self._report_watchers(issue) print(Style.RESET_ALL) def getComments(self, key): """ :param key: :return: """ logging.info("Attempting to retrieve the issue with key '%s'" % key) issues = self._jira.search_issues('key = ' + key) if len(issues) > 1: raise Exception("Expected only one issue for '%s' but found '%d'" % (key, len(issues))) if len(issues) == 1: # comments = issues[0].fields.comment.comments # comments = issues[0].raw['fields']['comment']['comments'] comments = self._jira.comments(issues[0]) if len(comments) > 0: print("Found the following '%d' comments" % len(comments)) comment_ctr = 0 for comment_id in comments: print("-----------------------------------") comment_ctr += 1 comment = self._jira.comment(key, comment_id) author = comment.author.displayName date_created = comment.created body = comment.body print(Fore.BLUE + "%d. author '%s' date '%s'" % (comment_ctr, author, date_created)) print(Style.RESET_ALL) print(body)
##Get arguments. parser = argparse.ArgumentParser( description= 'You have to specify 2 arguments that describes source project and destionation peroject.' ) parser.add_argument('--source', action='store', help='Source Project') parser.add_argument('--dest', action='store', help='Destination Project') args = parser.parse_args() if not args.source or not args.dest: parser.print_help() sys.exit() ##Acess Jira and get info. jira = JIRA(server=server, basic_auth=(username, password)) projSrc = jira.project_components(args.source) projDst = jira.project_components(args.dest) ###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)):
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 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!')
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
failureDetails = failureDetails + '{code:title=' + testurl + '' + className_re + '/' + name_re + '}\n' failureDetails = failureDetails + prettyXML(s) failureDetails = failureDetails + '\n{code}\n\n' #print failureDetails rootJBIDE_dict = { 'project' : { 'key': 'JBIDE' }, 'summary' : str(len(testcaselist)) + ' Test Failure(s) in JBIDE ' + jbide_affectedversion + ' for ' + component + ' component', 'description' : failureSummary + failureDetails, 'issuetype' : { 'name' : 'Task' }, 'priority' : { 'name' :'Critical'}, 'versions' : [{ "name" : jbide_affectedversion }], 'components' : [{ "name" : component }], 'labels' : [ "testfailure" ] } jira = JIRA(options={'server':jiraserver}, basic_auth=(options.usernameJIRA, options.passwordJIRA)) CLJBIDE = jira.project_components(jira.project('JBIDE')) # full list of components in JBIDE rootJBIDE = jira.create_issue(fields=rootJBIDE_dict) componentLead = queryComponentLead(CLJBIDE, component, 0) try: jira.assign_issue(rootJBIDE, componentLead) except: print "[WARNING] Unexpected error! User {0} tried to assign {1} to {2}: {3}".format(options.usernameJIRA, rootJBIDE, componentLead, sys.exc_info()[0]) accept = raw_input("\nAccept new JIRA " + jiraserver + '/browse/' + rootJBIDE.key + " => " + componentLead + " ? [Y/n] ") if accept.capitalize() in ["N"] : rootJBIDE.delete() # see JIRA_components listing in components.py # Sample usage: see createTestFailureJIRA.py.examples.txt
def report_issue(self, build): try: jira = JIRA(server='https://issues.voltdb.com/', basic_auth=(JIRA_USER, JIRA_PASS), options=dict(verify=False)) except: logging.exception('Could not connect to Jira') return build_report_url = self.jhost + '/job/' + job + '/' + str( build) + '/api/python' build_report = eval(self.read_url(build_report_url)) build_url = build_report.get('url') build_result = build_report.get('result') if build_result == 'SUCCESS': # only generate Jira issue if the test fails print 'No new issue created. Build ' + str( build) + 'resulted in: ' + build_result return summary_url = self.jhost + '/job/' + job + '/' + str( build) + '/artifact/summary.out' summary_report = self.read_url(summary_url) pframe_split = summary_report.split('Problematic frame:') pframe_split = pframe_split[1].split('C') pframe_split = pframe_split[1].split(']') pframe_split = pframe_split[1].split('#') pframe = pframe_split[0].strip() summary = job + ':' + str(build) + ' - ' + pframe # search_issues gets a parsing error on (), so escape it. existing = jira.search_issues('summary ~ \'%s\'' % summary.replace('()', '\\\\(\\\\)', 10)) if len(existing) > 0: print 'No new Jira issue created. Build ' + str( build) + ' has already been reported.' return 'Already reported' old_issue = '' existing = jira.search_issues( 'summary ~ \'%s\'' % pframe_split[0].strip().replace('()', '\\\\(\\\\)', 10)) for issue in existing: if str(issue.fields.status ) != 'Closed' and u'grammar-gen' in issue.fields.labels: old_issue = issue build_artifacts = build_report.get('artifacts')[0] pid_fileName = build_artifacts['fileName'] pid_url = build_url + 'artifact/' + pid_fileName query_split = summary_report.split( '(or it was never started??), after SQL statement:') crash_query = query_split[1] hash_split = summary_report.split('#', 1) hash_split = hash_split[1].split( '# See problematic frame for where to report the bug.') sigsegv_message = hash_split[ 0] + '# See problematic frame for where to report the bug.\n#' description = job + ' build ' + str(build) + ' : ' + str(build_result) + '\n' \ + 'Jenkins build: ' + build_url + ' \n \n' \ + 'DDL: ' + 'https://github.com/VoltDB/voltdb/blob/master/tests/sqlgrammar/DDL.sql' + ' \n \n' \ + 'hs_err_pid: ' + pid_url + ' \n \n' \ + 'SIGSEGV Message: \n' + '#' + sigsegv_message + ' \n \n' \ + 'Query that Caused the Crash: ' + crash_query description = description.replace('#', '\#') labels = ['grammar-gen'] component = 'Core' components = jira.project_components(JIRA_PROJECT) jira_component = {} for c in components: if c.name == component: jira_component = {'name': c.name, 'id': c.id} break current_version_raw = str( self.read_url( 'https://raw.githubusercontent.com/VoltDB/voltdb/master/version.txt' )) current_version_float = float(current_version_raw) current_version = 'V' + current_version_raw current_version = current_version.strip() next_version = current_version_float + .1 next_version = str(next_version) next_version = 'V' + next_version next_version = next_version[:4] jira_versions = jira.project_versions(JIRA_PROJECT) this_version = {} new_version = {} for v in jira_versions: if str(v.name) == current_version: this_version = {'name': v.name, 'id': v.id} if str(v.name) == next_version: new_version = {'name': v.name, 'id': v.id} issue_dict = { 'project': JIRA_PROJECT, 'summary': summary, 'description': description, 'issuetype': { 'name': 'Bug' }, 'priority': { 'name': 'Blocker' }, 'labels': labels, 'customfield_10430': { 'value': 'CORE team' }, 'components': [jira_component] } if new_version: issue_dict['versions'] = [new_version] issue_dict['fixVersions'] = [new_version] elif this_version: issue_dict['versions'] = [this_version] issue_dict['fixVersions'] = [this_version] if old_issue: new_comment = jira.add_comment(old_issue, description) print 'JIRA-action: New comment on issue: ' + str( old_issue) + ' created for failure on build ' + str(build) else: new_issue = jira.create_issue(fields=issue_dict) print 'JIRA-action: New issue ' + new_issue.key + ' created for failure on build ' + str( build)
jbide_fixversion = options.jbidefixversion jbds_fixversion = options.jbdsfixversion if options.fusefixversion: fuse_fixversion = options.fusefixversion else: # set fuse fixversion a.b.c.d = JBDS fixversion (a-1).b.c.d fuse_bits = jbds_fixversion.split(".") fuse_fixversion = str( int(fuse_bits[0]) - 1) + "." + fuse_bits[1] + "." + fuse_bits[2] + "." + fuse_bits[3] # sys.exit("jbds_fixversion = " + jbds_fixversion + "\n" + "fuse_fixversion = " + fuse_fixversion) # debug jiraserver = options.jiraserver jira = JIRA(options={'server': jiraserver}, basic_auth=(options.jirauser, options.jirapwd)) CL = jira.project_components( jira.project('JBIDE')) # full list of components in JBIDE from components import checkFixVersionsExist, queryComponentLead, defaultAssignee def setComponentLead(theissue, componentLead): try: jira.assign_issue(theissue, componentLead) except: print "[WARNING] Unexpected error! User {0} tried to assign {1} to {2}: {3}".format( options.jirauser, theissue, componentLead, sys.exc_info()[0]) def setTaskLabel(theissue): try:
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())
print("Watchers:", watcher.emailAddress) print("") print("CreatedOn:", issue.fields.created) print("Updated:", issue.fields.updated) print("Components:", issue.fields.components) print("Labels:", issue.fields.labels) print("--------------------------\n") #-------------Get list of all SSO and LMS components in project------------- jiraProject = jira.project('LLSTC') components = jira.project_components(jiraProject) componentList = [] if debug: print("-------------beginning of LMS Components------------\n") for c in components: if "LMS" in c.name: componentList.append(c.name) if debug: print(c.name) print("-------------end of LMS Components------------\n") print("-------------beginning of Auth Components------------\n") for c in components: if "SSO" in c.name: componentList.append(c.name) if debug: print(c.name)
class AntonJira(): def __init__(self, server, email, apitoken): self.jira_instance = JIRA(server=server, basic_auth=(email, apitoken)) def get_project(self, project_key): """ name -- project name lead.displayName -- leader name """ return self.jira_instance.project(project_key) def get_project_components(self, project): """ requires : project instance returns : list of components """ return [ comp.name for comp in self.jira_instance.project_components(project) ] def get_issue_detail(self, issue_key): """ .fields.summary -- summary .fields.votes.votes -- votes .fields.description .fields.comment : [{ .author.displayName .body }] """ return self.jira_instance.issue(issue_key) def get_issues_in_project(self, project_key): """ [ .fields.summary -- summary .fields.votes.votes -- votes .fields.description .fields.comment : [{ .author.displayName .body }] ] """ return self.jira_instance.search_issues(f"project={project_key}") def get_issues_of_current_user_in_project(self, project_key): """ [ .fields.summary -- summary .fields.votes.votes -- votes .fields.description .fields.comment : [{ .author.displayName .body }] ] """ return self.jira_instance.search_issues( f"project={project_key} and assignee=currentuser()") def get_issues_of_current_user(self): """ [ .fields.summary -- summary .fields.votes.votes -- votes .fields.description .fields.comment : [{ .author.displayName .body }] ] """ return self.jira_instance.search_issues(f"assignee=currentuser()")