|
| 1 | +import argparse |
| 2 | +import pprint |
| 3 | +import requests |
| 4 | +import datetime |
| 5 | +import matplotlib.dates as mdates |
| 6 | +import pandas as pd |
| 7 | +import matplotlib.pyplot as plt |
| 8 | +import urllib.request |
| 9 | +import urllib.parse |
| 10 | +import json |
| 11 | +import datetime |
| 12 | +from pytz import timezone |
| 13 | + |
| 14 | +# ---- # |
| 15 | +# init # |
| 16 | +# ---- # |
| 17 | +point_dict = {} |
| 18 | +total_points = 0 |
| 19 | +done = 0 |
| 20 | +undone = 0 |
| 21 | +parser = argparse.ArgumentParser() |
| 22 | +parser.add_argument('instancename') |
| 23 | +parser.add_argument('authstring') |
| 24 | +parser.add_argument('sprintname') |
| 25 | +args = parser.parse_args() |
| 26 | +BASIC = 'Basic ' + args.authstring |
| 27 | + |
| 28 | +# ---------- # |
| 29 | +# Get Sprint # |
| 30 | +# ---------- # |
| 31 | +params = { |
| 32 | + 'sysparm_query': 'short_description=' + args.sprintname |
| 33 | +} |
| 34 | +param = urllib.parse.urlencode(params) |
| 35 | +url = "https://" + args.instancename + ".service-now.com/api/now/table/rm_sprint?" + param |
| 36 | +req = urllib.request.Request(url) |
| 37 | +req.add_header("authorization", BASIC) |
| 38 | +with urllib.request.urlopen(req) as res: |
| 39 | + r = res.read().decode("utf-8") |
| 40 | +obj = json.loads(r) |
| 41 | +# Get the start and end dates of a Sprint |
| 42 | +start_date = obj['result'][0]['start_date'] |
| 43 | +start_date = (datetime.datetime.strptime(start_date, '%Y-%m-%d %H:%M:%S') + datetime.timedelta(hours=9)).date() |
| 44 | +print(start_date) |
| 45 | +end_date = obj['result'][0]['end_date'] |
| 46 | +end_date = (datetime.datetime.strptime(end_date, '%Y-%m-%d %H:%M:%S') + datetime.timedelta(hours=9)).date() |
| 47 | +# Initializing the points array |
| 48 | +while start_date <= end_date: |
| 49 | + point_dict[str(start_date)] = 0 |
| 50 | + start_date = start_date + datetime.timedelta(days=1) |
| 51 | +# --------- # |
| 52 | +# Get Story # |
| 53 | +# --------- # |
| 54 | +params = { |
| 55 | + 'sysparm_query': 'sprint.short_descriptionLIKE' + args.sprintname |
| 56 | +} |
| 57 | +param = urllib.parse.urlencode(params) |
| 58 | +url = "https://" + args.instancename + ".service-now.com/api/now/table/rm_story?" + param |
| 59 | +req = urllib.request.Request(url) |
| 60 | +req.add_header("authorization", BASIC) |
| 61 | +with urllib.request.urlopen(req) as res: |
| 62 | + r = res.read().decode("utf-8") |
| 63 | +obj = json.loads(r) |
| 64 | +# Story Loop |
| 65 | +for name in obj['result']: |
| 66 | + if len(name['story_points']) > 0: |
| 67 | + total_points += int(name['story_points']) |
| 68 | + if name['closed_at'] != '': |
| 69 | + close_date = datetime.datetime.strptime( |
| 70 | + name['closed_at'], '%Y-%m-%d %H:%M:%S') |
| 71 | + close_date = close_date.date() |
| 72 | + if name['state'] == '3': |
| 73 | + if str(close_date) in point_dict: |
| 74 | + point_dict[str(close_date)] += int(name['story_points']) |
| 75 | + else: |
| 76 | + point_dict[str(close_date)] = int(name['story_points']) |
| 77 | + if name['state'] == '3': |
| 78 | + done += int(name['story_points']) |
| 79 | + else: |
| 80 | + undone += int(name['story_points']) |
| 81 | +counta = 0 |
| 82 | +for i in point_dict.items(): |
| 83 | + counta += int(i[1]) |
| 84 | + point_dict[i[0]] = total_points - counta |
| 85 | +plt.xkcd() |
| 86 | +fig, ax = plt.subplots() |
| 87 | +# Creating a performance line |
| 88 | +x = [] |
| 89 | +y = [] |
| 90 | +plt.ylim(0, total_points + 5) |
| 91 | +counta = 0 |
| 92 | +for key in point_dict.keys(): |
| 93 | + if datetime.datetime.today() >= datetime.datetime.strptime(key, '%Y-%m-%d'): |
| 94 | + x.append(datetime.datetime.strptime(key, '%Y-%m-%d')) |
| 95 | + y.append(point_dict[key]) |
| 96 | +# Holiday determination |
| 97 | +DATE = "yyyymmdd" |
| 98 | +def isBizDay(DATE): |
| 99 | + Date = datetime.date(int(DATE[0:4]), int(DATE[4:6]), int(DATE[6:8])) |
| 100 | + if Date.weekday() >= 5: |
| 101 | + return 0 |
| 102 | + else: |
| 103 | + return 1 |
| 104 | +# Get the number of weekdays |
| 105 | +total_BizDay = 0 |
| 106 | +for key in point_dict.keys(): |
| 107 | + if isBizDay(key.replace('-', '')) == 1: |
| 108 | + total_BizDay += 1 |
| 109 | +# Creating an ideal line |
| 110 | +x2 = [] |
| 111 | +y2 = [] |
| 112 | +point_dict_len = len(point_dict) |
| 113 | +average = total_points / (total_BizDay - 1) |
| 114 | +for key in point_dict.keys(): |
| 115 | + dtm = datetime.datetime.strptime(key, '%Y-%m-%d') |
| 116 | + x2.append(dtm) |
| 117 | + y2.append(total_points) |
| 118 | + # If the next day is a weekday, consume the ideal line. |
| 119 | + if isBizDay((dtm + datetime.timedelta(days=1)).strftime("%Y%m%d")) == 1: |
| 120 | + total_points -= average |
| 121 | +days = mdates.DayLocator() |
| 122 | +daysFmt = mdates.DateFormatter('%m/%d') |
| 123 | +ax.xaxis.set_major_locator(days) |
| 124 | +ax.xaxis.set_major_formatter(daysFmt) |
| 125 | +plt.title("" + args.sprintname + " Burndown") |
| 126 | +plt.plot(x2, y2, label="Ideal", color='green') |
| 127 | +plt.plot(x2, y2, marker='.', markersize=20, color='green') |
| 128 | +plt.plot(x, y, label="Actual", color='red') |
| 129 | +plt.plot(x, y, marker='.', markersize=20, color='red') |
| 130 | +plt.grid() |
| 131 | +plt.xlabel("Days") |
| 132 | +plt.ylabel("Remaining Effort(pts)") |
| 133 | +plt.subplots_adjust(bottom=0.2) |
| 134 | +plt.legend() |
| 135 | +# Viewing the graph |
| 136 | +# plt.show() |
| 137 | +# Saving a graph |
| 138 | +plt.savefig('figure.png') |
0 commit comments