Several enhancements
This commit is contained in:
@@ -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')
|
||||||
+64
-38
@@ -32,42 +32,54 @@ def take_closest(myList, myNumber):
|
|||||||
|
|
||||||
def get_assignee(type, issue):
|
def get_assignee(type, issue):
|
||||||
# isBuergerfrontend = False
|
# 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
|
# use assignee of story
|
||||||
return 'rab9001'
|
return cfg['def_assignees']['backend'] #'rab9001'
|
||||||
#return issue.fields.assignee.name
|
#return issue.fields.assignee.name
|
||||||
elif type == 'Dev Frontend':
|
elif type == 'Dev Frontend':
|
||||||
# for component in issue.fields.components:
|
# for component in issue.fields.components:
|
||||||
# isBuergerfrontend = component.name == "Bürgerfrontend"
|
# isBuergerfrontend = component.name == "Bürgerfrontend"
|
||||||
# if isBuergerfrontend:
|
# if isBuergerfrontend:
|
||||||
return 'thh9001'
|
return cfg['def_assignees']['frontend'] #'thh9001'
|
||||||
elif type == 'Dev Citizen-Frontend':
|
elif type == 'Dev Citizen-Frontend':
|
||||||
return 'reu9001'
|
return cfg['def_assignees']['cit_frontend'] #'reu9001'
|
||||||
elif type.startswith('Test-Anpassung') or type == 'DoD Test':
|
elif type.startswith('Test-Anpassung') or type == 'DoD Test':
|
||||||
if issue.key.startswith('WRGWR-'):
|
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:
|
else:
|
||||||
return 'osm9005'
|
return cfg['def_assignees']['default'] #'zim9003'
|
||||||
elif type == 'DoD PO':
|
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):
|
def create_subtask(issue, parent, type, subtask_sp=0, estimation_remark="", no_unit_test=False):
|
||||||
escaped = issue.fields.summary.replace('"', r'\"')
|
escaped = issue.fields.summary.replace('"', r'\"')
|
||||||
logging.info(f'summary ~ \"{escaped}\"')
|
logging.info(f'summary ~ \"{escaped}\"')
|
||||||
|
|
||||||
assignee = get_assignee(type, issue)
|
assignee = get_assignee(type, issue)
|
||||||
description = get_description(type)
|
description = get_description(type, no_unit_test=no_unit_test)
|
||||||
|
|
||||||
try:
|
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:
|
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:
|
except exceptions.JIRAError:
|
||||||
# correct estimation
|
# correct estimation
|
||||||
#subtask.fields('timetracking': {'originalEstimate'}).Value() = subtask_sp
|
#subtask.fields('timetracking': {'originalEstimate'}).Value() = subtask_sp
|
||||||
single_issue = jira_server.issue(subtask[0].key)
|
#single_issue = jira_server.issue(subtask[0].key)
|
||||||
try:
|
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:
|
except Exception as e:
|
||||||
logging.info(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))
|
logging.info('Created Sub-Task {}'.format(new_issue))
|
||||||
|
|
||||||
def get_description(type):
|
def get_description(type, no_unit_test=False):
|
||||||
if (type == 'DoD SA/DEV'):
|
if (type == 'Dev Backend'):
|
||||||
description = cfg['subtask_desc_dev']
|
description = cfg['templates']['subtask_desc_be_dev']
|
||||||
elif (type == 'DoD Test'):
|
if no_unit_test==False:
|
||||||
description = cfg['subtask_desc_test']
|
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'):
|
elif (type == 'DoD PO'):
|
||||||
description = cfg['subtask_desc_po']
|
description = cfg['templates']['subtask_desc_po']
|
||||||
else:
|
else:
|
||||||
description = 's. Story'
|
description = 's. Story'
|
||||||
return description
|
return description
|
||||||
@@ -124,7 +145,7 @@ with open("config.yaml", encoding='utf8') as f:
|
|||||||
|
|
||||||
host = "https://jira.wien.gv.at/"
|
host = "https://jira.wien.gv.at/"
|
||||||
pat = "OTAyMTM3MjY0MjE0Ovz6bu0ti1TlEdSegmmiWiJ2wGeD"
|
pat = "OTAyMTM3MjY0MjE0Ovz6bu0ti1TlEdSegmmiWiJ2wGeD"
|
||||||
sprint = 6
|
sprint = 5
|
||||||
|
|
||||||
headers = JIRA.DEFAULT_OPTIONS["headers"].copy()
|
headers = JIRA.DEFAULT_OPTIONS["headers"].copy()
|
||||||
headers["Authorization"] = f"Bearer {pat}"
|
headers["Authorization"] = f"Bearer {pat}"
|
||||||
@@ -132,7 +153,8 @@ jira_server = JIRA(server=host, options={"headers": headers})
|
|||||||
|
|
||||||
# use filter or single issue
|
# use filter or single issue
|
||||||
issues_Jql = jira_server.search_issues('filter=25827') #, expand='changelog')
|
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')
|
#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()}')
|
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:
|
for comment in comments:
|
||||||
comment_lines = comment.body.splitlines()
|
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 comment_lines[0][:5] == "Dev B":
|
||||||
|
if "Dev Unit-Tests: NN" in comment_lines:
|
||||||
|
no_unit_test = True
|
||||||
|
|
||||||
for comment_line in comment_lines:
|
for comment_line in comment_lines:
|
||||||
if comment_line != "":
|
if comment_line != "":
|
||||||
# pos = comment_lines[2].find('NN')
|
# 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
|
subtask_ph = float(comment_line_sub_parts[0]) * 0.5
|
||||||
estimation_remark = ""
|
estimation_remark = ""
|
||||||
else:
|
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)
|
subtask_ph = take_closest([1,2,3,5,8,13,21,42], sp_sum/3)
|
||||||
|
|
||||||
if comment_line_parts[0][:15] != "Test-Ausführung":
|
#if comment_line_parts[0][:15] != "Test-Ausführung":
|
||||||
create_subtask(issue,
|
create_subtask(issue,
|
||||||
issue.key,
|
issue.key,
|
||||||
comment_line_parts[0],
|
comment_line_parts[0],
|
||||||
subtask_ph,
|
subtask_ph,
|
||||||
estimation_remark)
|
estimation_remark,
|
||||||
|
no_unit_test=no_unit_test)
|
||||||
sp_sum += subtask_ph / 0.5
|
sp_sum += subtask_ph / 0.5
|
||||||
else:
|
else:
|
||||||
logging.info(f'Sub-task creation skipped {comment_line_parts[0]} equals \'NN\'')
|
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
|
# find corresponding sub-task
|
||||||
# issuelinks.inwardIssue
|
# issuelinks.inwardIssue
|
||||||
try:
|
try:
|
||||||
if sp != sp_sum:
|
if sp != sp_sum:
|
||||||
issue.update(fields={'customfield_10022': sp_sum})
|
if cfg['update_sp_sum']:
|
||||||
logging.info("Estimated story-points changed to Sum(Sub-Tasks story-points): {}".format(sp_sum, 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:
|
except exceptions.JIRAError:
|
||||||
logging.error("Issue {} has no (visible) Story Points-attribute".format(issue.key))
|
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))
|
logging.info('Done {}'.format(issue.key))
|
||||||
|
|
||||||
#create standard sub-tasks
|
#create standard sub-tasks
|
||||||
for type in ['DoD SA/DEV','DoD Test','DoD PO']:
|
if cfg['separate_dod_subtasks']:
|
||||||
create_subtask(issue,
|
for type in ['DoD SA','DoD PO']:
|
||||||
issue.key,
|
create_subtask(issue,
|
||||||
type,
|
issue.key,
|
||||||
no_unit_test=no_unit_test
|
type,
|
||||||
)
|
no_unit_test=no_unit_test
|
||||||
|
)
|
||||||
|
|
||||||
#add or remove label #Unit-Test on story-level
|
#add or remove label #Unit-Test on story-level
|
||||||
if no_unit_test == False:
|
if no_unit_test == False:
|
||||||
|
|||||||
Reference in New Issue
Block a user