Esempio n. 1
0
def find_cycle_time_ave(report_group_slug, stop):
    try:
        dr = DailyRecord.objects.get(date=make_end_date(date=stop), group=report_group_slug)
    except DailyRecord.DoesNotExist:
        print "No Daily Record: %s %s" % (report_group_slug, make_end_date(date=stop))
        raise

    return dr.moving_cycle_time
Esempio n. 2
0
def state_transition_counts(state, months, count_type="exit", raw=False):
    end = make_end_date(date=datetime.datetime.now())

    start = end - relativedelta(months=months)
    start = make_start_date(date=start)

    counts = []
    data = []

    current_date = start
    while current_date <= end:
        if current_date.weekday() == 5:  # Saturday
            current_date += relativedelta(days=2)
        elif current_date.weekday() == 6:  # Sunday
            current_date += relativedelta(days=1)

        range_start = make_start_date(date=current_date)
        range_end = make_end_date(date=current_date)

        if count_type == "exit":
            kwargs = dict(
                exited__gte=range_start,
                exited__lte=range_end,
            )
        elif count_type == "enter":
            kwargs = dict(
                entered__gte=range_start,
                entered__lte=range_end,
            )

        count = StateLog.objects.filter(
            state=state,
            **kwargs
        ).count()

        data.append((current_date, count))
        counts.append(count)
        current_date = current_date + relativedelta(days=1)

    counts.sort()

    print "%s\t%s" % (start, end)
    print "Median\t%s" % median(counts)
    print "Average\t%s" % average(counts)
    print "Min\t%s" % counts[0]
    print "Max\t%s" % counts[-1]

    hist = histogram(counts)
    keys = hist.keys()
    keys.sort()
    for k in keys:
        print "%s\t%s" % (k, hist[k])

    if raw is True:
        for date, count in data:
            print "%s\t%s" % (date, count)
Esempio n. 3
0
def _hit_due_date(card):
    if card.due_date is None:
        return ''

    due_date = make_end_date(date=card.due_date)
    if card.done_date is None:
        done_date = make_end_date(date=now())
    else:
        done_date = make_end_date(date=card.done_date)
    return done_date <= due_date
Esempio n. 4
0
def _hit_due_date(card):
    if card.due_date is None:
        return ''

    due_date = make_end_date(date=card.due_date)
    if card.done_date is None:
        done_date = make_end_date(date=now())
    else:
        done_date = make_end_date(date=card.done_date)
    return done_date <= due_date
Esempio n. 5
0
def find_cycle_time_ave(report_group_slug, stop):
    try:
        dr = DailyRecord.objects.get(
            date=make_end_date(date=stop),
            group=report_group_slug,
        )
    except DailyRecord.DoesNotExist:
        print "No Daily Record: %s %s" % (report_group_slug, make_end_date(date=stop))
        raise

    return dr.moving_cycle_time
Esempio n. 6
0
def state_transition_counts(state, months, count_type="exit", raw=False):
    end = make_end_date(date=datetime.datetime.now())

    start = end - relativedelta(months=months)
    start = make_start_date(date=start)

    counts = []
    data = []

    current_date = start
    while current_date <= end:
        if current_date.weekday() == 5:  # Saturday
            current_date += relativedelta(days=2)
        elif current_date.weekday() == 6:  # Sunday
            current_date += relativedelta(days=1)

        range_start = make_start_date(date=current_date)
        range_end = make_end_date(date=current_date)

        if count_type == "exit":
            kwargs = dict(
                exited__gte=range_start,
                exited__lte=range_end,
            )
        elif count_type == "enter":
            kwargs = dict(
                entered__gte=range_start,
                entered__lte=range_end,
            )

        count = StateLog.objects.filter(state=state, **kwargs).count()

        data.append((current_date, count))
        counts.append(count)
        current_date = current_date + relativedelta(days=1)

    counts.sort()

    print "%s\t%s" % (start, end)
    print "Median\t%s" % median(counts)
    print "Average\t%s" % average(counts)
    print "Min\t%s" % counts[0]
    print "Max\t%s" % counts[-1]

    hist = histogram(counts)
    keys = hist.keys()
    keys.sort()
    for k in keys:
        print "%s\t%s" % (k, hist[k])

    if raw is True:
        for date, count in data:
            print "%s\t%s" % (date, count)
Esempio n. 7
0
def find_cycle_time_ave(stop):
    dr = DailyRecord.objects.get(
        date=make_end_date(date=stop),
        group='dev',
    )

    return dr.moving_cycle_time
    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 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(
                start_date__gte=start_date,
                start_date__lte=end_date,
            )
        )
        record.data = report_on_cards(kards)
        record.save()
        return record
Esempio n. 10
0
def weekly_moving_data(report_group_slug, start):
    start = parse_date(start)
    stop = start + relativedelta(days=6)

    start = make_start_date(date=start)
    stop = make_end_date(date=stop)
    return moving_data(report_group_slug, start, stop)
Esempio n. 11
0
    def moving_median_abs_dev(self, year=None, month=None, day=None, weeks=4):
        """
        The moving median absolute deviation of cycle time for every day in the last N weeks.
        See http://en.wikipedia.org/wiki/Median_absolute_deviation
        """

        end_date = make_end_date(year, month, day)
        start_date = end_date - relativedelta(weeks=weeks)
        start_date = make_start_date(date=start_date)

        qs = self.done().filter(
            done_date__lte=end_date,
            done_date__gte=start_date,
        ).scalar('_cycle_time')

        cycle_times = [t for t in qs if t is not None]
        median_cycle_time = median(cycle_times)

        if median_cycle_time is not None:
            absolute_deviations = [
                math.fabs(median_cycle_time - c) for c in cycle_times
            ]
            mad = median(absolute_deviations)
        else:
            mad = None

        if mad is None:
            mad = 0

        return int(round(mad))
    def test_regress_service_class_miscalc(self):
        from kardboard.util import make_start_date, make_end_date

        c = self.make_card(
            key="JANUS-234",
            title="MMF: Database visualization (Iteration 1)",
            backlog_date=datetime(2013, 2, 14),
            start_date=datetime(2013, 5, 30),
            done_date=datetime(2013, 8, 1),
            _service_class="Normal",
            state="Done",
            team="Team Venture",
        )
        c.save()

        Record = self._get_target_class()
        start_date = make_start_date(date=datetime(2013, 8, 1))
        end_date = make_end_date(date=datetime(2013, 8, 31))
        Record.calculate(start_date, end_date, group="team-venture")

        r = Record.objects.get(
            start_date=start_date,
            end_date=end_date,
            group="team-venture",
        )
        actual = r.data

        assert actual['Normal']['wip'] == 1
Esempio n. 13
0
def state():
    date = datetime.datetime.now()
    date = make_end_date(date=date)
    states = States()

    board = DisplayBoard(backlog_limit=0)  # defaults to all teams, 7 days of done
    board.cards  # force the card calculation
    board.rows

    title = app.config.get("SITE_NAME")

    metrics = []

    teams = teams_service.setup_teams(app.config)

    context = {
        "title": title,
        "board": board,
        "states": states,
        "metrics": metrics,
        "wip_limits": {},
        "date": date,
        "teams": teams,
        "updated_at": datetime.datetime.now(),
        "version": VERSION,
    }
    return render_template("state.html", **context)
Esempio n. 14
0
def team(team_slug=None):
    date = datetime.datetime.now()
    date = make_end_date(date=date)
    teams = app.config.get("CARD_TEAMS", [])

    team_mapping = {}
    for team in teams:
        team_mapping[slugify(team)] = team

    target_team = None
    if team_slug:
        target_team = team_mapping.get(team_slug, None)
        if not team:
            abort(404)

    board = DisplayBoard(teams=[target_team])

    metrics = [
        {
            "Ave. Cycle Time": Kard.objects.filter(team=target_team).moving_cycle_time(
                year=date.year, month=date.month, day=date.day
            )
        },
        {
            "Done this week": Kard.objects.filter(team=target_team)
            .done_in_week(year=date.year, month=date.month, day=date.day)
            .count()
        },
        {
            "Done this month": Kard.objects.filter(team=target_team)
            .done_in_month(year=date.year, month=date.month, day=date.day)
            .count()
        },
        {"On the board": len(board.cards)},
    ]

    done_cards = Kard.objects.done().filter(team=target_team).order_by("-done_date")

    title = "%s cards" % target_team

    report_config = (
        {"slug": "cycle", "name": "Cycle time"},
        {"slug": "classes", "name": "Throughput"},
        {"slug": "leaderboard", "name": "Leaderboard"},
    )

    context = {
        "title": title,
        "team_slug": team_slug,
        "target_team": target_team,
        "done_cards": done_cards,
        "metrics": metrics,
        "report_config": report_config,
        "board": board,
        "date": date,
        "updated_at": datetime.datetime.now(),
        "version": VERSION,
    }

    return render_template("team.html", **context)
Esempio n. 15
0
def dashboard(year=None, month=None, day=None):
    date = kardboard.util.now()
    now = kardboard.util.now()
    scope = "current"

    if year:
        date = date.replace(year=year)
        scope = "year"
    if month:
        date = date.replace(month=month)
        scope = "month"
        start, end = month_range(date)
        date = end
    if day:
        date = date.replace(day=day)
        scope = "day"

    date = make_end_date(date=date)

    wip_cards = list(Kard.in_progress(date))
    wip_cards = sorted(wip_cards, key=lambda c: c.current_cycle_time(date))
    wip_cards.reverse()

    backlog_cards = Kard.backlogged(date).order_by("key")

    metrics = [
        {"Ave. Cycle Time": Kard.objects.moving_cycle_time(year=date.year, month=date.month, day=date.day)},
        {"Done this week": Kard.objects.done_in_week(year=date.year, month=date.month, day=date.day).count()},
        {"Done this month": Kard.objects.done_in_month(year=date.year, month=date.month, day=date.day).count()},
        {"On the board": len(wip_cards) + backlog_cards.count()},
    ]

    title = "Dashboard"
    if scope == "year":
        title += " for %s"
    if scope == "month":
        title += " for %s/%s" % (date.month, date.year)
    if scope == "day" or scope == "current":
        title += " for %s/%s/%s" % (date.month, date.day, date.year)

    forward_date = date + relativedelta.relativedelta(days=1)
    back_date = date - relativedelta.relativedelta(days=1)

    if forward_date > now:
        forward_date = None

    context = {
        "forward_date": forward_date,
        "back_date": back_date,
        "scope": scope,
        "date": date,
        "title": title,
        "metrics": metrics,
        "wip_cards": wip_cards,
        "backlog_cards": backlog_cards,
        "updated_at": now,
        "version": VERSION,
    }

    return render_template("dashboard.html", **context)
Esempio n. 16
0
def state():
    date = datetime.datetime.now()
    date = make_end_date(date=date)
    states = States()

    board = DisplayBoard()  # defaults to all teams, 7 days of done
    board.cards  # force the card calculation
    board.rows

    title = app.config.get("SITE_NAME")

    wip_cards = [k for k in board.cards if k.state in states.in_progress]
    done_this_week = [k for k in board.cards if k.state == states.done]

    blocked = [k for k in wip_cards if k.blocked]

    metrics = [{"WIP": len(wip_cards)}, {"Blocked": len(blocked)}, {"Done this week": len(done_this_week)}]

    context = {
        "title": title,
        "board": board,
        "states": states,
        "metrics": metrics,
        "date": date,
        "updated_at": datetime.datetime.now(),
        "version": VERSION,
    }
    return render_template("team.html", **context)
Esempio n. 17
0
def state():
    date = datetime.datetime.now()
    date = make_end_date(date=date)
    states = States()

    board = DisplayBoard(backlog_limit=0)  # defaults to all teams, 7 days of done
    board.cards  # force the card calculation
    board.rows

    title = app.config.get('SITE_NAME')

    metrics = []

    teams = teams_service.setup_teams(
        app.config
    )

    context = {
        'title': title,
        'board': board,
        'states': states,
        'metrics': metrics,
        'wip_limits': {},
        'date': date,
        'teams': teams,
        'updated_at': datetime.datetime.now(),
        'version': VERSION,
    }
    return render_template('state.html', **context)
Esempio n. 18
0
def report_detailed_flow(group="all", months=3, cards_only=False):
    end = kardboard.util.now()
    months_ranges = month_ranges(end, months)

    start_day = make_start_date(date=months_ranges[0][0])
    end_day = make_end_date(date=end)

    if cards_only:
        only_arg = ('state_card_counts', 'date', 'group')
    else:
        only_arg = ('state_counts', 'date', 'group')

    reports = FlowReport.objects.filter(
        date__gte=start_day,
        date__lte=end_day,
        group=group).only(*only_arg)
    if not reports:
        abort(404)

    chart = {}
    chart['categories'] = []

    series = []
    for state in States():
        seri = {'name': state, 'data': []}
        series.append(seri)

    done_starting_point = 0
    for report in reports:
        chart['categories'].append(report.date.strftime("%m/%d"))
        for seri in series:
            if cards_only:
                daily_seri_data = report.state_card_counts.get(seri['name'], 0)
            else:
                daily_seri_data = report.state_counts.get(seri['name'], 0)

            if seri['name'] == "Done":
                if len(seri['data']) == 0:
                    done_starting_point = daily_seri_data
                    daily_seri_data = 0
                else:
                    daily_seri_data = daily_seri_data - done_starting_point

            seri['data'].append(daily_seri_data)
    chart['series'] = series

    start_date = reports.order_by('date').first().date
    reports.order_by('-date')
    context = {
        'title': "Detailed Cumulative Flow",
        'reports': reports,
        'months': months,
        'cards_only': cards_only,
        'chart': chart,
        'start_date': start_date,
        'updated_at': reports[0].updated_at,
        'states': States(),
        'version': VERSION,
    }
    return render_template('report-detailed-flow.html', **context)
Esempio n. 19
0
def state():
    date = datetime.datetime.now()
    date = make_end_date(date=date)
    states = States()

    board = DisplayBoard(backlog_limit=0)  # defaults to all teams, 7 days of done
    board.cards  # force the card calculation
    board.rows

    title = app.config.get('SITE_NAME')

    metrics = []

    teams = teams_service.setup_teams(
        app.config
    )

    context = {
        'title': title,
        'board': board,
        'states': states,
        'metrics': metrics,
        'date': date,
        'teams': teams,
        'updated_at': datetime.datetime.now(),
        'version': VERSION,
    }
    return render_template('team.html', **context)
Esempio n. 20
0
def state():
    date = datetime.datetime.now()
    date = make_end_date(date=date)

    board = DisplayBoard()  # defaults to all teams, 7 days of done

    title = app.config.get("SITE_NAME")

    wip_cards = Kard.in_progress(date)
    backlog_cards = Kard.backlogged(date)
    metrics = [
        {"Ave. Cycle Time": Kard.objects.moving_cycle_time(year=date.year, month=date.month, day=date.day)},
        {"Done this week": Kard.objects.done_in_week(year=date.year, month=date.month, day=date.day).count()},
        {"Done this month": Kard.objects.done_in_month(year=date.year, month=date.month, day=date.day).count()},
        {"On the board": wip_cards.count() + backlog_cards.count()},
    ]

    context = {
        "title": title,
        "board": board,
        "states": states,
        "metrics": metrics,
        "date": date,
        "updated_at": datetime.datetime.now(),
        "version": VERSION,
    }

    return render_template("team.html", **context)
Esempio n. 21
0
def chart_flow(group="all", months=3):
    end = kardboard.util.now()
    months_ranges = month_ranges(end, months)

    start_day = make_start_date(date=months_ranges[0][0])
    end_day = make_end_date(date=end)

    records = DailyRecord.objects.filter(
        date__gte=start_day,
        date__lte=end_day,
        group=group)

    chart = CumulativeFlowChart(900, 300)
    chart.add_data([r.backlog_cum for r in records])
    chart.add_data([r.in_progress_cum for r in records])
    chart.add_data([r.done for r in records])
    chart.setup_grid(records)

    records.order_by('-date')
    context = {
        'title': "Cumulative Flow",
        'updated_at': datetime.datetime.now(),
        'chart': chart,
        'flowdata': records,
        'version': VERSION,
    }

    return render_template('chart-flow.html', **context)
    def test_regress_service_class_miscalc(self):
        from kardboard.util import make_start_date, make_end_date

        c = self.make_card(
            key="JANUS-234",
            title="MMF: Database visualization (Iteration 1)",
            backlog_date=datetime(2013, 2, 14),
            start_date=datetime(2013, 5, 30),
            done_date=datetime(2013, 8, 1),
            _service_class="Normal",
            state="Done",
            team="Team Venture",
        )
        c.save()

        Record = self._get_target_class()
        start_date = make_start_date(date=datetime(2013, 8, 1))
        end_date = make_end_date(date=datetime(2013, 8, 31))
        Record.calculate(start_date, end_date, group="team-venture")

        r = Record.objects.get(
            start_date=start_date,
            end_date=end_date,
            group="team-venture",
        )
        actual = r.data

        assert actual['Normal']['wip'] == 1
Esempio n. 23
0
def report_detailed_flow(group="all", months=3, cards_only=False):
    end = kardboard.util.now()
    months_ranges = month_ranges(end, months)

    start_day = make_start_date(date=months_ranges[0][0])
    end_day = make_end_date(date=end)

    if cards_only:
        only_arg = ('state_card_counts', 'date', 'group')
    else:
        only_arg = ('state_counts', 'date', 'group')

    reports = FlowReport.objects.filter(
        date__gte=start_day,
        date__lte=end_day,
        group=group).only(*only_arg)
    if not reports:
        abort(404)

    chart = {}
    chart['categories'] = []

    series = []
    for state in States():
        seri = {'name': state, 'data': []}
        series.append(seri)

    done_starting_point = 0
    for report in reports:
        chart['categories'].append(report.date.strftime("%m/%d"))
        for seri in series:
            if cards_only:
                daily_seri_data = report.state_card_counts.get(seri['name'], 0)
            else:
                daily_seri_data = report.state_counts.get(seri['name'], 0)

            if seri['name'] == "Done":
                if len(seri['data']) == 0:
                    done_starting_point = daily_seri_data
                    daily_seri_data = 0
                else:
                    daily_seri_data = daily_seri_data - done_starting_point

            seri['data'].append(daily_seri_data)
    chart['series'] = series

    start_date = reports.order_by('date').first().date
    reports.order_by('-date')
    context = {
        'title': "Detailed Cumulative Flow",
        'reports': reports,
        'months': months,
        'cards_only': cards_only,
        'chart': chart,
        'start_date': start_date,
        'updated_at': reports[0].updated_at,
        'states': States(),
        'version': VERSION,
    }
    return render_template('report-detailed-flow.html', **context)
Esempio n. 24
0
def state():
    date = datetime.datetime.now()
    date = make_end_date(date=date)

    board = DisplayBoard()  # defaults to all teams, 7 days of done

    title = app.config.get('SITE_NAME')

    wip_cards = Kard.in_progress(date)
    backlog_cards = Kard.backlogged(date)
    metrics = [
        {'Ave. Cycle Time': Kard.objects.moving_cycle_time(
            year=date.year, month=date.month, day=date.day)},
        {'Done this week': Kard.objects.done_in_week(
            year=date.year, month=date.month, day=date.day).count()},
        {'Done this month':
            Kard.objects.done_in_month(
                year=date.year, month=date.month, day=date.day).count()},
        {'On the board': wip_cards.count() + backlog_cards.count()},
    ]

    context = {
        'title': title,
        'board': board,
        'states': states,
        'metrics': metrics,
        'date': date,
        'updated_at': datetime.datetime.now(),
        'version': VERSION,
    }

    return render_template('state.html', **context)
Esempio n. 25
0
def report_flow(group="all", months=3):
    end = kardboard.util.now()
    months_ranges = month_ranges(end, months)

    start_day = make_start_date(date=months_ranges[0][0])
    end_day = make_end_date(date=end)

    records = DailyRecord.objects.filter(date__gte=start_day, date__lte=end_day, group=group)

    chart = {}
    chart["categories"] = [report.date.strftime("%m/%d") for report in records]
    series = [{"name": "Planning", "data": []}, {"name": "Todo", "data": []}, {"name": "Done", "data": []}]
    for row in records:
        series[0]["data"].append(row.backlog)
        series[1]["data"].append(row.in_progress)
        series[2]["data"].append(row.done)
    chart["series"] = series

    start_date = records.order_by("date").first().date
    records.order_by("-date")
    context = {
        "title": "Cumulative Flow",
        "updated_at": datetime.datetime.now(),
        "chart": chart,
        "start_date": start_date,
        "flowdata": records,
        "version": VERSION,
    }
    return render_template("chart-flow.html", **context)
Esempio n. 26
0
    def moving_median_abs_dev(self, year=None, month=None, day=None, weeks=4):
        """
        The moving median absolute deviation of cycle time for every day in the last N weeks.
        See http://en.wikipedia.org/wiki/Median_absolute_deviation
        """

        end_date = make_end_date(year, month, day)
        start_date = end_date - relativedelta(weeks=weeks)
        start_date = make_start_date(date=start_date)

        qs = self.done().filter(
            done_date__lte=end_date,
            done_date__gte=start_date,
        ).scalar('_cycle_time')

        cycle_times = [t for t in qs if t is not None]
        median_cycle_time = median(cycle_times)

        if median_cycle_time is not None:
            absolute_deviations = [math.fabs(median_cycle_time - c) for c in cycle_times]
            mad = median(absolute_deviations)
        else:
            mad = None

        if mad is None:
            mad = 0

        return int(round(mad))
Esempio n. 27
0
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)
Esempio n. 28
0
    def calculate(klass, date, group='all'):
        """
        Creates or updates a DailyRecord for the date provided.
        """

        date = make_end_date(date=date)

        try:
            k = klass.objects.get(date=date, group=group)
        except klass.DoesNotExist:
            k = klass()
            k.date = date
            k.group = group

        k.backlog = ReportGroup(group, Kard.backlogged(date)).queryset.count()
        k.in_progress = ReportGroup(group,
                                    Kard.in_progress(date)).queryset.count()
        k.done = ReportGroup(
            group, Kard.objects.filter(done_date__lte=date)).queryset.count()
        k.completed = ReportGroup(
            group, Kard.objects.filter(done_date=date)).queryset.count()
        k.moving_cycle_time = ReportGroup(
            group, Kard.objects).queryset.moving_cycle_time(year=date.year,
                                                            month=date.month,
                                                            day=date.day)
        k.moving_lead_time = ReportGroup(
            group, Kard.objects).queryset.moving_lead_time(year=date.year,
                                                           month=date.month,
                                                           day=date.day)

        k.save()
Esempio n. 29
0
    def calculate(klass, date, group='all'):
        """
        Creates or updates a DailyRecord for the date provided.
        """

        date = make_end_date(date=date)

        try:
            k = klass.objects.get(date=date, group=group)
        except klass.DoesNotExist:
            k = klass()
            k.date = date
            k.group = group

        k.backlog = ReportGroup(group, Kard.backlogged(date)).queryset.count()
        k.in_progress = ReportGroup(group, Kard.in_progress(date)).queryset.count()
        k.done = ReportGroup(group, Kard.objects.filter(done_date__lte=date)).queryset.count()
        k.completed = ReportGroup(group, Kard.objects.filter(done_date=date)).queryset.count()
        k.moving_cycle_time = ReportGroup(group, Kard.objects).queryset.moving_cycle_time(
            year=date.year, month=date.month, day=date.day)
        k.moving_lead_time = ReportGroup(group, Kard.objects).queryset.moving_lead_time(
            year=date.year, month=date.month, day=date.day)
        k.moving_std_dev = ReportGroup(group, Kard.objects).queryset.moving_std_dev(
            year=date.year, month=date.month, day=date.day)
        k.moving_median_abs_dev = ReportGroup(group, Kard.objects).queryset.moving_median_abs_dev(
            year=date.year, month=date.month, day=date.day)

        k.save()
Esempio n. 30
0
def _get_time_range(weeks):
    end = make_end_date(
        date=datetime.now()
    )
    start = make_start_date(
        date=end - relativedelta(weeks=weeks)
    )
    return start, end
Esempio n. 31
0
def report_efficiency(group="all", months=3):
    state_mappings = app.config.get("EFFICIENCY_MAPPINGS", None)
    if state_mappings is None:
        abort(404)
    stats = teams_service.EfficiencyStats(mapping=state_mappings)

    end = kardboard.util.now()
    months_ranges = month_ranges(end, months)

    start_day = make_start_date(date=months_ranges[0][0])
    end_day = make_end_date(date=end)

    records = FlowReport.objects.filter(date__gte=start_day, date__lte=end_day, group=group)

    incremented_state_counts = []
    for r in records:
        a_record = {"date": r.date}
        a_record.update(r.state_counts)
        incremented_state_counts.append(a_record)

    for group_name in app.config.get("EFFICIENCY_INCREMENTS", ()):
        stats.make_incremental(incremented_state_counts, group_name)

    data = []
    for r in incremented_state_counts:
        efficiency_stats = stats.calculate(r)
        data.append({"date": r["date"], "stats": efficiency_stats})

    chart = {}
    chart["categories"] = [report.date.strftime("%m/%d") for report in records]
    group_names = app.config.get("EFFICIENCY_MAPPINGS_ORDER", state_mappings.keys())
    series = []
    for group_name in group_names:
        seri_data = []
        for d in data:
            seri_data.append(d["stats"][group_name])
        seri = dict(name=group_name, data=seri_data)
        series.append(seri)
    chart["series"] = series

    table_data = []
    for row in data:
        table_row = {"Date": row["date"]}
        for group_name in group_names:
            table_row[group_name] = row["stats"][group_name]
        table_data.append(table_row)

    start_date = records.order_by("date").first().date
    context = {
        "title": "Efficiency",
        "start_date": start_date,
        "chart": chart,
        "table_data": table_data,
        "data_keys": ["Date"] + list(group_names),
        "updated_at": records.order_by("date")[len(records) - 1].date,
        "version": VERSION,
    }
    return render_template("chart-efficiency.html", **context)
Esempio n. 32
0
def report_detailed_flow(group="all", months=3, cards_only=False):
    end = kardboard.util.now()
    months_ranges = month_ranges(end, months)

    start_day = make_start_date(date=months_ranges[0][0])
    end_day = make_end_date(date=end)

    if cards_only:
        only_arg = ("state_card_counts", "date", "group")
    else:
        only_arg = ("state_counts", "date", "group")

    reports = FlowReport.objects.filter(date__gte=start_day, date__lte=end_day, group=group).only(*only_arg)
    if not reports:
        abort(404)

    chart = {}
    chart["categories"] = []

    series = []
    for state in States():
        seri = {"name": state, "data": []}
        series.append(seri)

    done_starting_point = 0
    for report in reports:
        chart["categories"].append(report.date.strftime("%m/%d"))
        for seri in series:
            if cards_only:
                daily_seri_data = report.state_card_counts.get(seri["name"], 0)
            else:
                daily_seri_data = report.state_counts.get(seri["name"], 0)

            if seri["name"] == "Done":
                if len(seri["data"]) == 0:
                    done_starting_point = daily_seri_data
                    daily_seri_data = 0
                else:
                    daily_seri_data = daily_seri_data - done_starting_point

            seri["data"].append(daily_seri_data)
    chart["series"] = series

    start_date = reports.order_by("date").first().date
    reports.order_by("-date")
    context = {
        "title": "Detailed Cumulative Flow",
        "reports": reports,
        "months": months,
        "cards_only": cards_only,
        "chart": chart,
        "start_date": start_date,
        "updated_at": reports[0].updated_at,
        "states": States(),
        "version": VERSION,
    }
    return render_template("report-detailed-flow.html", **context)
Esempio n. 33
0
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
Esempio n. 34
0
    def done_in_week(self, year=None, month=None, day=None, date=None):
        """
        Kards that were completed in the week of the specified day.
        """
        if not date:
            date = munge_date(year, month, day)
            date = make_end_date(date=date)
        start_date, end_date = week_range(date)

        results = self.done().filter(done_date__lte=date, done_date__gte=start_date)
        return results
Esempio n. 35
0
    def done_in_range(self, start_date, end_date):
        end_date = make_end_date(date=end_date)
        start_date = make_start_date(date=start_date)

        done = Kard.objects.filter(
            team=self.team_name,
            done_date__gte=start_date,
            done_date__lte=end_date,
            _service_class__nin=self.exclude_classes,
        )
        return done
Esempio n. 36
0
    def done_in_week(self, year=None, month=None, day=None, date=None):
        """
        Kards that were completed in the week of the specified day.
        """
        if not date:
            date = munge_date(year, month, day)
            date = make_end_date(date=date)
        start_date, end_date = week_range(date)

        results = self.done().filter(done_date__lte=date,
                                     done_date__gte=start_date)
        return results
Esempio n. 37
0
    def done_in_month(self, year=None, month=None, day=None, date=None):
        """
        Kards that have been completed in the specified month.
        """
        if not date:
            date = munge_date(year=year, month=month, day=day)
            date = make_end_date(date=date)

        start_date, faux_end_date = month_range(date)

        results = self.done().filter(done_date__lte=date, done_date__gte=start_date)
        return results
Esempio n. 38
0
def team(team_slug=None):
    date = datetime.datetime.now()
    date = make_end_date(date=date)
    teams = app.config.get('CARD_TEAMS', [])

    team_mapping = {}
    for team in teams:
        team_mapping[slugify(team)] = team

    target_team = None
    if team_slug:
        target_team = team_mapping.get(team_slug, None)
        if not team:
            abort(404)

    board = DisplayBoard(teams=[target_team, ])

    metrics = [
        {'Ave. Cycle Time': Kard.objects.filter(team=target_team).moving_cycle_time(
            year=date.year, month=date.month, day=date.day)},
        {'Done this week': Kard.objects.filter(team=target_team).done_in_week(
            year=date.year, month=date.month, day=date.day).count()},
        {'Done this month':
            Kard.objects.filter(team=target_team).done_in_month(
                year=date.year, month=date.month, day=date.day).count()},
        {'On the board': len(board.cards)},
    ]

    done_cards = Kard.objects.done().filter(team=target_team).order_by('-done_date')

    title = "%s cards" % target_team

    report_config = (
        {'slug': 'cycle', 'name': 'Cycle time'},
        {'slug': 'classes', 'name': 'Throughput'},
        {'slug': 'leaderboard', 'name': 'Leaderboard'},
    )

    context = {
        'title': title,
        'team_slug': team_slug,
        'target_team': target_team,
        'done_cards': done_cards,
        'metrics': metrics,
        'report_config': report_config,
        'board': board,
        'date': date,
        'updated_at': datetime.datetime.now(),
        'version': VERSION,
    }

    return render_template('team.html', **context)
Esempio n. 39
0
def find_wip(report_group_slug, stop):
    sl = FlowReport.objects.get(
        date=make_end_date(date=stop),
        group=report_group_slug,
    )

    exclude = [u'Backlog', u'Done', ]
    wip = 0
    for state, count in sl.state_counts.items():
        if state not in exclude:
            wip += count

    return wip
Esempio n. 40
0
    def done_in_range(self, start_date, end_date):
        end_date = make_end_date(date=end_date)
        start_date = make_start_date(date=start_date)

        done = Kard.objects.filter(
            team=self.team_name,
            done_date__gte=start_date,
            done_date__lte=end_date,
            _service_class__nin=self.exclude_classes,
        )

        self._card_info(done)
        return done
Esempio n. 41
0
    def done_in_month(self, year=None, month=None, day=None, date=None):
        """
        Kards that have been completed in the specified month.
        """
        if not date:
            date = munge_date(year=year, month=month, day=day)
            date = make_end_date(date=date)

        start_date, faux_end_date = month_range(date)

        results = self.done().filter(done_date__lte=date,
                                     done_date__gte=start_date)
        return results
Esempio n. 42
0
def report_cycle(group="all", months=3, year=None, month=None, day=None):
    today = datetime.datetime.today()
    if day:
        end_day = datetime.datetime(year=year, month=month, day=day)
        if end_day > today:
            end_day = today
    else:
        end_day = today

    start_day = end_day - relativedelta.relativedelta(months=months)
    start_day = make_start_date(date=start_day)
    end_day = make_end_date(date=end_day)

    records = DailyRecord.objects.filter(
        date__gte=start_day,
        date__lte=end_day,
        group=group)

    daily_moving_averages = [(r.date, r.moving_cycle_time) for r in records]
    daily_moving_lead = [(r.date, r.moving_lead_time) for r in records]
    daily_mad = [(r.date, r.moving_median_abs_dev) for r in records]

    start_date = daily_moving_averages[0][0]
    chart = {}
    chart['series'] = [
        {
            'name': 'Cycle time',
            'data': [r[1] for r in daily_moving_averages],
        },
        {
            'name': 'Unpredictability',
            'data': [r[1] for r in daily_mad],
        }
    ]
    chart['goal'] = app.config.get('CYCLE_TIME_GOAL', ())

    daily_moving_averages.reverse()  # reverse order for display
    daily_moving_lead.reverse()
    daily_mad.reverse()
    context = {
        'title': "How quick can we do it?",
        'updated_at': datetime.datetime.now(),
        'chart': chart,
        'months': months,
        'start_date': start_date,
        'daily_averages': daily_moving_averages,
        'daily_mad': daily_mad,
        'version': VERSION,
    }

    return render_template('report-cycle.html', **context)
Esempio n. 43
0
def chart_cycle_distribution(group="all", months=3):
    ranges = (
        (0, 4, "< 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)
    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('chart-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)
        pct = ReportGroup(group, Kard.objects.filter(query)).queryset.count() / float(total)
        distro.append((label, pct))

    chart = CycleDistributionChart(700, 400)
    chart.add_data([r[1] for r in distro])
    chart.set_pie_labels([r[0] for r in distro])
    context = {
        'data': distro,
        'chart': chart,
        'title': "How quick can we do it?",
        'updated_at': datetime.datetime.now(),
        'distro': distro,
        'version': VERSION,
    }
    return render_template('chart-cycle-distro.html', **context)
Esempio n. 44
0
    def _date(self, dtype, date=None, days=0):
        from kardboard.util import make_end_date, make_start_date
        from kardboard.util import now

        if not date:
            date = now()

        if dtype == 'start':
            date = make_start_date(date=date)
        elif dtype == 'end':
            date = make_end_date(date=date)

        date = date + relativedelta(days=days)
        return date
Esempio n. 45
0
def queue_daily_record_updates(days=365):
    from kardboard.app import app
    from kardboard.util import make_end_date

    report_groups = app.config.get('REPORT_GROUPS', {})
    group_slugs = report_groups.keys()
    group_slugs.append('all')

    now = datetime.datetime.now()

    for i in xrange(0, days):
        target_date = now - relativedelta.relativedelta(days=i)
        target_date = make_end_date(date=target_date)
        for slug in group_slugs:
            update_daily_record.delay(target_date, slug)
Esempio n. 46
0
    def _set_up_records(self):
        from kardboard.util import make_start_date
        from kardboard.util import make_end_date

        start_date = datetime.datetime(2011, 1, 1)
        end_date = datetime.datetime(2011, 6, 30)

        start_date = make_start_date(date=start_date)
        end_date = make_end_date(date=end_date)

        current_date = start_date
        while current_date <= end_date:
            r = self.make_record(current_date)
            r.save()
            current_date = current_date + relativedelta(days=1)
Esempio n. 47
0
def report_service_class(group="all", months=None):
    from kardboard.app import app
    service_class_order = app.config.get('SERVICE_CLASSES', {}).keys()
    service_class_order.sort()
    service_classes = [
        app.config['SERVICE_CLASSES'][k] for k in service_class_order
    ]

    if months is None:
        # We want the current report
        try:
            scr = ServiceClassSnapshot.objects.get(group=group, )
        except ServiceClassSnapshot.DoesNotExist:
            scr = ServiceClassSnapshot.calculate(group=group, )
        time_range = 'current'
        start_date = make_start_date(date=datetime.datetime.now())
        end_date = make_end_date(date=datetime.datetime.now())
    else:
        start = now()
        months_ranges = month_ranges(start, months)
        start_date = months_ranges[0][0]
        end_date = months_ranges[-1][1]
        try:
            scr = ServiceClassRecord.objects.get(
                group=group,
                start_date=start_date,
                end_date=end_date,
            )
        except ServiceClassRecord.DoesNotExist:
            scr = ServiceClassRecord.calculate(
                group=group,
                start_date=start_date,
                end_date=end_date,
            )
        time_range = 'past %s months' % months

    context = {
        'title': "Service classes: %s" % time_range,
        'service_classes': service_classes,
        'data': scr.data,
        'start_date': start_date,
        'end_date': end_date,
        'updated_at': scr.updated_at,
        'version': VERSION,
    }

    return render_template('report-service-class.html', **context)
Esempio n. 48
0
def card_block(key):
    try:
        card = Kard.objects.get(key=key)
        action = 'block'
        if card.blocked:
            action = 'unblock'
    except Kard.DoesNotExist:
        abort(404)

    now = datetime.datetime.now()
    if action == 'block':
        f = CardBlockForm(request.form, blocked_at=now)
    if action == 'unblock':
        f = CardUnblockForm(request.form, unblocked_at=now)

    if 'cancel' in request.form.keys():
        return True  # redirect
    elif request.method == "POST" and f.validate():
        if action == 'block':
            blocked_at = datetime.datetime.combine(f.blocked_at.data,
                                                   datetime.time())
            blocked_at = make_start_date(date=blocked_at)
            result = card.block(f.reason.data, blocked_at)
            if result:
                card.save()
                flash("%s blocked" % card.key)
                return True  # redirect
        if action == 'unblock':
            unblocked_at = datetime.datetime.combine(f.unblocked_at.data,
                                                     datetime.time())
            unblocked_at = make_end_date(date=unblocked_at)
            result = card.unblock(unblocked_at)
            if result:
                card.save()
                flash("%s unblocked" % card.key)
                return True  # redurect

    context = {
        'title': "%s a card" % (action.capitalize(), ),
        'action': action,
        'card': card,
        'form': f,
        'updated_at': datetime.datetime.now(),
        'version': VERSION,
    }

    return render_template('card-block.html', **context)
Esempio n. 49
0
def main():
    oldest_card = Kard.objects.all().order_by('+backlog_date')[0]
    start_date = make_start_date(date=oldest_card.backlog_date)
    end_date = make_end_date(date=datetime.datetime.now())

    print "Daily records: %s" % DailyRecord.objects.count()
    print "Creating daily records"
    print "%s --> %s" % (start_date, end_date)

    days = end_date - start_date
    print "Going back %s days" % days.days

    r = queue_daily_record_updates.apply(args=(days.days, ))
    r.get()

    print "DONE!"
    print "Daily records: %s" % DailyRecord.objects.count()
Esempio n. 50
0
def report_flow(group="all", months=3):
    end = kardboard.util.now()
    months_ranges = month_ranges(end, months)

    start_day = make_start_date(date=months_ranges[0][0])
    end_day = make_end_date(date=end)

    records = DailyRecord.objects.filter(date__gte=start_day,
                                         date__lte=end_day,
                                         group=group)

    chart = {}
    chart['categories'] = [report.date.strftime("%m/%d") for report in records]
    series = [
        {
            'name': "Planning",
            'data': []
        },
        {
            'name': "Todo",
            'data': []
        },
        {
            'name': "Done",
            'data': []
        },
    ]
    for row in records:
        series[0]['data'].append(row.backlog)
        series[1]['data'].append(row.in_progress)
        series[2]['data'].append(row.done)
    chart['series'] = series

    start_date = records.order_by('date').first().date
    records.order_by('-date')
    context = {
        'title': "Cumulative Flow",
        'updated_at': datetime.datetime.now(),
        'chart': chart,
        'start_date': start_date,
        'flowdata': records,
        'version': VERSION,
    }
    return render_template('chart-flow.html', **context)
Esempio n. 51
0
    def moving_lead_time(self, year=None, month=None, day=None, weeks=4):
        """
        The moving average of lead time for every day in the last N weeks.
        """

        end_date = make_end_date(year, month, day)
        start_date = end_date - relativedelta(weeks=weeks)
        start_date = make_start_date(date=start_date)

        qs = self.done().filter(
            done_date__lte=end_date,
            done_date__gte=start_date,
        )

        average = qs.average('_lead_time')
        if math.isnan(average):
            average = 0

        return int(round(average))
Esempio n. 52
0
    def capture(klass, group='all'):
        date = datetime.datetime.now()
        date = make_end_date(date=date)
        try:
            r = klass.objects.get(date=date, group=group)
        except klass.DoesNotExist:
            r = klass()
            r.date = date
            r.group = group

        states = States()

        for state in states:
            group_cards = ReportGroup(
                group, Kard.objects.filter(state=state)).queryset
            r.state_counts[state] = group_cards.count()

            non_defects = [c for c in group_cards.only('_type') if c.is_card]
            r.state_card_counts[state] = len(non_defects)

        r.save()
        return r
Esempio n. 53
0
    def moving_std_dev(self, year=None, month=None, day=None, weeks=4):
        """
        The moving cycle time standard deviation for every day in the last N weeks.
        """

        end_date = make_end_date(year, month, day)
        start_date = end_date - relativedelta(weeks=weeks)
        start_date = make_start_date(date=start_date)

        qs = self.done().filter(
            done_date__lte=end_date,
            done_date__gte=start_date,
        ).scalar('_cycle_time')

        cycle_times = [t for t in qs if t is not None]
        stdev = standard_deviation(cycle_times)
        if stdev is not None:
            stdev = int(round(stdev))
        else:
            stdev = 0

        return stdev
Esempio n. 54
0
def state():
    date = datetime.datetime.now()
    date = make_end_date(date=date)
    states = States()

    board = DisplayBoard()  # defaults to all teams, 7 days of done
    board.cards  # force the card calculation
    board.rows

    title = app.config.get('SITE_NAME')

    wip_cards = [k for k in board.cards if k.state in states.in_progress]
    done_this_week = [k for k in board.cards if k.state == states.done]

    blocked = [k for k in wip_cards if k.blocked]

    metrics = [
        {
            'WIP': len(wip_cards)
        },
        {
            'Blocked': len(blocked)
        },
        {
            'Done this week': len(done_this_week)
        },
    ]

    context = {
        'title': title,
        'board': board,
        'states': states,
        'metrics': metrics,
        'date': date,
        'updated_at': datetime.datetime.now(),
        'version': VERSION,
    }
    return render_template('team.html', **context)
Esempio n. 55
0
    def moving_cycle_time(self, year=None, month=None, day=None, weeks=4):
        """
        The moving average of cycle time for every day in the last N weeks.
        """

        end_date = make_end_date(year, month, day)
        start_date = end_date - relativedelta(weeks=weeks)
        start_date = make_start_date(date=start_date)

        qs = self.done().filter(
            done_date__lte=end_date,
            done_date__gte=start_date,
        )

        try:
            average = qs.average('_cycle_time')
        except TypeError:
            average = float('nan')

        if math.isnan(average):
            return 0

        return int(round(average))
Esempio n. 56
0
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)