def report_types(group="all", months=3, start=None): start = start or datetime.datetime.today() months_ranges = month_ranges(start, months) rg = ReportGroup(group, Kard.objects) rg_cards = rg.queryset types = list(rg_cards.distinct('_type')) types.sort() datatable = { 'headers': ('Month', 'Type', 'Throughput', 'Cycle Time', 'Lead Time'), 'rows': [], } months = [] for arange in months_ranges: for typ in types: row = [] start, end = arange filtered_cards = Kard.objects.filter(done_date__gte=start, done_date__lte=end, _type=typ) rg = ReportGroup(group, filtered_cards) cards = rg.queryset if cards.count() > 0: month_name = start.strftime("%B") if month_name not in months: row.append(month_name) months.append(month_name) else: row.append('') row.append(typ) row.append(cards.count()) row.append("%d" % cards.average('_cycle_time')) row.append("%d" % cards.average('_lead_time')) if row: row = tuple(row) datatable['rows'].append(row) context = { 'title': "By type", 'updated_at': datetime.datetime.now(), 'datatable': datatable, 'version': VERSION, } return render_template('report-types.html', **context)
def value_txt_report(year_number, month_number, group="all"): start_date = munge_date(year=year_number, month=month_number, day=1) start_date = make_start_date(date=start_date) start_date, end_date = month_range(start_date) rg = ReportGroup(group, Kard.objects.done()) done = rg.queryset cards = done.filter(done_date__gte=start_date, done_date__lte=end_date).order_by('-done_date') cards = [c for c in cards if c.is_card] context = { 'title': "Completed Value Cards", 'cards': cards, 'start_date': start_date, 'end_date': end_date, 'updated_at': datetime.datetime.now(), 'version': VERSION, } response = make_response(render_template('done-report.txt', **context)) response.headers['Content-Type'] = "text/plain" return response
def report_assignee(group="all"): states = States() states_of_interest = [s for s in states if s not in (states.backlog, states.done)] # ReportGroup of WIP rg = ReportGroup(group, Kard.objects.filter(state__in=states_of_interest)) distro = {} for k in rg.queryset.all(): assignee = k._assignee or "Unassigned" distro.setdefault(assignee, 0) distro[assignee] += 1 total = 0 total = float(sum(distro.values())) distro = distro.items() distro.sort(key=lambda x: x[1]) distro.reverse() percentages = [(x[0], (x[1] / total)) for x in distro] percentages = [(x[0], round(x[1], 2)) for x in percentages] chart = {} chart['data'] = percentages context = { 'data': distro, 'chart': chart, 'total': total, 'title': "Assignee breakdown", 'updated_at': datetime.datetime.now(), 'version': VERSION, } return render_template('report-assignee.html', **context)
def std_dev(start, stop): start = make_start_date(date=start) stop = make_end_date(date=stop) rg = ReportGroup('dev', Kard.objects.filter(done_date__gte=start, done_date__lte=stop)) cards = [c for c in rg.queryset if c.is_card] cycle_times = [c.cycle_time for c in cards] over_21 = [c for c in cycle_times if c > 21] over_21_pct = float(len(over_21)) / len(cycle_times) print "%s -- %s" % (start, stop) print "\t Sample: %s" % len(cards) print "\t Over 21: %s / %s" % (len(over_21), over_21_pct * 100) print "\t Ave: %s" % average(cycle_times) print "\t Stdev: %s" % standard_deviation(cycle_times) ct, pct = percentile(.8, cycle_times) print "\t 80pct: %s / %s" % (ct, pct * 100) cards_by_class = {} for c in cards: cards_by_class.setdefault(c.service_class['name'], []) cards_by_class[c.service_class['name']].append(c) for sclass, cards in cards_by_class.items(): cycle_times = [c.cycle_time for c in cards] print "\t ## %s" % (sclass) print "\t\t Sample: %s" % len(cards) print "\t\t Ave: %s" % average(cycle_times) print "\t\t Stdev: %s" % standard_deviation(cycle_times)
def calculate(cls, start_date, end_date, group="all"): from kardboard.models import Kard from kardboard.models import ReportGroup start_date = make_start_date(date=start_date) end_date = make_end_date(date=end_date) try: record = cls.objects.get( group=group, start_date=start_date, end_date=end_date, ) except cls.DoesNotExist: record = cls() record.start_date = start_date record.end_date = end_date record.group = group record.data = {} kards = ReportGroup(group, Kard.objects.filter( done_date__gte=start_date, done_date__lte=end_date, ) ) record.data = report_on_cards(list(kards.queryset)) record.save() return record
def find_value_cards(year): start = make_start_date(year, 1, 1) end = make_end_date(year, 12, 31) query = Kard.objects.filter(done_date__gte=start, done_date__lte=end) rg = ReportGroup('dev', query) cards = [c for c in rg.queryset if c.is_card] return cards
def report_throughput(group="all", months=3, start=None): start = start or datetime.datetime.today() months_ranges = month_ranges(start, months) defect_types = app.config.get('DEFECT_TYPES', None) with_defects = defect_types is not None month_counts = [] for arange in months_ranges: start, end = arange filtered_cards = Kard.objects.filter(done_date__gte=start, done_date__lte=end) rg = ReportGroup(group, filtered_cards) cards = rg.queryset if with_defects: counts = {'card': 0, 'defect': 0} for card in cards: if card.type.strip() in defect_types: counts['defect'] += 1 else: counts['card'] += 1 month_counts.append((start.strftime("%B"), counts)) else: num = cards.count() month_counts.append((start.strftime("%B"), num)) chart = {} chart['categories'] = [c[0] for c in month_counts] if with_defects: chart['series'] = [ { 'data': [c[1]['card'] for c in month_counts], 'name': 'Cards' }, { 'data': [c[1]['defect'] for c in month_counts], 'name': 'Defects' } ] else: chart['series'] = [{ 'data': [c[1] for c in month_counts], 'name': 'Cards', }] context = { 'title': "How much have we done?", 'updated_at': datetime.datetime.now(), 'chart': chart, 'month_counts': month_counts, 'version': VERSION, 'with_defects': with_defects, } return render_template('report-throughput.html', **context)
def daily_throughput_average(report_group_slug, stop, weeks=4): start = (stop - relativedelta(days=weeks * 7)) + relativedelta(days=1) start = make_start_date(date=start) query = Kard.objects.filter( done_date__gte=start, done_date__lte=stop,) kards = list(ReportGroup(report_group_slug, query).queryset) return len(kards) / float(weeks * 7)
def report_leaderboard(group="all", months=3, person=None, start_month=None, start_year=None): start = datetime.datetime.today() if start_month and start_year: start = start.replace(month=start_month, year=start_year) months_ranges = month_ranges(start, months) start = months_ranges[0][0] end = months_ranges[-1][-1] rg = ReportGroup(group, Kard.objects.done()) done = rg.queryset cards = done.filter(done_date__gte=start, done_date__lte=end) people = {} for card in cards: try: devs = card.ticket_system_data['developers'] for d in devs: p = people.get(d, PersonCardSet(d)) p.add_card(card) people[d] = p except KeyError: pass if person: person = people.get(person, None) people = [] if not person: abort(404) else: people = people.values() people.sort(reverse=True) context = { 'people': people, 'person': person, 'months': months, 'group': group, 'start': start, 'end': end, 'start_month': start_month, 'start_year': start_year, 'title': "Developer Leaderboard", 'updated_at': datetime.datetime.now(), 'version': VERSION, } if person: context['title'] = "%s: %s" % (person.name, context['title']) return render_template('leaderboard.html', **context)
def calculate(cls, group="all"): from kardboard.models import Kard from kardboard.models import ReportGroup try: record = cls.objects.get( group=group, ) except cls.DoesNotExist: record = cls() record.group = group record.data = {} kards = ReportGroup(group, Kard.in_progress()) record.data = report_on_cards(list(kards.queryset)) record.save() return record
def moving_data(report_group_slug, start, stop): query = Kard.objects.filter( done_date__gte=start, done_date__lte=stop, ) kards = list(ReportGroup(report_group_slug, query).queryset) bad_kards = [k for k in kards if k.cycle_time is None] print "Bad cards" print "*" * 10 print[k.key for k in bad_kards] features = [k for k in kards if k.is_card] defects = [k for k in kards if not k.is_card] over_sla = [k for k in kards if k.cycle_time > k.service_class['upper']] card_cycle_ave = average([k.cycle_time for k in kards]) or 0 card_stddev = standard_deviation([k.cycle_time for k in kards]) or 0 wip = find_wip(report_group_slug, stop) tpa = daily_throughput_average(report_group_slug, stop) try: little_law = int(round(wip / float(tpa))) except: little_law = "" cycle_time_ave = find_cycle_time_ave(report_group_slug, stop) data = { 'start': start, 'stop': stop, 'features': len(features), 'bugfixes': len(defects), 'little_law': little_law, 'cycle_time_ave': int(round(cycle_time_ave)), 'wip': wip, 'cycle_average': int(round(card_cycle_ave)), 'stddev': int(round(card_stddev)), 'over_sla': len(over_sla), } return data
def done(group="all", months=3, start=None): start = start or datetime.datetime.today() months_ranges = month_ranges(start, months) start = months_ranges[0][0] end = months_ranges[-1][-1] rg = ReportGroup(group, Kard.objects.done()) done = rg.queryset cards = done.filter(done_date__gte=start, done_date__lte=end).order_by('-done_date') context = { 'title': "Completed Cards", 'cards': cards, 'updated_at': datetime.datetime.now(), 'version': VERSION, } return render_template('done.html', **context)
def blocked(group="all", months=3, start=None): start = start or datetime.datetime.today() months_ranges = month_ranges(start, months) start = months_ranges[0][0] end = months_ranges[-1][-1] rg = ReportGroup(group, Kard.objects()) blocked_cards = rg.queryset blocked_cards = blocked_cards.filter( start_date__gte=start, start_date__lte=end, blocked_ever=True).order_by('-start_date') context = { 'title': "Blocked", 'cards': blocked_cards, 'start': start, 'end': end, 'updated_at': datetime.datetime.now(), 'version': VERSION, } return render_template('blocked.html', **context)
def report_cycle_distribution(group="all", months=3, limit=None): from kardboard.services.reports import CycleTimeDistribution defects_only, cards_only = False, False if limit == 'cards': cards_only = True if limit == 'defects': defects_only = True today = datetime.datetime.today() start_day = today - relativedelta.relativedelta(months=months) start_day = make_start_date(date=start_day) end_day = make_end_date(date=today) context = { 'title': "How quick can we do it?", 'updated_at': datetime.datetime.now(), 'version': VERSION, } query = Q(done_date__gte=start_day) & Q(done_date__lte=end_day) if defects_only: query = query & Q(_type__in=app.config.get('DEFECT_TYPES', [])) elif cards_only: query = query & Q(_type__nin=app.config.get('DEFECT_TYPES', [])) rg = ReportGroup(group, Kard.objects.filter(query)) cards = list(rg.queryset) total = len(cards) if total == 0: context = { 'error': "Zero cards were completed in the past %s months" % months, } return render_template('report-cycle-distro.html', **context) cdr = CycleTimeDistribution(cards=cards) chart = {} chart['categories'] = cdr.days() chart['series'] = [] service_class_series = cdr.service_class_series() sclasses = service_class_series.keys() sclasses.sort() for sclass in sclasses: seri = service_class_series[sclass] chart['series'].append(dict(name=sclass, data=seri)) context = { 'histogram_data': cdr.histogram(), 'chart': chart, 'title': "How quick can we do it?", 'months': months, 'total': total, 'updated_at': datetime.datetime.now(), 'version': VERSION, } if defects_only: context['title'] = "Defects: %s" % (context['title']) context['card_type'] = 'defects' elif cards_only: context['title'] = "Cards: %s" % (context['title']) context['card_type'] = 'cards' else: context['title'] = "All: %s" % (context['title']) context['card_type'] = 'cards and defects' return render_template('report-cycle-distro.html', **context)
def report_cycle_distribution(group="all", months=3, limit=None): defects_only, cards_only = False, False if limit == 'cards': cards_only = True if limit == 'defects': defects_only = True ranges = ( (0, 4, "Less than 5 days"), (5, 10, "5-10 days"), (11, 15, "11-15 days"), (16, 20, "16-20 days"), (21, 25, "21-25 days"), ( 26, 30, "26-30 days", ), (31, 9999, "> 30 days"), ) today = datetime.datetime.today() start_day = today - relativedelta.relativedelta(months=months) start_day = make_start_date(date=start_day) end_day = make_end_date(date=today) context = { 'title': "How quick can we do it?", 'updated_at': datetime.datetime.now(), 'version': VERSION, } query = Q(done_date__gte=start_day) & Q(done_date__lte=end_day) if defects_only: query = query & Q(_type__in=app.config.get('DEFECT_TYPES', [])) elif cards_only: query = query & Q(_type__nin=app.config.get('DEFECT_TYPES', [])) rg = ReportGroup(group, Kard.objects.filter(query)) total = rg.queryset.count() if total == 0: context = { 'error': "Zero cards were completed in the past %s months" % months } return render_template('report-cycle-distro.html', **context) distro = [] for row in ranges: lower, upper, label = row query = Q(done_date__gte=start_day) & Q(done_date__lte=end_day) & \ Q(_cycle_time__gte=lower) & Q(_cycle_time__lte=upper) if defects_only: query = query & Q(_type__in=app.config.get('DEFECT_TYPES', [])) else: query = query & Q(_type__nin=app.config.get('DEFECT_TYPES', [])) pct = ReportGroup( group, Kard.objects.filter(query)).queryset.count() / float(total) pct = round(pct, 2) distro.append((label, pct)) chart = {} chart['data'] = distro context = { 'data': distro, 'chart': chart, 'title': "How quick can we do it?", 'months': months, 'total': total, 'updated_at': datetime.datetime.now(), 'version': VERSION, } if defects_only: context['title'] = "Defects: %s" % (context['title']) context['card_type'] = 'defects' elif cards_only: context['title'] = "Cards: %s" % (context['title']) context['card_type'] = 'cards' else: context['title'] = "All: %s" % (context['title']) context['card_type'] = 'cards and defects' return render_template('report-cycle-distro.html', **context)