def send_tickets(grouped_by_groupd_id, project, login, password): options = {"server": "https://jira.onap.org"} jira = JIRA(options, auth=(login, password)) epic_dict = { 'project': project, 'summary': "The subsequent stories are generated per group id of outdated dependencies", 'description': "Outdated dependencies", 'customfield_10003': 'Outdated dependencies', 'issuetype': {'name': 'Epic'}, } epic_issue = jira.create_issue(epic_dict) for key in grouped_by_groupd_id: description = "" summary = "[automatically generated] Outdated dependency " + str(key) for i_key in grouped_by_groupd_id[key]: description += "\n-= Take this result with a grain of salt, use your own judgment, and check for CVE =-\n" description += "\nartifact: " + str(i_key['declared']['artifact_id']) description += "\npackage: " + str(i_key['declared']['package']) description += "\ndeclared version: " + str(i_key['declared']['version']) description += "\nlatest version: " + str(i_key['latest_version']) description += "\nuri checked: " + str(i_key['uri']) issue = jira.create_issue(project=project, description=description, summary=summary, issuetype={'name': 'Story'}) jira.add_issues_to_epic(epic_issue.id, [issue.key]) print("created: " + str(issue.permalink()))
def add_issue(find, push_to_jira): eng = Engagement.objects.get(test=find.test) prod = Product.objects.get(engagement= eng) jpkey = JIRA_PKey.objects.get(product=prod) jira_conf = jpkey.conf if push_to_jira: if 'Active' in find.status() and 'Verified' in find.status(): jira = JIRA(server=jira_conf.url, basic_auth=(jira_conf.username, jira_conf.password)) new_issue = jira.create_issue(project=jpkey.project_key, summary=find.title, description=find.long_desc(), issuetype={'name': 'Bug'}, priority={'name': jira_conf.get_priority(find.severity)}) j_issue = JIRA_Issue(jira_id=new_issue.id, jira_key=new_issue, finding = find) j_issue.save() if jpkey.enable_engagement_epic_mapping: epic = JIRA_Issue.objects.get(engagement=eng) issue_list = [j_issue.jira_id,] jira.add_issues_to_epic(epic_id=epic.jira_id, issue_keys=[str(j_issue.jira_id)], ignore_epics=True)
def test_jira_api(args): logger.info(str(args)) if not isinstance(g.identity, AnonymousIdentity): raw_ua = request.headers.get('User-Agent') or '' ua = user_agents.parse(raw_ua) args['os'] = ua.os.family args['browser'] = ua.browser.family args['device'] = ua.device.family args['ua'] = raw_ua from jira import JIRA HOST = current_app.config['JIRA_HOST'] BASIC_AUTH_USER = current_app.config['BASIC_AUTH_USER'] BASIC_AUTH_PASSWORD = current_app.config['BASIC_AUTH_PASSWORD'] PROJECT = current_app.config['PROJECT'] EPIC = current_app.config['EPIC'] # 权限 jira = JIRA(HOST, basic_auth=(BASIC_AUTH_USER, BASIC_AUTH_PASSWORD)) # 新建issue new_issue = jira.create_issue(project=PROJECT, summary=args['summary'], description=args.get('description', ''), issuetype={'name': args['type']}) # 添加issue到epic jira.add_issues_to_epic(EPIC, [str(new_issue.key)]) # 添加额外信息到comment display_list = ['summary', 'description', 'type', 'file'] comment_body = ''.join( ['%s:%s \n' % (k, args[k]) for k in args if k not in display_list]) jira.add_comment(new_issue.key, comment_body) if comment_body else "" # 添加附件 data = request.files.getlist("file") current_app.logger.info(type(data)) if data: for file_data in data: current_app.logger.info(file_data.filename) binary = file_data.stream.read() if binary: jira.add_attachment(issue=new_issue, attachment=binary, filename=str(uuid.uuid1()) + '.' + file_data.filename.split(".")[-1])
class JIRAUtilities: # Translate state changes to JIRA transition values transition_ids = {'ToDo': '11', 'Prog': '21', 'Imped': '41', 'Done': '31'} user_story_dict_cloud = { #'project': 'SP', #'project': 'TTP', # Tsafi, 15 Jan 2020 'project': DEFAULT_JIRA_PARAMS[JIRA_INST]['PROJECT'], # Tsafi 3 Feb 2020 'issuetype': 'Story', 'summary': "Story 302", 'customfield_10322': [{ 'value': 'Dev Team 1' }] # Team field ********cloud } user_story_dict_vm = { #'project': 'SP', #'project': 'TTP', #Tsafi 14 Jan 2020 - temp change to work with TTP project already exists on local Jira 'project': DEFAULT_JIRA_PARAMS[JIRA_INST]['PROJECT'], # Tsafi 3 Feb 2020 'issuetype': 'Story', 'summary': "Story 302", #'customfield_10201': [{'value': 'Dev Team 1'}] # Team field vm 'customfield_10200': [{ 'value': 'Dev Team 1' }] # Tsafi 21 Jan 2020 } # 10120 is the 'Epic Name' and it's a must have epic_dict_cloud = { #'project': 'SP', #'project': 'TTP', # Tsafi, 15 Jan 2020 'project': DEFAULT_JIRA_PARAMS[JIRA_INST]['PROJECT'], # Tsafi 3 Feb 2020 #'issuetype': 'Epic', 'issuetype': { 'name': 'Epic' }, # Tsafi 15 Jan 2020 'summary': "Epic 1", 'customfield_10120': "Epic 1", # Epic name is the same as Epic summary ********cloud 'customfield_10322': [{ 'value': 'Dev Team 1' }] # Team field ********cloud } epic_dict_vm = { 'project': DEFAULT_JIRA_PARAMS[JIRA_INST]['PROJECT'], # Tsafi 3 Feb 2020 'issuetype': 'Epic', 'summary': "Epic 1", 'customfield_10103': "Epic 1", # Epic name is the same as Epic summary vm #'customfield_10102': "Epic 1", # Tsafi 14 Jan 2020 'customfield_10200': [{ 'value': 'Dev Team 1' }] # Tsafi 21 Jan 2020 - Team field vm } def __init__(self, instance_type): self.instance_type = instance_type if instance_type == 'cloud': # Using Nela's Jira cloud account #self.jira_inst = JIRA(basic_auth=('*****@*****.**', 'FiJBeI3H81sceRofBcY4E84E'), # options={'server': 'https://dr-agile.atlassian.net'}) # Using Tsafi's Jira cloud account #self.jira_inst = JIRA(basic_auth=('*****@*****.**', 'tDshA7M9zEhMkaiC13RA146E'), # options={'server': 'https://dr-agile.atlassian.net'}) self.jira_inst = JIRA( basic_auth=(DEFAULT_JIRA_PARAMS['CLOUD']['USER'], DEFAULT_JIRA_PARAMS['CLOUD']['PASS']), options={'server': DEFAULT_JIRA_PARAMS['CLOUD']['URL']}) else: # Using Nela's Jira local VM #self.jira_inst = JIRA(basic_auth=('nela.g', 'q1w2e3r4'), options={'server': 'http://192.168.56.101:8080'}) # Using Tsafi's Jira local VM #self.jira_inst = JIRA(basic_auth=('tsafrir.m', 'Sim1965'), options={'server': 'http://192.168.43.55:8080'}) #self.jira_inst = JIRA(basic_auth=('tsafrir.m', 'Sim1965'), options={'server': 'http://192.168.43.41:8080'}) #self.jira_inst = JIRA(basic_auth=('tsafrir.m', 'Sim1965'), options={'server': 'http://10.0.0.61:8080'}) self.jira_inst = JIRA( basic_auth=(DEFAULT_JIRA_PARAMS['LOCAL']['USER'], DEFAULT_JIRA_PARAMS['LOCAL']['PASS']), options={'server': DEFAULT_JIRA_PARAMS['LOCAL']['URL']}) def __del__(self): del self.jira_inst # Creation def create_epic(self, name, team): if self.instance_type == 'cloud': dict = self.epic_dict_cloud dict['customfield_10120'] = name # ********cloud dict['customfield_10322'] = [{'value': team.name}] # ********cloud else: dict = self.epic_dict_vm dict['customfield_10103'] = name # tsafi 21 Jan 2020 #dict['customfield_10102'] = name # Tsafi 14 Jan 2020 #dict['customfield_10201'] = {'value': team.name} dict['customfield_10200'] = { 'value': team.name } # Tsafi 21 Jan 2020 dict['summary'] = name epic = self.jira_inst.create_issue(fields=dict) return epic def create_user_story_with_epic(self, user_story_name, team, epic_key=None): if self.instance_type == 'cloud': dict = self.user_story_dict_cloud dict['customfield_10322'] = [{'value': team.name}] # ********cloud else: dict = self.user_story_dict_vm #dict['customfield_10201'] = {'value': team.name} dict['customfield_10200'] = { 'value': team.name } # Tsafi 21 Jan 2020 dict['summary'] = user_story_name user_story = self.jira_inst.create_issue(fields=dict) if epic_key: self.jira_inst.add_issues_to_epic(epic_key, [user_story.key]) # Note the performance impact, this is another fetch after # adding a story to epic user_story = self.jira_inst.issue(user_story.id) return user_story def create_list_of_epics(self, list_of_epics, team): print('\nCreating list of epics for team %s in JIRA' % team.name) for epic in list_of_epics: jira_epic = self.create_epic(epic.name, team) epic.key = jira_epic.key def create_list_of_user_stories(self, list_of_user_stories, team): print('\nCreating list of user stories for team %s in JIRA' % team.name) for user_story in list_of_user_stories: jira_user_story = self.create_user_story_with_epic( user_story.name, team, user_story.epic.key) user_story.key = jira_user_story.key # Create Sprint # Best would be to provide a board_id of a board that contains all issues def create_sprint(self, board_id, sprint_name, start_date, sprint_size): end_date = start_date + timedelta(days=sprint_size) start_date_str = start_date.strftime("%d/%b/%y %#I:%M %p") end_date_str = end_date.strftime("%d/%b/%y %#I:%M %p") print(start_date_str) print(end_date_str) new_sprint = self.jira_inst.create_sprint(sprint_name, board_id, startDate=start_date_str, endDate=end_date_str) print("Created new Sprint: name = %s, id = %s" % (new_sprint.name, new_sprint.id)) print("Start date %s, end date %s" % (new_sprint.startDate, new_sprint.endDate)) return new_sprint # Set up Sprints def start_sprint(self, sprint_id): print("Please start the sprint manually on a global board") key = input() def end_sprint(self, sprint_id): print("Please end the sprint manually on a global board") key = input() def add_issues_to_sprint(self, sprint_id, user_stories_keys): print("Adding %d issues to Sprint %d" % (len(user_stories_keys), sprint_id)) self.jira_inst.add_issues_to_sprint(sprint_id, user_stories_keys) def update_one_issue(self, issue_key, transition): print("Updating issue %s, moving to %s" % (issue_key, transition)) self.jira_inst.transition_issue(issue_key, self.transition_ids[transition]) def advance_time_by_one_day(self): if self.instance_type != 'cloud': #Tsafi 20 Jan 2020, change path for Tsafi's local VM #path = "C:\\Windows\\WinSxS\\amd64_openssh-client-components-onecore_31bf3856ad364e35_10.0.17763.1_none_f0c3262e74c7e35c\\ssh.exe [email protected] \"cd /home/nelkag/Simulator/misc; python /home/nelkag/Simulator/misc/advanceoneday.py\"" #path = "C:\\Windows\\System32\\OpenSSH\\ssh.exe [email protected] \"cd /home/tsafi/SAFESimulator; python3 ./advanceoneday.py\"" #path = "C:\\Windows\\System32\\OpenSSH\\ssh.exe [email protected] python3 /home/tsafi/SAFESimulator/advanceoneday.py" #path = "C:\\Windows\\System32\\OpenSSH\\ssh.exe [email protected] date" #path = "C:\\Windows\\Sysnative\\OpenSSH\\ssh.exe [email protected] python3 ./SAFESimulator/advanceoneday.py" #path = "C:\\Windows\\Sysnative\\OpenSSH\\ssh.exe [email protected] python3 ./SAFESimulator/advanceoneday.py" path = 'C:\\Windows\\Sysnative\\OpenSSH\\ssh.exe tsafi@' + LOCAL_JIRA_IP + ' python3 ./SAFESimulator/advanceoneday.py' print(path) os.system(path)
class JiraOperations: def __init__(self, credentials): self.epics_dict = {} self.options = {"server": SERVER_ADDRESS} self.jira = JIRA(self.options, basic_auth=credentials) self.get_epics() def get_epics(self): found_issues = self.jira.search_issues( 'project=10031275_mPAD_ITV and type = Epic and summary ~ "Timesheet "' ) for issue in found_issues: self.epics_dict[issue.fields.summary] = issue.id def get_epic_id(self): today = datetime.datetime.now() month_name = today.strftime("%B")[:3] year = str(today.year)[2:] timesheet_string = 'Timesheet ' + month_name + year return self.epics_dict[timesheet_string] def find_issue_key(self, summary): found_issues = self.jira.search_issues( 'project=10031275_mPAD_ITV and type = Story and reporter = ' 'currentUser() and status != done') for issue in found_issues: if issue.fields.summary.upper().strip() == summary: return issue def is_story_created(self, summary): found_issues = self.jira.search_issues( 'project=10031275_mPAD_ITV and type = Story and reporter = ' 'currentUser() and status != done') for issue in found_issues: if issue.fields.summary.upper().strip() == summary: return True return False def get_stories_created_today(self): issue_list = [] work_time = datetime.timedelta() break_time = datetime.timedelta() found_issues = self.jira.search_issues( 'project=10031275_mPAD_ITV and type = Story and reporter = ' 'currentUser() and createdDate > startOfDay() and (status = open OR ' 'status = Done)') for issue in found_issues: issue_list.append( "Key: %s, status: %s, summary: %s, created: %s, duration: %s, description: %s" % (issue.key, issue.fields.status, issue.fields.summary, date_convert(issue.fields.created), timedelta_to_str( calculate_duration(issue.fields.created, issue.fields.resolutiondate)), issue.fields.description)) if issue.fields.summary.upper().strip() == 'WORK': work_time = work_time + calculate_duration( issue.fields.created, issue.fields.resolutiondate) elif issue.fields.summary.upper().strip() == 'BREAK': break_time = break_time + calculate_duration( issue.fields.created, issue.fields.resolutiondate) total_work_today = work_time - break_time issue_list.append("Total work time today: %s" % timedelta_to_str(total_work_today)) return issue_list def create_ticket(self, summary, description=''): created_issue = self.jira.create_issue(project=PROJECT_KEY, summary=summary, description=description, issuetype={'name': 'Story'}, labels=[TT_VERSION]) self.jira.add_issues_to_epic(self.get_epic_id(), [created_issue.key]) return created_issue.key def close_ticket(self, ticket_number): self.jira.transition_issue(ticket_number, 'done', fields={'resolution': { 'name': 'Done' }}) def description_init(self, summary): if self.is_story_created(summary): issue = self.find_issue_key(summary) if issue.fields.description is not None: return issue.fields.description else: return '' else: return '' def update_description(self, summary, description): if self.is_story_created(summary): issue_to_update = self.find_issue_key(summary) issue_to_update.update(fields={'description': description}) return True else: return False def get_week_statistics(self): week_statistics = [] work_story = 0 break_story = 0 work_time = datetime.timedelta() break_time = datetime.timedelta() balance = datetime.timedelta() work_expected = datetime.timedelta(hours=EXPECTED_HOURS, minutes=EXPECTED_MINUTES) found_issues = self.jira.search_issues( 'project=10031275_mPAD_ITV and type = Story and reporter = ' 'currentUser() and createdDate > startOfWeek() and (status = open OR ' 'status = Done)') for issue in found_issues: issue_summary = issue.fields.summary.upper().strip() if issue_summary == 'WORK': work_time = work_time + calculate_duration( issue.fields.created, issue.fields.resolutiondate) work_story += 1 elif issue_summary == 'BREAK': break_time = break_time + calculate_duration( issue.fields.created, issue.fields.resolutiondate) break_story += 1 elif issue_summary.split()[0] == 'BAL': hours = int(issue_summary.split()[3]) minutes = int(issue_summary.split()[5]) if issue_summary.split()[2] == '+': balance = balance + datetime.timedelta(hours=hours, minutes=minutes) work_time = work_time + datetime.timedelta(hours=hours, minutes=minutes) elif issue_summary.split()[2] == '-': balance = balance + datetime.timedelta(hours=hours, minutes=minutes) work_time = work_time - datetime.timedelta(hours=hours, minutes=minutes) else: messagebox.showinfo( "Error", "Found story with unknown sign: %s." % issue_summary.split()[2]) else: messagebox.showinfo( "Error", "Found story with unknown summary: %s." % issue.fields.summary) total_work_week = work_time - break_time total_work_expected = work_story * work_expected if total_work_expected - total_work_week >= datetime.timedelta(): time_left_today = timedelta_to_str(total_work_expected - total_work_week) else: time_left_today = '-' + timedelta_to_str(total_work_week - total_work_expected) week_statistics.append( "Total work stories created this week: %d, total break stories created this week: %d." % (work_story, break_story)) week_statistics.append("Total work time expected this week: %s" % timedelta_to_str(total_work_expected)) week_statistics.append( "Total worked time this week: %s, including %s from balance stories" % (timedelta_to_str(total_work_week), str(balance))) week_statistics.append("Left time for today: %s" % time_left_today) return week_statistics def start_work(self): if not self.is_story_created('WORK'): self.create_ticket('WORK') return True else: return False def stop_work(self): if self.is_story_created('WORK'): issue_key = self.find_issue_key('WORK') self.close_ticket(issue_key) return True else: return False def start_break(self, description): if not self.is_story_created('BREAK'): self.create_ticket('BREAK', description) return True else: return False def stop_break(self): if self.is_story_created('BREAK'): issue_key = self.find_issue_key('BREAK') self.close_ticket(issue_key) return True else: return False
class JiraWrapper: JIRA_REQUEST = 'project={} AND labels in ({})' def __init__(self, args, url, user, password, jira_project, assignee, check_functional_errors, check_performance_degradation, check_missed_thresholds, performance_degradation_rate, missed_thresholds_rate, issue_type='Bug', labels=None, watchers=None, jira_epic_key=None): self.valid = True self.args = args self.url = url self.password = password self.user = user self.check_functional_errors = check_functional_errors self.check_performance_degradation = check_performance_degradation self.check_missed_thresholds = check_missed_thresholds self.performance_degradation_rate = performance_degradation_rate self.missed_thresholds_rate = missed_thresholds_rate try: self.connect() except Exception: self.valid = False return self.projects = [project.key for project in self.client.projects()] self.project = jira_project if self.project not in self.projects: self.client.close() self.valid = False return self.assignee = assignee self.issue_type = issue_type self.labels = list() if labels: self.labels = [label.strip() for label in labels.split(",")] self.watchers = list() if watchers: self.watchers = [ watcher.strip() for watcher in watchers.split(",") ] self.jira_epic_key = jira_epic_key self.client.close() def connect(self): self.client = JIRA(self.url, basic_auth=(self.user, self.password)) def create_issue(self, title, priority, description, issue_hash, attachments=None, get_or_create=True, additional_labels=None): _labels = [issue_hash] if additional_labels and isinstance(additional_labels, list): _labels.extend(additional_labels) _labels.extend(self.labels) issue_data = { 'project': { 'key': self.project }, 'summary': title, 'description': description, 'issuetype': { 'name': self.issue_type }, 'assignee': { 'name': self.assignee }, 'priority': { 'name': priority }, 'labels': _labels } jira_request = self.JIRA_REQUEST.format(issue_data["project"]["key"], issue_hash) if get_or_create: issue, created = self.get_or_create_issue(jira_request, issue_data) else: issue = self.post_issue(issue_data) created = True if attachments: for attachment in attachments: if 'binary_content' in attachment: self.add_attachment( issue.key, attachment=attachment['binary_content'], filename=attachment['message']) for watcher in self.watchers: self.client.add_watcher(issue.id, watcher) if self.jira_epic_key: self.client.add_issues_to_epic(self.jira_epic_key, [issue.id]) return issue, created def add_attachment(self, issue_key, attachment, filename=None): issue = self.client.issue(issue_key) for _ in issue.fields.attachment: if _.filename == filename: return self.client.add_attachment(issue, attachment, filename) def post_issue(self, issue_data): issue = self.client.create_issue(fields=issue_data) print("Issue " + issue.key + " created." + " Description - " + issue_data['summary']) return issue def get_or_create_issue(self, search_string, issue_data): issuetype = issue_data['issuetype']['name'] created = False jira_results = self.client.search_issues(search_string) issues = [] for each in jira_results: if each.fields.summary == issue_data.get('summary', None): issues.append(each) if len(issues) == 1: issue = issues[0] if len(issues) > 1: print(' more then 1 issue with the same summary') else: print(issuetype + 'issue already exists:' + issue.key) else: issue = self.post_issue(issue_data) created = True return issue, created @staticmethod def create_functional_error_description(error, arguments): title = "Functional error in test: " + str(arguments['simulation']) + ". Request \"" \ + str(error['Request name']) + "\"." description = "{panel:title=" + title + \ "|borderStyle=solid|borderColor=#ccc|titleBGColor=#23b7c9|bgColor=#d7f0f3} \n" description += "h3. Request description\n" if error['Request name']: description += "*Request name*: " + error['Request name'] + "\n" if error['Method']: description += "*HTTP Method*: " + error['Method'] + "\n" if error['Request URL']: description += "*Request URL*: " + error['Request URL'] + "\n" if error['Request_params'] and str(error['Request_params']) != " ": description += "*Request params*: " + str( error['Request_params'])[2:-2].replace(" ", "\n") + "\n" if error['Request headers']: description += "*Request headers*: {code}" headers = str(error['Request headers']).replace(": ", ":") for header in headers.split(" "): description += header + "\n" description += "{code}\n" description += "---- \n h3. Error description\n" if error['Error count']: description += "*Error count*: " + str( error['Error count']) + ";\n" if error['Response code']: description += "*Response code*: " + error['Response code'] + ";\n" if error['Error_message']: description += "*Error message*: {color:red}" + str( error['Error_message']) + "{color}\n" if error['Response']: if len(str(error['Response'])) < 55000: description += "---- \n h3. Response body {code:html}" + str( error['Response']) + "{code}" else: description += "---- \n See response body attached.\n" description += "{panel}" return description @staticmethod def get_functional_error_hash_code(error, arguments): error_str = arguments['simulation'] + "_" + error['Request URL'] + "_" + str(error['Error_message']) + "_" \ + error['Request name'] return hashlib.sha256(error_str.strip().encode('utf-8')).hexdigest() @staticmethod def create_performance_degradation_description( performance_degradation_rate, compare_with_baseline, arguments): title = "Performance degradation in test: " + str( arguments['simulation']) description = "{panel:title=" + title + \ "|borderStyle=solid|borderColor=#ccc|titleBGColor=#23b7c9|bgColor=#d7f0f3} \n" description += "{color:red}" + "Test performance degradation is {}% compared to the baseline."\ .format(performance_degradation_rate) + "{color} \n" description += "h3. The following requests are slower than baseline:\n" for request in compare_with_baseline: description += "\"{}\" reached {} ms by {}. Baseline {} ms.\n".format( request['request_name'], request['response_time'], arguments['comparison_metric'], request['baseline']) description += "{panel}" return description @staticmethod def create_missed_thresholds_description(missed_threshold_rate, compare_with_thresholds, arguments): title = "Missed thresholds in test: " + str(arguments['simulation']) description = "{panel:title=" + title + \ "|borderStyle=solid|borderColor=#ccc|titleBGColor=#23b7c9|bgColor=#d7f0f3} \n" description += "{color:red}" + "Percentage of requests exceeding the threshold was {}%." \ .format(missed_threshold_rate) + "{color} \n" for color in ['yellow', 'red']: colored = False for th in compare_with_thresholds: if th['threshold'] == color: if not colored: description += f"h3. The following {color} thresholds were exceeded:\n" colored = True appendage = calculate_appendage(th['target']) description += f"\"{th['request_name']}\" {th['target']}{appendage} " \ f"with value {th['metric']}{appendage} " \ f"exceeded threshold of {th[color]}{appendage}\n" description += "{panel}" return description def report_errors(self, aggregated_errors): for error in aggregated_errors: issue_hash = self.get_functional_error_hash_code( aggregated_errors[error], self.args) title = "Functional error in test: " + str(self.args['simulation']) + ". Request \"" \ + str(aggregated_errors[error]['Request name']) + "\" failed with error message: " \ + str(aggregated_errors[error]['Error_message'])[0:100] description = self.create_functional_error_description( aggregated_errors[error], self.args) if len(str(aggregated_errors[error]['Response'])) < 55000: self.create_issue(title, 'Major', description, issue_hash) else: content = io.StringIO() content.write(str(aggregated_errors[error]['Response'])) attachment = { "binary_content": content, "message": "response_body.txt" } self.create_issue(title, 'Major', description, issue_hash, [attachment]) def report_performance_degradation(self, performance_degradation_rate, compare_with_baseline): issue_hash = hashlib.sha256("{} performance degradation".format( self.args['simulation']).strip().encode('utf-8')).hexdigest() title = "Performance degradation in test: " + str( self.args['simulation']) description = self.create_performance_degradation_description( performance_degradation_rate, compare_with_baseline, self.args) self.create_issue(title, 'Major', description, issue_hash) def report_missed_thresholds(self, missed_threshold_rate, compare_with_thresholds): issue_hash = hashlib.sha256("{} missed thresholds".format( self.args['simulation']).strip().encode('utf-8')).hexdigest() title = "Missed thresholds in test: " + str(self.args['simulation']) description = self.create_missed_thresholds_description( missed_threshold_rate, compare_with_thresholds, self.args) self.create_issue(title, 'Major', description, issue_hash)
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)
def update_or_create_jira_issue(study_id, user_token, is_curator): try: params = app.config.get('JIRA_PARAMS') user_name = params['username'] password = params['password'] updated_studies = [] try: jira = JIRA(options=options, basic_auth=(user_name, password)) except: return False, 'Could not connect to JIRA server, incorrect username or password?', updated_studies # Get the MetaboLights project mtbls_project = jira.project(project) studies = [study_id] if not study_id and is_curator: studies = get_all_studies(user_token) for study in studies: study_id = study[0] user_name = study[1] release_date = study[2] update_date = study[3] study_status = study[4] curator = study[5] issue = [] summary = None # Get an issue based on a study accession search pattern search_param = "project='" + mtbls_project.key + "' AND summary ~ '" + study_id + " \\\-\\\ 20*'" issues = jira.search_issues(search_param) # project = MetaboLights AND summary ~ 'MTBLS121 ' new_summary = study_id + ' - ' + release_date.replace('-', '') + ' - ' + \ study_status + ' (' + user_name + ')' try: if issues: issue = issues[0] else: if study_status == 'Submitted': logger.info("Could not find Jira issue for " + search_param) print("Creating new Jira issue for " + search_param) issue = jira.create_issue(project=mtbls_project.key, summary='MTBLS study - To be updated', description='Created by API', issuetype={'name': 'Story'}) else: continue # Only create new cases if the study is in status Submitted except Exception: # We could not find or create a Jira issue continue summary = issue.fields.summary # Follow pattern 'MTBLS123 - YYYYMMDD - Status' try: assignee = issue.fields.assignee.name except: assignee = "" valid_curator = False jira_curator = "" if curator: if curator.lower() == 'mark': jira_curator = 'mwilliam' valid_curator = True elif curator.lower() == 'keeva': jira_curator = 'keeva' valid_curator = True else: jira_curator = "" # Release date or status has changed, or the assignee (curator) has changed if summary.startswith('MTBLS') and (summary != new_summary or assignee != jira_curator): # Add "Curation" Epic issues_to_add = [issue.key] jira.add_issues_to_epic(curation_epic, issues_to_add) # Add the Curation Epic labels = maintain_jira_labels(issue, study_status, user_name) # Add a comment to the issue. comment_text = 'Status ' + study_status + '. Database update date ' + update_date jira.add_comment(issue, comment_text) # Change the issue's summary, comments and description. issue.update(summary=new_summary, fields={"labels": labels}, notify=False) if valid_curator: # ToDo, what if the curation log is not up to date? issue.update(assignee={'name': jira_curator}) updated_studies.append(study_id) logger.info('Updated Jira case for study ' + study_id) print('Updated Jira case for study ' + study_id) except Exception: return False, 'Update failed', updated_studies return True, 'Ticket(s) updated successfully', updated_studies
class JiraWrapper(object): JIRA_REQUEST = 'project={} AND labels in ({})' def __init__(self, url, user, password, project, assignee, issue_type='Bug', labels=None, watchers=None, jira_epic_key=None): self.valid = True self.url = url self.password = password self.user = user try: self.connect() except: self.valid = False return self.projects = [project.key for project in self.client.projects()] self.project = project.upper() print(self.projects) if self.project not in self.projects: self.client.close() self.valid = False return self.assignee = assignee self.issue_type = issue_type self.labels = list() if labels: self.labels = [label.strip() for label in labels.split(",")] self.watchers = list() if watchers: self.watchers = [ watchers.strip() for watchers in watchers.split(",") ] self.jira_epic_key = jira_epic_key self.client.close() def connect(self): self.client = JIRA(self.url, basic_auth=(self.user, self.password)) def markdown_to_jira_markdown(self, content): return content.replace("###", "h3.").replace("**", "*") def create_issue(self, title, priority, description, issue_hash, attachments=None, get_or_create=True, additional_labels=None): description = self.markdown_to_jira_markdown(description) _labels = [issue_hash] if additional_labels and isinstance(additional_labels, list): _labels.extend(additional_labels) _labels.extend(self.labels) issue_data = { 'project': { 'key': self.project }, 'summary': re.sub('[^A-Za-z0-9//\. _]+', '', title), 'description': description, 'issuetype': { 'name': self.issue_type }, 'assignee': { 'name': self.assignee }, 'priority': { 'name': priority }, 'labels': _labels } jira_request = self.JIRA_REQUEST.format(issue_data["project"]["key"], issue_hash) if get_or_create: issue, created = self.get_or_create_issue(jira_request, issue_data) else: issue = self.post_issue(issue_data) created = True if attachments: for attachment in attachments: if 'binary_content' in attachment: self.add_attachment( issue.key, attachment=attachment['binary_content'], filename=attachment['message']) for watcher in self.watchers: self.client.add_watcher(issue.id, watcher) if self.jira_epic_key: self.client.add_issues_to_epic(self.jira_epic_key, [issue.id]) return issue, created def add_attachment(self, issue_key, attachment, filename=None): issue = self.client.issue(issue_key) for _ in issue.fields.attachment: if _.filename == filename: return self.client.add_attachment(issue, attachment, filename) def post_issue(self, issue_data): print(issue_data) issue = self.client.create_issue(fields=issue_data) logging.info( f' \u2713 {issue_data["issuetype"]["name"]} issue was created: {issue.key}' ) return issue def get_or_create_issue(self, search_string, issue_data): issuetype = issue_data['issuetype']['name'] created = False jira_results = self.client.search_issues(search_string) issues = [] for each in jira_results: if each.fields.summary == issue_data.get('summary', None): issues.append(each) if len(issues) == 1: issue = issues[0] if len(issues) > 1: print(' more then 1 issue with the same summary') else: print(f' {issuetype} issue already exists: {issue.key}') else: issue = self.post_issue(issue_data) created = True return issue, created
class JiraTool: def __init__(self): self.server = jira_url self.basic_auth = (usrer, password) self.jiraClinet = None def login(self): self.jiraClinet = JIRA(server=self.server, basic_auth=self.basic_auth) if self.jiraClinet != None: return True else: return False def findIssueById(self, issueId): if issueId: if self.jiraClinet == None: self.login() return self.jiraClinet.issue(issueId) else: return 'Please input your issueId' def deleteAllIssue(self, project): project_issues = self.jiraClinet.search_issues('project=' + project.name) for issue in project_issues: logging.info('delete issue %s' % (issue)) issue.delete() def deleteAllSprint(self, board): sprints = self.jiraClinet.sprints(board.id) for sprint in sprints: logging.info('delete sprint %s' % (sprint.name)) sprint.delete() def getProject(self, name): projects = self.jiraClinet.projects() #logging.info("get project %s" %(name)) for project in projects: #logging.info("project %s" %(project.name)) if (name == project.name): return project return None #return self.jiraClinet.create_project(key='SCRUM', name=name, assignee='yujiawang', type="Software", template_name='Scrum') def getBoard(self, project, name): boards = self.jiraClinet.boards() for board in boards: if (name == board.name): logging.info("board:%s id:%d" % (board.name, board.id)) return board return self.jiraClinet.create_board(name, [project.id]) def createSprint(self, board, name, startDate=None, endDate=None): logging.info("==== create sprint[%s] in board[%s] ====>" % (name, board.name)) sprint = self.jiraClinet.create_sprint(name, board.id, startDate, endDate) return sprint def getSprint(self, board_id, sprint_name): sprints = self.jiraClinet.sprints(board_id) for sprint in sprints: if sprint.name == sprint_name: return sprint return None def createEpicTask(self, project, sprint, summary, description, assignee, participant, duedate): issue_dict = { 'project': { 'key': project.key }, 'issuetype': { 'id': issuetypes['Epic'] }, 'customfield_10002': summary, # epic 名称 'summary': summary, 'description': description, "customfield_10004": sprint.id, # sprint 'assignee': { 'name': assignee }, 'customfield_10303': participant, 'customfield_10302': '2018-08-24T05:41:00.000+0800' } logging.info(duedate) logging.info(issue_dict) #juse for debug issue = self.jiraClinet.create_issue(issue_dict) self.jiraClinet.add_issues_to_sprint(sprint.id, [issue.raw['key']]) logging.info( "===> add epic task[%s key:%s] to sprint [%s]" % (issue.raw['fields']['summary'], issue.raw['key'], sprint.name)) #dumpIssue(issue) #juse for debug return issue def createTask(self, project, sprint, epic, summary, description, assignee, participant): issue_dict = { 'project': { 'key': project.key }, 'issuetype': { 'id': issuetypes['Task'] }, 'summary': summary, 'description': description, 'assignee': { 'name': assignee }, 'customfield_10303': participant } issue = self.jiraClinet.create_issue(issue_dict) logging.info("==> add task[%s key:%s] link epic [%s key: %s]" % (issue.raw['fields']['summary'], issue.raw['key'], epic.raw['fields']['summary'], epic.raw['key'])) self.jiraClinet.add_issues_to_epic(epic.id, [issue.raw['key']]) self.jiraClinet.add_issues_to_sprint(sprint.id, [issue.raw['key']]) return issue def createSubTask(self, project, parent, summary, description, assignee, participant): issue_dict = { 'project': { 'key': project.key }, 'parent': { 'key': parent.raw['key'] }, 'issuetype': { 'id': issuetypes['Sub-Task'] }, 'summary': summary, 'description': description, 'assignee': { 'name': assignee }, 'customfield_10303': participant } issue = self.jiraClinet.create_issue(issue_dict) logging.info("=> add sub task[%s key:%s] to task [%s key: %s]" % (issue.raw['fields']['summary'], issue.raw['key'], parent.raw['fields']['summary'], parent.raw['key'])) return issue
def update_or_create_jira_issue(user_token, is_curator): try: params = app.config.get('JIRA_PARAMS') user_name = params['username'] password = params['password'] default_curator = 'metabolights-api' updated_studies = [] try: jira = JIRA(options=options, basic_auth=(user_name, password)) except: return False, 'Could not connect to JIRA server, incorrect username or password?', updated_studies # Get the MetaboLights project mtbls_project = jira.project(project) if is_curator: studies = get_all_studies(user_token) for study in studies: study_id = None user_name = None release_date = None update_date = None study_status = None curator = None status_change = None curation_due_date = None try: study_id = safe_str(study[0]) user_name = safe_str(study[1]) release_date = safe_str(study[2]) update_date = safe_str(study[3]) study_status = safe_str(study[4]) curator = safe_str(study[5]) status_change = safe_str(study[6]) curation_due_date = safe_str(study[7]) except Exception as e: logger.error(str(e)) issue = [] summary = None # date is 'YYYY-MM-DD HH24:MI' due_date = status_change[:10] logger.info('Checking Jira ticket for ' + study_id + '. Values: ' + user_name + '|' + release_date + '|' + update_date + '|' + study_status + '|' + curator + '|' + status_change + '|' + due_date) # Get an issue based on a study accession search pattern search_param = "project='" + mtbls_project.key + "' AND summary ~ '" + study_id + " \\\-\\\ 20*'" issues = jira.search_issues(search_param) # project = MetaboLights AND summary ~ 'MTBLS121 ' new_summary = study_id + ' - ' + release_date.replace('-', '') + ' - ' + \ study_status + ' (' + user_name + ')' try: if issues: issue = issues[0] else: if study_status == 'Submitted' or study_status == 'In Curation': logger.info("Could not find Jira issue for " + search_param) print("Creating new Jira issue for " + search_param) issue = jira.create_issue(project=mtbls_project.key, summary='MTBLS study - To be updated', description='Created by API', issuetype={'name': 'Story'}) else: continue # Only create new cases if the study is in status Submitted/In Curation except: # We could not find or create a Jira issue. continue summary = issue.fields.summary # Follow pattern 'MTBLS123 - YYYYMMDD - Status' if not summary.startswith('MTBLS'): continue # Skip all cases that are not related the study accession numbers try: assignee = issue.fields.assignee.name except: assignee = "" assignee_changed = False valid_curator = False jira_curator = "" if curator: if curator.lower() == 'mark': jira_curator = 'mwilliam' valid_curator = True elif curator.lower() == 'pamela': jira_curator = 'ppruski' valid_curator = True elif curator.lower() == 'xuefei' or curator.lower() == 'reza' or curator.lower() == 'keeva': jira_curator = default_curator # We do not have a current curation listed in the log valid_curator = True assignee_changed = True if assignee != jira_curator else False else: jira_curator = "" if not status_change: status_change = "No status changed date reported" # Release date or status has changed, or the assignee (curator) has changed summary_changed = True if summary != new_summary else False curator_update = True if assignee != default_curator and jira_curator != default_curator else False if assignee_changed or summary_changed: # Add "Curation" Epic issues_to_add = [issue.key] jira.add_issues_to_epic(curation_epic, issues_to_add) # Add the Curation Epic labels = maintain_jira_labels(issue, study_status, user_name) # Add a comment to the issue. comment_text = 'Current status ' + study_status + '. Status last changed date ' + status_change + \ '. Curation due date ' + due_date + '. Database update date ' + update_date if jira_curator == default_curator: comment_text = comment_text + '. Default curator has been changed from "' \ + curator + '" to "' + default_curator + '"' if assignee_changed: comment_text = comment_text + '. Curator in Jira changed from "' + assignee + '" to "' + jira_curator + '"' if summary_changed: comment_text = comment_text + '. Summary in Jira changed from "' + summary + '" to "' + new_summary + '"' jira.add_comment(issue, comment_text) # Change the issue's summary, comments and description. issue.update(summary=new_summary, fields={"labels": labels}, notify=False) # if valid_curator: # ToDo, what if the curation log is not up to date? issue.update(assignee={'name': jira_curator}, notify=False) updated_studies.append(study_id) logger.info('Updated Jira case for study ' + study_id) print('Updated Jira case for study ' + study_id) except Exception as e: logger.error("Jira updated failed for " + study_id + ". " + str(e)) return False, 'Update failed: ' + str(e), str(study_id) return True, 'Ticket(s) updated successfully', updated_studies
class DonburiJira: def __init__(self, server, project, auth_user, auth_key): self.project = project self.issue_url_base = server.rstrip("/") + '/browse/' self.jira = JIRA({'server': server}, basic_auth=(auth_user, auth_key)) def jira_instance(self): return self.jira def create_issue(self, account_id, summary, description, issuetype='Task'): issue = self.jira.create_issue(project=self.project, summary=summary, description=description, assignee={'accountId': account_id}, issuetype={'name': issuetype}) return issue def add_to_epic(self, issue, epic_id): epic = self.jira.issue(epic_id) if epic == None: return False # add ticket to epic self.jira.add_issues_to_epic(epic_id=epic_id, issue_keys=[issue.key]) # take over labels from epic labels = epic.fields.labels issue.fields.labels.extend(labels) issue.update(fields={"labels": issue.fields.labels}) return True def add_labels(self, issue, labels): tmp_labels = issue.fields.labels + labels issue.update(fields={"labels": tmp_labels}) def search_issues(self, query): issues = [] block_size = 100 idx = 0 while True: issues_tmp = self.jira.search_issues(query, idx, block_size) if len(issues_tmp) == 0: break idx = idx + len(issues_tmp) issues = issues + issues_tmp return sorted(issues, key=lambda u: str(u.fields.assignee)) def labeled_issues(self, label, resolution='Done', resolutiondate='-60d'): query = f"project = {self.project} AND " \ f"labels in (\"{label}\") AND " \ f"resolution = {resolution} AND " \ f"status != \"Canceled\" AND " \ f"resolutiondate > {resolutiondate}" #f"updatedDate > {updatedDate}" return self.search_issues(query) def epiced_issues(self, epic_name, createdDate='-60d'): query = f"project = {self.project} AND " \ f"\"Epic Link\" = {epic_name} AND " \ f"createdDate > {createdDate}" return self.search_issues(query) def print_issues(self, issues): for issue in issues: url = self.issue_url_base + issue.key labels = issue.fields.labels if issue.fields.assignee is not None: assignee = issue.fields.assignee.displayName else: assignee = "None" summary = issue.fields.summary status = issue.fields.status resolution = issue.fields.resolution print(f"{url},{assignee},{labels},\"{summary}\",{resolution}") def list_unlabeled_issues(self, issues, exclude_label): marked_issues = [] for issue in issues: labels = issue.fields.labels if len(labels) == 0 or (len(labels) == 1 and labels[0] == exclude_label): marked_issues.append(issue) return marked_issues