Ejemplo n.º 1
0
 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)
Ejemplo n.º 2
0
 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)
Ejemplo n.º 3
0
    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"'
Ejemplo n.º 4
0
    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]
Ejemplo n.º 5
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)
Ejemplo n.º 6
0
    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
Ejemplo n.º 7
0
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)
Ejemplo n.º 8
0
 def __init__(self, key, artistKey, title, date = None, tracksCount = None):
     Release.__init__(self, title, date, tracksCount)
     self._key = key
     self._artistKey = artistKey
Ejemplo n.º 9
0
 def __init__(self, key, artistKey, title, date=None, tracksCount=None):
     Release.__init__(self, title, date, tracksCount)
     self._key = key
     self._artistKey = artistKey
Ejemplo n.º 10
0
 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
Ejemplo n.º 11
0
 def __init__(self, title, date = None, tracksCount = None):
     Release.__init__(self, title, date, tracksCount)
     self._keys = {}