Skip to content

Instantly share code, notes, and snippets.

@noandrea
Last active August 2, 2024 14:42
Show Gist options
  • Save noandrea/734aec5cbfd94d1debcb2ead213fd14d to your computer and use it in GitHub Desktop.
Save noandrea/734aec5cbfd94d1debcb2ead213fd14d to your computer and use it in GitHub Desktop.
Grooming for issues
#!/home/andrea/.local/share/miniconda3/bin/python
# Installation
#
# python dependencies
# pip3 install jira questionary
#
# create the env var to connect to jira
# export JIRA_USER=user@myorg.com
# export JIRA_TOKEN=***
# export JIRA_HOST=https://MYORG.atlassian.net
# https://jira.readthedocs.io/
from jira import JIRA
import os
import datetime
import questionary
import json
def get_client():
user = os.getenv("JIRA_USER")
token = os.getenv("JIRA_TOKEN")
host = os.getenv("JIRA_HOST")
return JIRA(server=host, basic_auth=(user, token))
def to_date(date_string):
return datetime.datetime.fromisoformat(date_string)
def main():
client = get_client()
priority_weight = {
"Lowest": 1000,
"Low": 500,
"Medium": 100,
"High": 50,
"Highest": 10
}
# the query is to get all open issues and re-prioritize them
q = 'project = "MOON" and status in (Triage) ORDER BY created DESC'
print(f"JQL:\n {q}")
# do_update = questionary.confirm("Do you want to update the issues?", default=False).ask()
do_update = False
options = {"Top 5": 5, "Top 10": 10, "Top 20": 20, "All": None}
howMany = questionary.select(
"How many issues do you want to see?",
choices=options,
use_shortcuts=True
).ask()
issues = []
for issue in client.search_issues(q, maxResults=None):
age = (datetime.datetime.now(tz=datetime.timezone.utc) - to_date(issue.fields.updated)).days
weight = priority_weight[issue.fields.priority.name]
score = weight * age
issues.append({"key": issue.key, "score": score, "_age": age, "_weight": weight, "_priority": issue.fields.priority.name, "_updated": issue.fields.updated})
if do_update:
client.update(issue.key, fields={"priority": {"name": "High"}})
# sort the issues by score
issues.sort(key=lambda x: x.get("score", 0), reverse=False)
print(f"{'Key':10} | {'Score':10} | {'URL'}")
for record in issues[-options[howMany]:]:
print(f'{record["key"]:10} | {record["score"]:10.0f} | {os.getenv("JIRA_HOST")}/browse/{record["key"]}')
#print(f'{record["key"]:10} [{record["score"]:10.0f}] ({record["_priority"]} {record["_age"]} {record["_weight"]} {record["_updated"]})')
print(f"{'Total':10} | {issues.__len__():10.0f}")
# client.update(fields={"priority": {"name": "High"}})
# Story points per release per user
def get_story_points_per_release_per_user():
STORY_POINT_ESTIMATE_FIELD = "customfield_10016"
client = get_client()
# list the releases
releases = client.project_versions("MOON")
releases.sort(key=lambda x: x.raw.get("releaseDate", "5000-01-01"), reverse=True)
do_select_release = True
while do_select_release:
# select the release
selected = questionary.checkbox("Select the release", choices=[release.name for release in releases]).ask()
if selected.__len__() == 0:
print("at least one release must be selected")
continue
# get the issue for the release
q = f'project = "MOON" and fixVersion in ({",".join(selected)}) and type != Epic'
print(f"JQL:\n {q}")
# "user": {"story_points": 0, "issues": 0}
metrics_by_user = {}
for issue in client.search_issues(q):
sp = issue.get_field(STORY_POINT_ESTIMATE_FIELD) or 0
if sp == 0:
print(f'⚠️ issue {os.getenv("JIRA_HOST")}/browse/{issue.key} has no story points {issue.fields.creator.displayName}')
assignee = issue.get_field("assignee")
if assignee is None:
print(f'⚠️ issue {os.getenv("JIRA_HOST")}/browse/{issue.key} has no assignee')
assignee = "Unassigned"
else:
assignee = assignee.displayName
data = metrics_by_user.get(assignee, {"story_points": 0, "issues": 0})
data["story_points"] += sp
data["issues"] += 1
metrics_by_user[assignee] = data
# transform the dictionary into a list
# sort the list by story points
summary = sorted([{'n': k, 'p': v['story_points'], 'i': v['issues']} for k,v in metrics_by_user.items()], key=lambda x: x["p"], reverse=True)
print(f'Summary for version ({",".join(selected)})')
print(f"{'User':20s} | {'Story Points':15s} | {'Issues':10s}")
print("-" * 50)
sum_story_points = 0
sum_issues = 0
for record in summary:
sum_story_points += record["p"]
sum_issues += record["i"]
print(f'{record["n"]:20s} | {record["p"]:15.0f} | {record["i"]:10.0f}')
print("-" * 50)
print(f"{'Total':20s} | {sum_story_points:15.0f} | {sum_issues:10.0f}")
do_select_release = questionary.confirm("Do you want to select another release?").ask()
if __name__ == '__main__':
opt1 = "Re-prioritize issues"
opt2 = "Analyze release story points"
answer = questionary.select(
"Select the action",
choices=[opt1, opt2],
use_shortcuts=True
).ask()
if answer == opt1:
main()
elif answer == opt2:
get_story_points_per_release_per_user()
else:
print("Invalid option")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment