From 55b4fc9fd817e09433a78e73c98255c5d28c5a0a Mon Sep 17 00:00:00 2001 From: Zimmerberger Markus Date: Wed, 13 Jul 2022 08:06:31 +0200 Subject: [PATCH] Several minor changes --- JIRA_SubTasks_byComments.py | 102 ++++++++++++++++++++-------------- JIRA_actualize_story_state.py | 73 ++++++++++++++++++++++++ JIRA_keep_SubTask_in_sync.py | 98 ++++++++++++++++++++++++++++++++ 3 files changed, 230 insertions(+), 43 deletions(-) create mode 100644 JIRA_actualize_story_state.py create mode 100644 JIRA_keep_SubTask_in_sync.py diff --git a/JIRA_SubTasks_byComments.py b/JIRA_SubTasks_byComments.py index 86df003..bc42512 100644 --- a/JIRA_SubTasks_byComments.py +++ b/JIRA_SubTasks_byComments.py @@ -10,9 +10,12 @@ import logging def get_assignee(type): if type == 'Dev Backend': - return 'rab9001' - elif type == 'Dev Frontend': + #return 'rab9001' return 'reu9001' + elif type == 'Dev Frontend': + # consider verwaltungsfrontend or bürgerfrontend + return 'thh9001' + # return 'reu9001' elif type[:14] == 'Test-Anpassung': return 'ada9001' @@ -24,16 +27,21 @@ def create_subtask(issue, parent, type, subtask_sp): if len(subtask) == 0: #right = (comment_line.find(':')+2)-len(comment_line) #subtask_sp = float(comment_line[right:]) + + # issuetype of testcase-creation/modification should be 'Test' + # if type == 'Test-Anpassung': + issue_type = 'Sub-task' + assignee = get_assignee(type) issue_dict = { 'project': {'id': 12501}, 'summary': "[{}] {}".format(type, escaped), 'description': issue.fields.description, - 'issuetype': {'name': 'Sub-task'}, + 'issuetype': {'name': issue_type}, + 'priority': {'name': issue.fields.priority.name}, 'parent': {'key': parent}, - # todo - json - #'components': [{'name': issue.fields.components}], - #'labels': [{'name': issue.fields.labels}], + 'labels': issue.fields.labels, + #'customfield_10022' - Story Points 'timetracking': {'originalEstimate': "{}d".format(subtask_sp)}, 'assignee': {'name': assignee} } @@ -44,6 +52,7 @@ def create_subtask(issue, parent, type, subtask_sp): for component in issue.fields.components: existingComponents.append({"name" : component.name}) new_issue.update(fields={"components": existingComponents}) + #new_issue.update(priority={"name": issue.fields.priority.name}) logging.info('Created Sub-Task {}'.format(new_issue)) else: @@ -57,15 +66,18 @@ logging.basicConfig(filename='sub_task_generator.log', host = "https://jira.wien.gv.at/" pat = "OTAyMTM3MjY0MjE0Ovz6bu0ti1TlEdSegmmiWiJ2wGeD" +sprint = 46 headers = JIRA.DEFAULT_OPTIONS["headers"].copy() headers["Authorization"] = f"Bearer {pat}" jira_server = JIRA(server=host, options={"headers": headers}) -#issues_Jql = jira_server.search_issues('filter=19527') #, 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') +# use filter or single issue +issues_Jql = jira_server.search_issues('filter=20406') #, expand='changelog') +#issues_Jql = jira_server.search_issues('key in (WRGWR-1558)') #, 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('Creating sub-tasks out of estimation of pre-planning sprint {}'.format(38)) +logging.info('Creating sub-tasks out of estimation of pre-planning sprint {}'.format(sprint)) xsp = lambda s: s or 0 issue: Issue @@ -73,47 +85,51 @@ for issue in issues_Jql: logging.info('Working on {}'.format(issue)) sp_sum = 0 - # sp = 0 - # if hasattr(issue.fields, 'customfield_10022'): - # sp = xsp(issue.fields.customfield_10022) - # sp_sum += sp + # only Issues with empty (or 0) story-points + sp = 0 + if hasattr(issue.fields, 'customfield_10022'): + sp = xsp(issue.fields.customfield_10022) - single_issue = jira_server.issue(issue.key) - comments = single_issue.fields.comment.comments + if sp != 0: + logging.info('Ignoring issue {} as there are Story-Points already omitted'.format(issue)) + else: + single_issue = jira_server.issue(issue.key) + comments = single_issue.fields.comment.comments - # Find all comments made by me on this issue. - for comment in comments: - comment_lines = comment.body.splitlines() + # Find all comments made by me on this issue. + for comment in comments: + comment_lines = comment.body.splitlines() - if len(comment_lines) == 5: - for comment_line in comment_lines: - if comment_line != "": - # pos = comment_lines[2].find('NN') - comment_line_parts = comment_line.split(':') + if len(comment_lines) == 5: + if comment_lines[0][:5] == "Dev B": + for comment_line in comment_lines: + if comment_line != "": + # pos = comment_lines[2].find('NN') + comment_line_parts = comment_line.split(':') - # if 1. Test* != NN create a sub-task (if not existing) - if len(comment_line_parts) == 2: - if comment_line_parts[1] != u'\xa0': # empty string - if comment_line_parts[1][-2:] != 'NN': # NN means NotNecessary - subtask_ph = float(comment_line_parts[1]) * 0.5 - if comment_line_parts[0][:15] != "Test-Ausführung": - create_subtask(issue, - issue.key, - comment_line_parts[0], - subtask_ph) - sp_sum += subtask_ph / 0.5 + # if 1. Test* != NN create a sub-task (if not existing) + if len(comment_line_parts) == 2: + if comment_line_parts[1] != u'\xa0': # empty string + if comment_line_parts[1][-2:] != 'NN': # NN means NotNecessary + subtask_ph = float(comment_line_parts[1]) * 0.5 + if comment_line_parts[0][:15] != "Test-Ausführung": + create_subtask(issue, + issue.key, + comment_line_parts[0], + subtask_ph) + sp_sum += subtask_ph / 0.5 - # find corresponding sub-task - # issuelinks.inwardIssue - try: - issue.update(fields={'customfield_10022': sp_sum}) - logging.info("Estimated story-points changed to Sum(Sub-Tasks story-points): {}".format(sp_sum, sp_sum)) + # find corresponding sub-task + # issuelinks.inwardIssue + try: + 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)) + except exceptions.JIRAError: + logging.error("Issue {} has no (visible) Story Points-attribute".format(issue.key)) - finally: - logging.info('Done {}'.format(issue.key)) + finally: + logging.info('Done {}'.format(issue.key)) #print('{}: {}, SP: {}'.format(issue.key, issue.fields.summary, sp)) diff --git a/JIRA_actualize_story_state.py b/JIRA_actualize_story_state.py new file mode 100644 index 0000000..862a8ad --- /dev/null +++ b/JIRA_actualize_story_state.py @@ -0,0 +1,73 @@ +# find all subtasks of a story and keep description in sync +# zisco 2022-05-03 07:25 + +from tkinter.tix import INTEGER +from tokenize import Number +from types import ClassMethodDescriptorType +from jira import JIRA, Issue, Comment, exceptions +import re +import csv +import xlsxwriter +import logging +import pymsteams +import asyncio +import os + + +# set https_proxy +os.environ['https_proxy'] = "http://uaproxy.wien.gv.at:3128" + +logging.basicConfig(filename='sub_task_generator.log', + filemode='a', + format='%(asctime)s - %(levelname)s: %(message)s', + level=logging.INFO, + encoding='utf-8') + +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=20266') #, expand='changelog') +#issues_Jql = jira_server.search_issues('key in (WRGWR-905)') #, 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('Actualize story-state out of sub-task state') + +issue: Issue +parent_issue: Issue +stateListSubTask = ['Done','Closed'] +stateListStory = ['Ready for Testing', 'Test', 'In Review', 'Done', 'Reopened'] + +for issue in issues_Jql: + logging.info('Working on {}'.format(issue)) + + # get all subtasks + allSubTasksDone = True + for subtask in issue.fields.subtasks: + if (subtask.fields.status.name not in stateListSubTask) and (allSubTasksDone): +# issue.update(notify=False, description=parent_issue.fields.description) +# logging.info('Updated sub-task description with story description') + allSubTasksDone = False + break + + # send info to teams as well + # TODO + + if allSubTasksDone and (issue.fields.status.name not in stateListStory): + logging.info('Story {} is in wrong state'.format(issue.key)) + + loop = asyncio.get_event_loop() + + # the async_connectorcard object is used instead of the normal one. + myTeamsMessage = pymsteams.async_connectorcard("https://atos365.webhook.office.com/webhookb2/f107169d-26b9-46ff-a973-b7aa1f37e552@33440fc6-b7c7-412c-bb73-0e70b0198d5a/IncomingWebhook/6cf64f8f6f1b40e198c1a981921afed1/181686a4-0d13-4990-a87a-e4814c1abc55") + + myTeamsMessage.title("JIRA Sub-Task Syncer") + myTeamsMessage.text('Story {} is in wrong state'.format(issue.key)) + myTeamsMessage.color("#F9EC26") + + # to send the message, pass to the event loop + loop.run_until_complete(myTeamsMessage.send()) \ No newline at end of file diff --git a/JIRA_keep_SubTask_in_sync.py b/JIRA_keep_SubTask_in_sync.py new file mode 100644 index 0000000..318d2b9 --- /dev/null +++ b/JIRA_keep_SubTask_in_sync.py @@ -0,0 +1,98 @@ +# find all subtasks of a story and keep description in sync +# zisco 2022-05-03 07:25 + +from tkinter.tix import INTEGER +from tokenize import Number +from types import ClassMethodDescriptorType +from jira import JIRA, Issue, Comment, exceptions +import re +import csv +import xlsxwriter +import logging +import pymsteams +import asyncio +import os + + +def update_subtask(issue, parent): + issue_dict = { + 'project': {'id': 12501}, + 'summary': "[{}] {}".format(type, escaped), + 'description': issue.fields.description, + 'issuetype': {'name': issue_type}, + 'priority': {'name': issue.fields.priority.name}, + 'parent': {'key': parent}, + 'labels': issue.fields.labels, + #'customfield_10022' - Story Points + 'timetracking': {'originalEstimate': "{}d".format(subtask_sp)}, + 'assignee': {'name': assignee} + } + new_issue = jira_server.create_issue(fields=issue_dict) + + # consider components + existingComponents = [] + for component in issue.fields.components: + existingComponents.append({"name" : component.name}) + new_issue.update(fields={"components": existingComponents}) + #new_issue.update(priority={"name": issue.fields.priority.name}) + + logging.info('Created Sub-Task {}'.format(new_issue)) + +# set https_proxy +os.environ['https_proxy'] = "http://uaproxy.wien.gv.at:3128" + +logging.basicConfig(filename='sub_task_generator.log', + filemode='a', + format='%(asctime)s - %(levelname)s: %(message)s', + level=logging.INFO, + encoding='utf-8') + +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=20262') #, expand='changelog') +#issues_Jql = jira_server.search_issues('key in (WRGWR-905)') #, 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('Keep sub-task description in sync with story description') + +issue: Issue +parent_issue: Issue +chng=0 +for issue in issues_Jql: + logging.info('Working on {}'.format(issue)) + + # get parent-issue + parent = issue.fields.parent + + parent_issue = jira_server.issue(parent.key) + + if parent_issue.fields.description != issue.fields.description: + issue.update(notify=False, description=parent_issue.fields.description) + logging.info('Updated sub-task description with story description') + + chng += 1 + # send info to teams as well + # TODO + +# no changes detected +# You must create the connectorcard object with the Microsoft Webhook URL +if chng == 0: + loop = asyncio.get_event_loop() + + # the async_connectorcard object is used instead of the normal one. + myTeamsMessage = pymsteams.async_connectorcard("https://atos365.webhook.office.com/webhookb2/f107169d-26b9-46ff-a973-b7aa1f37e552@33440fc6-b7c7-412c-bb73-0e70b0198d5a/IncomingWebhook/6cf64f8f6f1b40e198c1a981921afed1/181686a4-0d13-4990-a87a-e4814c1abc55") + + myTeamsMessage.title("JIRA Sub-Task Syncer") + myTeamsMessage.text("No changes in description detected") + myTeamsMessage.color("#F9EC26") + + myTeamsMessage.printme() + + # to send the message, pass to the event loop + loop.run_until_complete(myTeamsMessage.send()) \ No newline at end of file