diff --git a/JIRA_SA_Links.py b/JIRA_SA_Links.py new file mode 100644 index 0000000..07be345 --- /dev/null +++ b/JIRA_SA_Links.py @@ -0,0 +1,69 @@ +# find comments with estimations out of pre-planning +# zisco 2021-09-30 19:50 +# TODO: Consider later created sub-tasks + +from multiprocessing.sharedctypes import Value +from types import ClassMethodDescriptorType +from jira import JIRA, Issue, Comment, exceptions +import re +import csv +import xlsxwriter +import logging +import yaml +from bisect import bisect_left +from datetime import date + + +logging.basicConfig(filename='sub_task_generator.log', + filemode='a', + format='%(asctime)s - %(levelname)s: %(message)s', + level=logging.INFO, + encoding='utf-8') + +with open("config.yaml", encoding='utf8') as f: + cfg = yaml.load(f, Loader=yaml.FullLoader) + +host = "https://jira.wien.gv.at/" +pat = "OTAyMTM3MjY0MjE0Ovz6bu0ti1TlEdSegmmiWiJ2wGeD" +headers = JIRA.DEFAULT_OPTIONS["headers"].copy() +headers["Authorization"] = f"Bearer {pat}" +jira_server = JIRA(server=host, options={"headers": headers}) + +# use filter or single issue +issues_Jql = jira_server.search_issues('filter=31528') #, expand='changelog') +logging.info(f"Modify 'DoD SA' sub-tasks with link to Dev sub-tasks") + +issue: Issue +for issue in issues_Jql: + logging.info('Working on {}'.format(issue)) + + # else: + # single_issue = jira_server.issue(issue.key) + + # find corresponding Dev sub-tasks + #escaped = issue.fields.summary.replace('"', r'\"') + #escaped = escaped.replace('[DoD SA]', '').strip() + escaped = issue.fields.summary.replace('[DoD SA]', '').strip() + logging.info(f'summary ~ \"{escaped}\"') + + description = issue.fields.description + links_section = '\r\n||Dev-Links||Typ||' + position = description.find(links_section) + if position != -1: + # clear list + description = description[:position + len(links_section)].strip() + else: + description += links_section + + for type in ['Backend','Frontend']: + subtask = jira_server.search_issues(f'summary ~ \"Dev {type} {escaped}\"') + if subtask: + # adapt DoD SA-desc + description += "\r\n|" + subtask[0].key + f'|{type}|' + + try: + issue.update(fields={'description': description}) + except Exception as e: + logging.info(e) + + logging.info(f'Link to {type} for {issue} created') \ No newline at end of file diff --git a/JIRA_SubTasks_byComments.py b/JIRA_SubTasks_byComments.py index 513e997..f0e5b52 100644 --- a/JIRA_SubTasks_byComments.py +++ b/JIRA_SubTasks_byComments.py @@ -32,42 +32,54 @@ def take_closest(myList, myNumber): def get_assignee(type, issue): # isBuergerfrontend = False - if (type == 'Dev Backend' or type.startswith('Dev Unit') or type == 'DoD SA/DEV'): + if (type == 'Dev Backend' or type.startswith('Dev Unit')): # use assignee of story - return 'rab9001' + return cfg['def_assignees']['backend'] #'rab9001' #return issue.fields.assignee.name elif type == 'Dev Frontend': # for component in issue.fields.components: # isBuergerfrontend = component.name == "Bürgerfrontend" # if isBuergerfrontend: - return 'thh9001' + return cfg['def_assignees']['frontend'] #'thh9001' elif type == 'Dev Citizen-Frontend': - return 'reu9001' + return cfg['def_assignees']['cit_frontend'] #'reu9001' elif type.startswith('Test-Anpassung') or type == 'DoD Test': if issue.key.startswith('WRGWR-'): - return 'ada9001' + return cfg['def_assignees']['wgwr_test'] #'ada9001' + elif issue.key.startswith('BAUWB-'): + return cfg['def_assignees']['bauwb_test'] #'osm9005' + elif issue.key.startswith('DKARB-'): + return cfg['def_assignees']['far_test'] #'osm9005' else: - return 'osm9005' + return cfg['def_assignees']['default'] #'zim9003' elif type == 'DoD PO': - return 'kom9010' + return cfg['def_assignees']['po'] #'kom9010' + elif type == 'DoD SA': + return cfg['def_assignees']['sa'] #'krp9002' def create_subtask(issue, parent, type, subtask_sp=0, estimation_remark="", no_unit_test=False): escaped = issue.fields.summary.replace('"', r'\"') logging.info(f'summary ~ \"{escaped}\"') assignee = get_assignee(type, issue) - description = get_description(type) + description = get_description(type, no_unit_test=no_unit_test) try: - subtask = jira_server.search_issues(f'summary ~ \"{type} {escaped}\"') + project = parent.split('-')[0] + subtask = jira_server.search_issues(f'summary ~ \"{type} {escaped}\" AND project = {project}') # add the project if subtask: - raise exceptions.JIRAError + single_issue = jira_server.issue(subtask[0].key) + if single_issue.fields.summary == f'[{type}] {escaped}': + raise exceptions.JIRAError except exceptions.JIRAError: # correct estimation #subtask.fields('timetracking': {'originalEstimate'}).Value() = subtask_sp - single_issue = jira_server.issue(subtask[0].key) + #single_issue = jira_server.issue(subtask[0].key) try: - single_issue.update(fields={'timetracking': {'originalEstimate': '{}d'.format(subtask_sp)}, 'description': description, 'assignee': {'name': assignee}}) + if cfg['update_desc_only']: + single_issue.update(fields={'description': description}) + else: + single_issue.update(fields={'timetracking': {'originalEstimate': '{}d'.format(subtask_sp)}, 'description': description, 'assignee': {'name': assignee}}) except Exception as e: logging.info(e) @@ -102,13 +114,22 @@ def create_subtask(issue, parent, type, subtask_sp=0, estimation_remark="", no_u logging.info('Created Sub-Task {}'.format(new_issue)) -def get_description(type): - if (type == 'DoD SA/DEV'): - description = cfg['subtask_desc_dev'] - elif (type == 'DoD Test'): - description = cfg['subtask_desc_test'] +def get_description(type, no_unit_test=False): + if (type == 'Dev Backend'): + description = cfg['templates']['subtask_desc_be_dev'] + if no_unit_test==False: + description += cfg['templates']['add_unit_test_desc'] + elif (type in ('Dev Frontend', 'Dev Citizen-Frontend')): + description = cfg['templates']['subtask_desc_fe_dev'] + elif (type == 'DoD SA'): + description = cfg['templates']['subtask_desc_sa'] + #TODO: Add links to DEV-SubTasks + elif (type == 'Test-Anpassung (TOSCA/xRay)'): + #TODO: Use desc of story + description = 's. Story' + #description = cfg['templates']['subtask_desc_test'] elif (type == 'DoD PO'): - description = cfg['subtask_desc_po'] + description = cfg['templates']['subtask_desc_po'] else: description = 's. Story' return description @@ -124,7 +145,7 @@ with open("config.yaml", encoding='utf8') as f: host = "https://jira.wien.gv.at/" pat = "OTAyMTM3MjY0MjE0Ovz6bu0ti1TlEdSegmmiWiJ2wGeD" -sprint = 6 +sprint = 5 headers = JIRA.DEFAULT_OPTIONS["headers"].copy() headers["Authorization"] = f"Bearer {pat}" @@ -132,7 +153,8 @@ jira_server = JIRA(server=host, options={"headers": headers}) # use filter or single issue issues_Jql = jira_server.search_issues('filter=25827') #, expand='changelog') -#issues_Jql = jira_server.search_issues('key in (WRGWR-2932)') #, expand='changelog') +#issues_Jql = jira_server.search_issues('filter=30617') #, expand='changelog') +issues_Jql = jira_server.search_issues('key in (BAUWB-600)') #, expand='changelog') #issues_Jql = jira_server.search_issues('labels in (Refinement11012022) AND type not in (Bug) AND status not in (Done) and ("Story Points" is EMPTY or "Story Points" = 0)') #, expand='changelog') logging.info(f'Creating sub-tasks out of estimation of pre-planning of {date.today()}') @@ -159,8 +181,11 @@ for issue in issues_Jql: for comment in comments: comment_lines = comment.body.splitlines() - if len(comment_lines) == 9: + if len(comment_lines) in (5, 9): if comment_lines[0][:5] == "Dev B": + if "Dev Unit-Tests: NN" in comment_lines: + no_unit_test = True + for comment_line in comment_lines: if comment_line != "": # pos = comment_lines[2].find('NN') @@ -184,27 +209,27 @@ for issue in issues_Jql: subtask_ph = float(comment_line_sub_parts[0]) * 0.5 estimation_remark = "" else: - if comment_line_sub_parts[0].strip() == "tbd": + if comment_line_sub_parts[0].strip().lower() == "tbd": subtask_ph = take_closest([1,2,3,5,8,13,21,42], sp_sum/3) - if comment_line_parts[0][:15] != "Test-Ausführung": - create_subtask(issue, - issue.key, - comment_line_parts[0], - subtask_ph, - estimation_remark) + #if comment_line_parts[0][:15] != "Test-Ausführung": + create_subtask(issue, + issue.key, + comment_line_parts[0], + subtask_ph, + estimation_remark, + no_unit_test=no_unit_test) sp_sum += subtask_ph / 0.5 else: logging.info(f'Sub-task creation skipped {comment_line_parts[0]} equals \'NN\'') - if "Unit-Test" in comment_line_parts[0]: - no_unit_test = True # find corresponding sub-task # issuelinks.inwardIssue try: if sp != sp_sum: - issue.update(fields={'customfield_10022': sp_sum}) - logging.info("Estimated story-points changed to Sum(Sub-Tasks story-points): {}".format(sp_sum, sp_sum)) + if cfg['update_sp_sum']: + issue.update(fields={'customfield_10022': sp_sum}) + logging.info("Estimated story-points changed to Sum(Sub-Tasks story-points): {}".format(sp_sum, sp_sum)) except exceptions.JIRAError: logging.error("Issue {} has no (visible) Story Points-attribute".format(issue.key)) @@ -213,12 +238,13 @@ for issue in issues_Jql: logging.info('Done {}'.format(issue.key)) #create standard sub-tasks - for type in ['DoD SA/DEV','DoD Test','DoD PO']: - create_subtask(issue, - issue.key, - type, - no_unit_test=no_unit_test - ) + if cfg['separate_dod_subtasks']: + for type in ['DoD SA','DoD PO']: + create_subtask(issue, + issue.key, + type, + no_unit_test=no_unit_test + ) #add or remove label #Unit-Test on story-level if no_unit_test == False: