def pull_kanban(self, project, time_range): if 'BRD-' + project.key not in self.cache.data[project.key].keys(): kanban = Release() kanban.key = 'BRD-%s' % project.key kanban.version = kanban.key kanban.name = 'Kanban Board' transaction.begin() self.cache.data[project.key][kanban.key] = kanban transaction.commit() transaction.begin() docid = self.cache.document_map.add( ['jira', str(project.key), kanban.key]) self.cache.catalog.index_doc(docid, kanban) transaction.commit() else: kanban = self.cache.data[project.key]['BRD-' + project.key] self.pull_kanban_stories(project, kanban, time_range)
def pull_scrum(self, project, time_range): for spr in self.agile.sprints(int(project.key)): print 'Sprint:', spr.name sid = getattr(spr, 'id') if str(sid) not in self.cache.data[project.key].keys(): sprint = Release() sprint.key = str(sid) sprint.version = str(sid) sprint.name = spr.name transaction.begin() self.cache.data[project.key][str(sid)] = sprint transaction.commit() transaction.begin() docid = self.cache.document_map.add( ['jira', project.key, str(sid)]) self.cache.catalog.index_doc(docid, sprint) transaction.commit() else: sprint = self.cache.data[project.key][str(sid)] self.pull_scrum_stories(project, sprint)
def run(self, jira, args): parser = argparse.ArgumentParser() parser.add_argument('dir', nargs='?') try: args = parser.parse_args(args) except: return project = Project('fP1', 'Fake Project 1', 'ted') if not 'fP1' in Jira.cache.data: Jira.cache.data['fP1'] = project docid = Jira.cache.document_map.add( ['jira', '', '', project.key] ) Jira.cache.catalog.index_doc(docid, project) release = Release('f1.0') if not 'f1.0' in Jira.cache.data: Jira.cache.data['fP1']['f1.0'] = release for id in range(1, 20): data = {} data['key'] = 'fS-%d' % id data['title'] = 'Story %s' % id data['points'] = [1,2,3,5,8,13,21][int(random() * 7)] data['ready'] = datetime(2015, 1, 1) started = int(random() * 15 + 1) data['started'] = datetime(2015, 1, started) if random() > 0.4: month = 1 resolved = started + int(random() * data['points'] * 2) if resolved > 30: month = 2 resolved = resolved - 30 data['resolved'] = datetime(2015, month, resolved) data['dev'] = ['ann', 'nic', 'sam', 'mia'][int(random() * 4)] story = make_story(data) release.add_story(story) docid = Jira.cache.document_map.add( ['jira', 'fP1', 'f1.0', story.key] ) Jira.cache.catalog.index_doc(docid, story) print 'Created fake project "fP1"'
def run(self, jira, args): parser = argparse.ArgumentParser() parser.add_argument('team', nargs='?') parser.add_argument('-c', nargs='*', required=False) parser.add_argument('-d', nargs='*', required=False) parser.add_argument('-e', action='store_true', required=False) parser.add_argument('-f', action='store_true', required=False) parser.add_argument('-g', nargs='?', required=False) parser.add_argument('-l', nargs='*', required=False) parser.add_argument('-k', nargs='*', required=False) parser.add_argument('-o', nargs='*', required=False) parser.add_argument('-p', nargs='*', required=False) parser.add_argument('-v', nargs='*', required=False) parser.add_argument('-s', nargs='*', required=False) parser.add_argument('-t', nargs='*', required=False) parser.add_argument('-x', nargs='*', required=False) try: self.args = parser.parse_args(args) except: return self.release = jira.cache.get_by_path(jira.cache.cwd) if not isinstance(self.release, Release): print 'Error: Must navigate to a release. (hint: help cd)' return if self.args.f: self.cycle_time = 'aggregate_cycle_time' else: self.cycle_time = 'cycle_time' types = ['7'] if self.args.t: if len(self.args.t) == 1: types = self.args.t[0].split(',') else: types = self.args.t if self.args.team: stories = [s for s in self.release.clean_stories(type=types) if s.scrum_team and s.scrum_team[:len(self.args.team)] \ == self.args.team] self.release = Release() for story in stories: self.release.add_story(story) if self.args.d: stories = [s for s in self.release.clean_stories(type=types) if s.developer and s.developer[:len(self.args.d[0])] \ == self.args.d[0]] self.release = Release() for story in stories: self.release.add_story(story) if self.args.v: stories = [s for s in self.release.clean_stories(type=types) if s.points and s.points == float(self.args.p[0])] self.release = Release() for story in stories: self.release.add_story(story) if self.args.c: cycle_time = int(self.args.c[0]) stories = [s for s in self.release.clean_stories(type=types) if getattr(s, self.cycle_time) < cycle_time] self.release = Release() for story in stories: self.release.add_story(story) if self.args.k: hide_keys = [] show_keys = [] for k in self.args.k: if k[0] == '!': hide_keys.append('NG-' + k[1:]) else: show_keys.append('NG-' + k) if show_keys: stories = [s for s in self.release.clean_stories(type=types) and s.key in show_keys] self.release = Release() for story in stories: self.release.add_story(story) if hide_keys: stories = [s for s in self.release.clean_stories(type=types) and s.key not in hide_keys] self.release = Release() for story in stories: self.release.add_story(story) if self.args.x: self.file = 'cycles-%s.%s' % (self.release.version, self.args.x[0]) else: self.file = None kanban = self.release.kanban() stories = self.release.clean_stories(type=types) if self.args.p: stories = [s for s in stories if s.priority in self.args.p] if not stories: print 'No data to report' return stories.sort(key=lambda i:i.key) if self.args.s: sorting = self.args.s if 'cycle_time' not in sorting: sorting.append('cycle_time') else: sorting = ['cycle_time'] if self.args.o and self.args.o[0] == 'hist': self.histogram(stories) elif self.args.o and self.args.o[0] == 'arrival': if len(self.args.o) == 2: if self.args.o[1] in self.release.WIP.keys(): state = self.release.WIP[self.args.o[1]] elif int(self.args.o[1]) in self.release.WIP.values(): state = int(self.args.o[1]) else: print 'Invalid state specified: %s' % self.args.o[1] return self.arrivals(stories, state) else: self.arrivals(stories) elif not self.args.o or self.args.o[0] == 'cycles': self.cycles(stories, sorting) else: print 'Unknown chart type: %s' % self.args.t[0]
class Command(BaseCommand): help = 'Render various charts' usage = 'chart [team] [-o chart_type] [-d developer] [-s sort_by]' \ ' [-p point] [-c cycle_time] [-x file_name.ext] [-t issue types] [-f]' \ ' [-l [issue keys]]' options_help = ''' -c : specify cycle time outlier limit -d : chart for developer -e : include estimates subplot -k : chart using or surpressing specific issue keys -f : calculate cycle times from the first in process date (default is last) -g : group stories by this strategy (default || estimate || resolved) -l : label points -o : specify chart type: cycle (default) || hist || arrival [state] -p : priority to chart -s : sorting criteria -t : issue types to chart -v : chart for estimate value -x : export graph to a file (valid extensions are pdf, png, or jpg) ''' examples = ''' chart chart App chart -k !1234 chart -o arrival 10090 chart -s scrum_team points -t 3 7 15''' def run(self, jira, args): parser = argparse.ArgumentParser() parser.add_argument('team', nargs='?') parser.add_argument('-c', nargs='*', required=False) parser.add_argument('-d', nargs='*', required=False) parser.add_argument('-e', action='store_true', required=False) parser.add_argument('-f', action='store_true', required=False) parser.add_argument('-g', nargs='?', required=False) parser.add_argument('-l', nargs='*', required=False) parser.add_argument('-k', nargs='*', required=False) parser.add_argument('-o', nargs='*', required=False) parser.add_argument('-p', nargs='*', required=False) parser.add_argument('-v', nargs='*', required=False) parser.add_argument('-s', nargs='*', required=False) parser.add_argument('-t', nargs='*', required=False) parser.add_argument('-x', nargs='*', required=False) try: self.args = parser.parse_args(args) except: return self.release = jira.cache.get_by_path(jira.cache.cwd) if not isinstance(self.release, Release): print 'Error: Must navigate to a release. (hint: help cd)' return if self.args.f: self.cycle_time = 'aggregate_cycle_time' else: self.cycle_time = 'cycle_time' types = ['7'] if self.args.t: if len(self.args.t) == 1: types = self.args.t[0].split(',') else: types = self.args.t if self.args.team: stories = [s for s in self.release.clean_stories(type=types) if s.scrum_team and s.scrum_team[:len(self.args.team)] \ == self.args.team] self.release = Release() for story in stories: self.release.add_story(story) if self.args.d: stories = [s for s in self.release.clean_stories(type=types) if s.developer and s.developer[:len(self.args.d[0])] \ == self.args.d[0]] self.release = Release() for story in stories: self.release.add_story(story) if self.args.v: stories = [s for s in self.release.clean_stories(type=types) if s.points and s.points == float(self.args.p[0])] self.release = Release() for story in stories: self.release.add_story(story) if self.args.c: cycle_time = int(self.args.c[0]) stories = [s for s in self.release.clean_stories(type=types) if getattr(s, self.cycle_time) < cycle_time] self.release = Release() for story in stories: self.release.add_story(story) if self.args.k: hide_keys = [] show_keys = [] for k in self.args.k: if k[0] == '!': hide_keys.append('NG-' + k[1:]) else: show_keys.append('NG-' + k) if show_keys: stories = [s for s in self.release.clean_stories(type=types) and s.key in show_keys] self.release = Release() for story in stories: self.release.add_story(story) if hide_keys: stories = [s for s in self.release.clean_stories(type=types) and s.key not in hide_keys] self.release = Release() for story in stories: self.release.add_story(story) if self.args.x: self.file = 'cycles-%s.%s' % (self.release.version, self.args.x[0]) else: self.file = None kanban = self.release.kanban() stories = self.release.clean_stories(type=types) if self.args.p: stories = [s for s in stories if s.priority in self.args.p] if not stories: print 'No data to report' return stories.sort(key=lambda i:i.key) if self.args.s: sorting = self.args.s if 'cycle_time' not in sorting: sorting.append('cycle_time') else: sorting = ['cycle_time'] if self.args.o and self.args.o[0] == 'hist': self.histogram(stories) elif self.args.o and self.args.o[0] == 'arrival': if len(self.args.o) == 2: if self.args.o[1] in self.release.WIP.keys(): state = self.release.WIP[self.args.o[1]] elif int(self.args.o[1]) in self.release.WIP.values(): state = int(self.args.o[1]) else: print 'Invalid state specified: %s' % self.args.o[1] return self.arrivals(stories, state) else: self.arrivals(stories) elif not self.args.o or self.args.o[0] == 'cycles': self.cycles(stories, sorting) else: print 'Unknown chart type: %s' % self.args.t[0] def histogram(self, stories): bins = len(stories)/5 if bins < 10: bins = 10 cycle_times = [s.cycle_time for s in stories if s.cycle_time] param = scipy.stats.norm.fit(cycle_times) x = numpy.linspace(min(cycle_times), max(cycle_times), 200) pdf_fitted = scipy.stats.norm.pdf(x, loc=param[0], scale=param[1]) pylab.plot(x, pdf_fitted, 'r-', label='Fitted') pylab.hist(cycle_times, bins, normed=True, alpha=.3) pylab.show(block=False) def arrivals(self, stories, state=6): ''' Chart a plot point for every arrival time in state ''' arrivals = self.release.kanban().state_arrival_interval(state) dates = [a['date'] for a in arrivals] arrivals = [round(a['interval']/60./60., 1) for a in arrivals] average = numpy.median([arrivals]) std = numpy.std([arrivals]) iql = numpy.percentile([arrivals], 25) iqh = numpy.percentile([arrivals], 75) nsul = [] nsuw = [] nsll = [] nslw = [] avg = [] for x in arrivals: nsul.append(average + (iqh * 3)) nsuw.append(average + (iqh * 2)) nslw.append(average - (iql * 2)) nsll.append(average - (iql * 3)) avg.append(average) pyplot.plot(dates, arrivals, '*', color='g') pyplot.plot(dates, nsul, 'o', linestyle='-', color='r') pyplot.plot(dates, nsuw, '.', linestyle=':', color='y') pyplot.plot(dates, nslw, '.', linestyle=':', color='y') pyplot.plot(dates, nsll, 'o', linestyle='-', color='r') pyplot.plot(dates, avg, '',linestyle='-.', markerfacecolor='None') pyplot.show(block=False) def cycles(self, stories, sorting): data = [] wip = [] estimates = [] estimate_labels = [] developer_labels = [] alldata = [] labels = [] ratios = [] count = [0] def compare(a, b): if not a[0]: return -1 if not b[0]: return 1 return cmp(a, b) stories.sort(key=lambda x:tuple([getattr(x, key) for key in sorting]), cmp=compare) for story in stories: if not story.started: continue alldata.append(getattr(story, self.cycle_time)) if not story.resolved: wip.append(getattr(story, self.cycle_time)) data.append(None) else: data.append(getattr(story, self.cycle_time)) wip.append(None) estimates.append(story.points) labels.append(story.key) estimate_labels.append(story.key) developer_labels.append(story.developer) if self.args.g and not self.args.g == 'default': if self.args.g == 'estimate': count.append(getattr(story, sorting[0])) elif self.args.g == 'resolved': if story.resolved: count.append(story.resolved) elif story.started: count.append(story.started) else: count.append(datetime.datetime.now()) else: count.append(count[-1] + 1) all_non_empty_data = [d for d in alldata if d] if not all_non_empty_data: print 'Nothing to do. Probably need to finish some work.' return std = numpy.std(all_non_empty_data) iql = numpy.percentile(all_non_empty_data, 25) iqh = numpy.percentile(all_non_empty_data, 75) average = numpy.median(all_non_empty_data) nsul = [] nsuw = [] nsll = [] nslw = [] avg = [] for x in data: nsul.append(average + (iqh * 3)) nsuw.append(average + (iqh * 2)) nslw.append(average - (iql * 2)) nsll.append(average - (iql * 3)) avg.append(average) if self.args.e: gs = gridspec.GridSpec(2, 1, height_ratios=[3, 1]) pyplot.subplot(gs[0]) pyplot.plot(count[1:], data, '*', color='g') pyplot.plot(count[1:], wip, '^', color='r') pyplot.plot(count[1:], nsul, 'o', linestyle='-', color='r') pyplot.plot(count[1:], nsuw, '.', linestyle=':', color='y') pyplot.plot(count[1:], nslw, '.', linestyle=':', color='y') pyplot.plot(count[1:], nsll, 'o', linestyle='-', color='r') pyplot.plot(count[1:], avg, '',linestyle='-.', markerfacecolor='None') previous_y = None y_label = 10 for label, x, y in zip(labels, count[1:], alldata): if not y: y = 0.0 if y == previous_y: y_label += 10 else: previous_y = y y_label = 10 if self.args.l is None: if y < iqh * 3 + average: continue if self.args.l is not None and len(self.args.l) \ and label not in self.args.l: continue pyplot.annotate( label, url='http://www.google.com', xy=(x, y), xytext=(-10,y_label), textcoords = 'offset points', ha='right', va='bottom', fontsize=7, bbox = dict(boxstyle = 'round,pad=0.3', fc='yellow', alpha=0.5), arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0')) yoffset = -10 odd = True for label, x, y in zip(developer_labels, count[1:], nsll): if not self.args.l: continue if odd: odd = False else: odd = True continue if not label: continue if yoffset < -30: yoffset = -10 pyplot.annotate( label, url='http://www.google.com', xy=(x, y), xytext=(10, yoffset), textcoords = 'offset points', ha='right', va='bottom', fontsize=8, bbox = dict(boxstyle = 'round,pad=0.3', fc='cyan', alpha=0.1), arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0')) yoffset -= 10 odd = True yoffset = 10 for label, x, y in zip(developer_labels, count[1:], nsul): if not self.args.l: continue if not self.args.l: if y < iqh * 3: continue if odd: odd = False continue else: odd = True if not label: continue if yoffset > 30: yoffset = 10 pyplot.annotate( label, url='http://www.google.com', xy=(x, y), xytext=(10, yoffset), textcoords = 'offset points', ha='right', va='bottom', fontsize=8, bbox = dict(boxstyle = 'round,pad=0.3', fc='cyan', alpha=0.1), arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0')) yoffset += 10 if not self.args.e: if self.file: pyplot.savefig(self.file, bbox=0) else: pyplot.show(block=False) return pyplot.grid(True) pyplot.subplot(gs[1]) pyplot.plot(count[1:], estimates, 'o', linestyle='', color='b') previous_label = '' label_count = 0 elevated = True for label, x, y in zip(estimate_labels, count[1:], estimates): if not self.args.l: continue if label == previous_label: label_count += 1 continue if label_count <=1 and not elevated: elevated = True yoffset = 25 else: elevated = False yoffset = 10 label_count = 0 previous_label = label pyplot.annotate( label, url='http://www.google.com', xy=(x, y), xytext=(-10,yoffset), textcoords = 'offset points', ha='right', va='bottom', fontsize=7, bbox = dict(boxstyle = 'round,pad=0.3', fc='yellow', alpha=0.5), arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0')) if self.file: pyplot.savefig(self.file, bbox=0) else: pyplot.show(block=False)
def make_release(self, average, std, stories, bandwidth, count, num_pairs, teams, std_dev_ct, avg_pts, std_pts): release = Release() sim = len(dao.Jira.cache.data['SIMS']) + 1 release.version = 'SIM-%d' % sim transaction.begin() dao.Jira.cache.data['SIMS'][release.version] = release transaction.commit() #tasks = scipy.stats.norm.rvs(loc=average, scale=std, size=stories) tasks = numpy.random.gumbel(loc=average, scale=std, size=stories) tasks = [round(task, 1) if task >= 0 else 0. for task in tasks] points = self.get_estimates(avg_pts, std_pts, stories) dev_capacity = self.get_pairs(bandwidth, std_dev_ct, num_pairs) command = 'simulate -s %d -a %.1f -d %.1f -p %d -b %.1f -v %.1f -c %d' \ % (stories, average, std, num_pairs, bandwidth, std_dev_ct, count) simulations[sim] = {'runs': {}} simulations[sim]['command'] = command simulations[sim]['runs'][0] = {} simulations[sim]['runs'][0]['tasks'] = copy.copy(tasks) pairs = dict((k, round(v, 1)) for (k, v) in zip(xrange(num_pairs), dev_capacity)) simulations[sim]['runs'][0]['pairs'] = copy.copy(pairs) capacity = sum(pairs.values()) count = 1 for estimate in points: story = Story('SIM-X%d' % count) story.id = count story.points = int(estimate) story.title = 'Story %d for simulation %d' % (count, sim) story.fix_versions = PersistentList() story.fix_versions.append('SIM-%d' % sim) story.history = History() ct = numpy.random.gumbel(loc=average, scale=std) + 1 months = int((ct / 30) ) + 3 # TODO: Fix this days = int(ct % 30) + 1 started = datetime.datetime(2013, 3, 1, 12, 0, 0) resolved = datetime.datetime(2013, months, days, 12, 0, 0) story.history.data.append((started, 1, 3, 'Sim PO 1')) story.history.data.append((resolved, 3, 6, 'Sim Dev 1')) story.status = 6 story.created = datetime.datetime.now() story.type = '72' story.assignee = None story.developer = None story.scrum_team = 'Sim Team %d' % int(random.random() * teams) story.project = 'SIMS' transaction.begin() release[story.key] = story transaction.commit() transaction.begin() docid = dao.Jira.cache.document_map.add( ['sim', story.project, story.fix_versions[0], story.key]) dao.Jira.cache.catalog.index_doc(docid, story) transaction.commit() count += 1 for day in xrange(20): fail = False tasked = False index = 0 for task in sorted(tasks, reverse=True): missed = 0 for pair in pairs.keys(): if pairs[pair] >= task: pairs[pair] -= task tasked = True break if not tasked: missed = task fail = True break tasked = False return release.version
class Command(BaseCommand): help = 'Report on the current release' usage = 'report [team] [-d developer]' options_help = ''' -d : report on a specific developer''' exmamples = ''' report report App''' def run(self, jira, args): parser = argparse.ArgumentParser() parser.add_argument('team', nargs='?') parser.add_argument('-d', nargs='*', required=False) parser.add_argument('-f', action='store_true', required=False) try: args = parser.parse_args(args) except: return self.release = jira.cache.get_by_path(jira.cache.cwd) if not isinstance(self.release, Release): print 'Error: Must navigate to a release. (hint: help cd)' return if args.team: stories = [s for s in self.release.values() if s.scrum_team and s.scrum_team[:len(args.team)] == args.team] self.release = Release() for story in stories: self.release.add_story(story) if args.d: stories = [s for s in self.release.values() if s.developer and s.developer[:len(args.d[0])] == args.d[0]] self.release = Release() for story in stories: self.release.add_story(story) if not self.release.keys(): print 'No data to report' return release = self.release kanban = release.kanban() kanban.average_cycle_times_by_type() smallest = 'None' if release.sort_by_size(): smallest = release.sort_by_size()[-1].points largest = 'None' if release.sort_by_size(): largest = release.sort_by_size()[0].points print 'Points in scope :', round(release.total_points(), 1) print 'Points completed :', round(release.points_completed(), 1) print 'Total WIP :', release.wip() print 'Stories :', release.total_stories() print ' Avg Points :', round(release.average_story_size(), 1) print ' Std Dev Points :', round(release.std_story_size(), 1) print ' Smallest points:', smallest print ' Largest points :', largest print ' Story WIP :', release.stories_in_process() print ' Avg Cycle Time :', kanban.average_cycle_time() print ' Std Cycle Time :', kanban.stdev_cycle_time() print ' Skew :', release.skew_cycle_time() print ' m Cycle Time :', kanban.median_cycle_time() print ' Total Variance :', kanban.variance_cycle_time() print ' Total Dev CT :', release.aggregate_developer_cycle_time() print ' Avg Dev CT :', release.average_developer_cycle_time() print ' Std Dev CT :', release.stdev_developer_cycle_time() print 'Bugs :', len(release.bugs()) print ' Production :', len(release.stories(type=['78'])) print ' Closed :', len(release.resolved_stories(['78'])) print ' Avg Cycle Time :', kanban.average_lead_time(type=['78']) print ' m Cycle Time :', kanban.median_lead_time(type=['78']) print ' Std Cycle Time :', kanban.stdev_lead_time(type=['78']) print ' Development :', len(release.stories(type=['1'])) print ' Closed :', len(release.resolved_stories(['1'])) print ' Avg Cycle Time :', kanban.average_lead_time(type=['1']) print ' m Cycle Time :', kanban.median_lead_time(type=['1']) print ' Std Cycle Time :', kanban.stdev_lead_time(type=['1']) print print 'WIP by Status:' wip = release.wip_by_status() print 'Status:', ' WIP:' for key in wip: print humanize(int(key)).ljust(16), ':', \ str(wip[key]['wip']).ljust(6) print print 'Total Cycle Times by Status (days):' cycle_times_in_status = kanban.cycle_times_in_status() total = 0 cycle_times = [] for status in cycle_times_in_status.keys(): if status in (1, 10089, 6): # ignore 'open' and 'ready' continue total += cycle_times_in_status[status] cycle_times.append((str(status), cycle_times_in_status[status])) cycle_times.sort(key=lambda x:x[1], reverse=True) for cycle_time in cycle_times: print humanize(int(cycle_time[0])).ljust(5), ':', \ str(cycle_time[1]).ljust(4), \ '%' + str(round(cycle_time[1]/float(total), 2) * 100) print 'Total :', total print 'Max PCE : %' + str(kanban.process_cycle_efficiency()) print print 'Average Takt Time in Status (days):' print ' Status:', 'Average:', 'Stdev:' averages = kanban.average_times_in_status() stds = kanban.std_times_in_status() for key in averages: print ' ', humanize(key).ljust(7), str(averages[key]).ljust(8), stds[key] print print 'Arrival Times for Status (days):' print ' Status:', 'Average:', 'Stdev:' averages = kanban.average_arrival_for_status() stds = kanban.std_arrival_for_status() for key in averages: print ' ', humanize(key).ljust(7), str(averages[key]).ljust(8), stds[key] print print 'State Transition Probabilities:' states = kanban.state_transition_probabilities() print 'Status: ', 'Stories:', 'Days:', 'Avg:', 'Std:' for state in states: print humanize(state) + ':' for exit in states[state]: print ' ', humanize(exit).ljust(5), str(len(states[state][exit]['days'])).ljust(8), str(sum(states[state][exit]['days'])).ljust(5), str(states[state][exit]['average']).ljust(5), states[state][exit]['std'] intervals = kanban.all_state_arrival_intervals() print print 'Arrival Intervals:' for state in intervals: print humanize(state).ljust(5), \ str(intervals[state]['average']).ljust(7), \ intervals[state]['std'] bugs = kanban.average_cycle_times_by_bug_count(type=['7']) print print 'Cycle Times By Bug Count:' print 'Bugs:', 'CT:' for bug in bugs: print str(bug).ljust(6), bugs[bug] print print 'Root Causes:' causes = {} for story in release.bugs(): if story.root_cause not in causes: causes[story.root_cause] = [story.key] else: causes[story.root_cause].append(story.key)
def __init__(self, key, artistKey, title, date = None, tracksCount = None): Release.__init__(self, title, date, tracksCount) self._key = key self._artistKey = artistKey
def __init__(self, key, artistKey, title, date=None, tracksCount=None): Release.__init__(self, title, date, tracksCount) self._key = key self._artistKey = artistKey
def make_story(self, key, data, links=True): if not data['fields']['fixVersions']: return None transaction.begin() story = Story(key) story.id = int(data['id']) story.history = History(data['changelog']) story.url = data['self'] story.title = data['fields']['summary'] story.fix_versions = PersistentList() for version in data['fields']['fixVersions']: story.fix_versions.append(version['name']) story.fix_version = data['fields']['fixVersions'] story.created = datetime.datetime.fromtimestamp(time.mktime( time.strptime(data['fields']['created'][:23], '%Y-%m-%dT%H:%M:%S.%f'))) story.type = data['fields']['issuetype']['id'] story.assignee = data['fields']['assignee'] story.developer = data['fields']['customfield_13435'] story.rank = data['fields']['customfield_12242'] if 'customfield_10722' in data['fields'] and data['fields'][ 'customfield_10722']: story.root_cause = data['fields']['customfield_10722'][ 'value'].strip() else: story.root_cause = '' if 'customfield_13330' in data['fields'] and data['fields'][ 'customfield_13330']: story.root_cause_details = data['fields']['customfield_13330'] else: story.root_cause_details = '' story.scrum_team = None if data['fields'].has_key('customfield_11261'): if data['fields']['customfield_11261']: story.scrum_team = data['fields']['customfield_11261'][ 'value'].strip() else: story.scrum_team = None story.points = None if data['fields'].has_key('customfield_10792'): story.points = data['fields']['customfield_10792'] if story.points: story.points = int(story.points) story.status = int(data['fields']['status']['id']) story.project = data['fields']['project']['key'] if not story.project in self.cache.data: self.cache.data[story.project] = Project(story.project, data['fields']['project']['name']) project = self.cache.data[story.project] for version in story.fix_versions: if version in project.keys(): if not project[version].has_key(story.key): project[version][story.key] = story else: release = Release() release.version = version release[story.key] = story project[version] = release transaction.commit() transaction.begin() docid = self.cache.document_map.add( ['jira', story.project, story.fix_versions[0], story.key]) self.cache.catalog.index_doc(docid, story) transaction.commit() if not links: return story transaction.begin() for link in data['fields']['issuelinks']: if link.has_key('outwardIssue'): type = link['type']['name'] key = link['outwardIssue']['key'] if not type in story['links']['out'].keys(): story['links']['out'][type] = Folder() story['links']['out'][type].key = type transaction.commit() s = self.get_story(key) if not s: continue story['links']['out'][type][key] = s else: if not key in story['links']['out'][type].keys(): s = self.get_story(key) if not s: continue story['links']['out'][type][key] = s elif link.has_key('inwardIssue'): type = link['type']['name'] key = link['inwardIssue']['key'] if not type in story['links']['in'].keys(): story['links']['in'][type] = Folder() story['links']['in'][type].key = type transaction.commit() s = self.get_story(key) if not s: continue story['links']['in'][type][key] = s else: if not key in story['links']['in'][type].keys(): s = self.get_story(key) if not s: continue story['links']['in'][type][key] = s transaction.commit() return story
def __init__(self, title, date = None, tracksCount = None): Release.__init__(self, title, date, tracksCount) self._keys = {}