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)