Exemplo n.º 1
0
def start_date_validator(form, field):
        states = States()
        if states.index(form.state.data) >= states.index(states.start):
            if field.data is None:
                raise ValidationError("Start date required since the card's state is %s" % form.state.data)
        else:
            field.errors = []
Exemplo n.º 2
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)
Exemplo n.º 3
0
def start_date_validator(form, field):
    states = States()
    if states.index(form.state.data) >= states.index(states.start):
        if field.data is None:
            raise ValidationError("Start date required since the card's state is %s" % form.state.data)
    elif field.data is None and form.backlog_date.data is not None and form.done_date.data is not None:
        raise ValidationError("Card with a backlog date and done date must have a start date")
    else:
        field.errors = []
Exemplo n.º 4
0
def start_date_validator(form, field):
    states = States()
    if states.index(form.state.data) >= states.index(states.start):
        if field.data is None:
            raise ValidationError(
                "Start date required since the card's state is %s" %
                form.state.data)
    elif (field.data is None and form.backlog_date.data is not None
          and form.done_date.data is not None):
        raise ValidationError(
            'Card with a backlog date and done date must have a start date')
    else:
        field.errors = []
Exemplo n.º 5
0
def funnel(state_slug):
    states = States()
    try:
        state = states.find_by_slug(state_slug)
        funnel = Funnel(state, app.config.get('FUNNEL_VIEWS', {})[state])
    except KeyError:
        abort(404)

    cards = funnel.ordered_cards()

    funnel_auth = False
    if kardboard.auth.is_authenticated() is True:
        funnel_auth = funnel.is_authorized(session.get('username', ''))

    if request.method == "POST":
        if kardboard.auth.is_authenticated() is False or funnel_auth is False:
            abort(403)

        start = time.time()
        card_key_list = request.form.getlist('card[]')

        counter = 1
        for card_key in card_key_list:
            Kard.objects(key=card_key.strip()).only('priority').update_one(
                set__priority=counter)
            counter += 1

        elapsed = (time.time() - start)
        return jsonify(message="Reordered %s cards in %.2fs" %
                       (counter, elapsed))

    title = "%s: All boards" % state

    funnel_markers = funnel.markers()

    context = {
        'title': title.replace("Backlog",
                               "Ready: Elabo").replace("OTIS", "TIE"),
        'state': state,
        'state_slug': state_slug,
        'cards': cards,
        'times_in_state': funnel.times_in_state(),
        'funnel_throughput': funnel.throughput,
        'funnel_markers': funnel_markers,
        'funnel_auth': funnel_auth,
        'updated_at': datetime.datetime.now(),
        'version': VERSION,
        'authenticated': kardboard.auth.is_authenticated(),
    }

    return render_template('funnel.html', **context)
Exemplo n.º 6
0
def funnel(state_slug):
    states = States()
    try:
        state = states.find_by_slug(state_slug)
        funnel = Funnel(state, app.config.get('FUNNEL_VIEWS', {})[state])
    except KeyError:
        abort(404)

    cards = funnel.ordered_cards()

    funnel_auth = False
    if kardboard.auth.is_authenticated() is True:
        funnel_auth = funnel.is_authorized(session.get('username', ''))

    if request.method == "POST":
        if kardboard.auth.is_authenticated() is False or funnel_auth is False:
            abort(403)

        start = time.time()
        card_key_list = request.form.getlist('card[]')

        counter = 1
        for card_key in card_key_list:
            Kard.objects(
                key=card_key.strip()
            ).only('priority').update_one(set__priority=counter)
            counter += 1

        elapsed = (time.time() - start)
        return jsonify(message="Reordered %s cards in %.2fs" % (counter, elapsed))

    title = "%s: All boards" % state

    funnel_markers = funnel.markers()

    context = {
        'title': title.replace("Backlog", "Ready: Elabo").replace("OTIS", "TIE"),
        'state': state,
        'state_slug': state_slug,
        'cards': cards,
        'times_in_state': funnel.times_in_state(),
        'funnel_throughput': funnel.throughput,
        'funnel_markers': funnel_markers,
        'funnel_auth': funnel_auth,
        'updated_at': datetime.datetime.now(),
        'version': VERSION,
        'authenticated': kardboard.auth.is_authenticated(),
    }

    return render_template('funnel.html', **context)
Exemplo n.º 7
0
def funnel(state_slug):
    states = States()
    try:
        state = states.find_by_slug(state_slug)
        funnel = Funnel(state, app.config.get("FUNNEL_VIEWS", {})[state])
    except KeyError:
        abort(404)

    cards = funnel.ordered_cards()

    funnel_auth = False
    if kardboard.auth.is_authenticated() is True:
        funnel_auth = funnel.is_authorized(session.get("username", ""))

    if request.method == "POST":
        if kardboard.auth.is_authenticated() is False or funnel_auth is False:
            abort(403)

        start = time.time()
        card_key_list = request.form.getlist("card[]")

        counter = 1
        for card_key in card_key_list:
            Kard.objects(key=card_key.strip()).only("priority").update_one(set__priority=counter)
            counter += 1

        elapsed = time.time() - start
        return jsonify(message="Reordered %s cards in %.2fs" % (counter, elapsed))

    title = "%s: All boards" % state

    funnel_markers = funnel.markers()

    context = {
        "title": title,
        "state": state,
        "state_slug": state_slug,
        "cards": cards,
        "times_in_state": funnel.times_in_state(),
        "funnel_throughput": funnel.throughput,
        "funnel_markers": funnel_markers,
        "funnel_auth": funnel_auth,
        "updated_at": datetime.datetime.now(),
        "version": VERSION,
        "authenticated": kardboard.auth.is_authenticated(),
    }

    return render_template("funnel.html", **context)
Exemplo n.º 8
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)
Exemplo n.º 9
0
def main():
    states = States()

    backlogged_states = []
    for k in Kard.backlogged():
        if k.state not in states:
            if k.state not in backlogged_states:
                backlogged_states.append(k.state)
            k.state = states.backlog
            k.save()

    start_states = []
    for k in Kard.in_progress():
        if k.state not in states:
            if k.state not in start_states:
                start_states.append(k.state)
            k.state = states.start
            k.save()

    print "Valid states"
    print states

    print "Backlogged states that went away"
    print backlogged_states

    print "Start states that went away"
    print start_states
Exemplo n.º 10
0
def jira_add_team_cards(team, filter_id):
    from kardboard.tickethelpers import JIRAHelper
    from kardboard.models import States
    from kardboard.app import app

    statsd_conn = app.statsd.get_client('tasks.jira_add_team_cards')

    counter = statsd_conn.get_client(class_=statsd.Counter)
    total_timer = statsd_conn.get_client(class_=statsd.Timer)
    total_timer.start()

    logger = jira_add_team_cards.get_logger()
    logger.info("JIRA BACKLOG SYNC %s: %s" % (team, filter_id))
    states = States()
    helper = JIRAHelper(app.config, None)
    issues = helper.service.getIssuesFromFilter(helper.auth, filter_id)
    for issue in issues:
        if Kard.objects.filter(key=issue.key):
            # Card exists, pass
            pass
        else:
            logger.info("JIRA BACKLOGGING %s: %s" % (team, issue.key))
            defaults = {
                'key': issue.key,
                'title': issue.summary,
                'backlog_date': datetime.datetime.now(),
                'team': team,
                'state': states.backlog,
            }
            c = Kard(**defaults)
            c.ticket_system.actually_update(issue)
            c.save()
            counter += 1

    total_timer.stop()
Exemplo n.º 11
0
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)
Exemplo n.º 12
0
def done_date_validator(form, field):
    states = States()
    if form.state.data == states.done:
        if field.data is None:
            raise ValidationError(
                "Done date required since the card's state is %s" %
                form.state.data)
    else:
        field.errors = []
Exemplo n.º 13
0
 def test_wip(self):
     from kardboard.models import States
     states = States()
     with mock.patch('kardboard.services.teams.Kard') as mock_Kard:
         self.service.wip()
         mock_Kard.objects.filter.assert_called_with(
             team=self.team,
             done_date=None,
             state__in=states.in_progress,
         )
Exemplo n.º 14
0
    def test_start_and_done_date_togetherness(self):
        from kardboard.models import States
        states = States()

        self.required_data['state'] = unicode(states.done)
        self.required_data['start_date'] = ''
        self.required_data['done_date'] = u'06/12/2011'
        f = self.Form(self._post_data())
        f.validate()
        self.assertEqual(2, len(f.start_date.errors))
Exemplo n.º 15
0
    def test_done_date_requiredness(self):
        """
        If the state is set to DONE_STATE then
        a done_date is required.
        """
        from kardboard.models import States
        states = States()

        self.required_data['state'] = unicode(states.done)

        f = self.Form(self._post_data())
        f.validate()
        self.assertEqual(2, len(f.done_date.errors))
Exemplo n.º 16
0
    def setUp(self):
        super(DisplayBoardTests, self).setUp()
        self.config[
            'TICKET_HELPER'] = 'kardboard.tickethelpers.TestTicketHelper'

        self.Kard = self._get_card_class()

        self.start_date = self._date('start', )

        from kardboard.models import States
        self.states = States()
        self.teams = self.config.get('CARD_TEAMS')
        self._set_up_cards()
Exemplo n.º 17
0
def team_backlog(team_slug=None):
    if request.method == "POST":
        if kardboard.auth.is_authenticated() is False:
            abort(403)

        start = time.time()
        card_key_list = request.form.getlist('card[]')
        counter = 1
        for card_key in card_key_list:
            Kard.objects(key=card_key.strip()).only('priority').update_one(
                set__priority=counter)
            counter += 1

        elapsed = (time.time() - start)
        return jsonify(message="Reordered %s cards in %.2fs" %
                       (counter, elapsed))

    teams = _get_teams()
    team = _find_team_by_slug(team_slug, teams)
    weeks = 4

    backlog = Kard.objects.filter(
        team=team.name,
        state=States().backlog,
    ).exclude('_ticket_system_data').order_by('priority')

    backlog_marker_data, backlog_markers = _team_backlog_markers(
        team, backlog, weeks)

    backlog_without_order = [k for k in backlog if k.priority is None]
    backlog_with_order = [k for k in backlog if k.priority is not None]
    backlog_with_order.sort(key=lambda k: k.priority)
    backlog = backlog_with_order + backlog_without_order

    title = "%s" % team.name

    context = {
        'title': title,
        'team_slug': team_slug,
        'team': team,
        'backlog': backlog,
        'backlog_markers': backlog_markers,
        'backlog_marker_data': backlog_marker_data,
        'updated_at': datetime.datetime.now(),
        'teams': teams,
        'version': VERSION,
        'authenticated': kardboard.auth.is_authenticated(),
    }

    return render_template('team-backlog.html', **context)
Exemplo n.º 18
0
def _init_card_form(*args, **kwargs):
    states = States()
    new = kwargs.get('new', False)
    if new:
        del kwargs['new']
    klass = get_card_form(new=new)
    f = klass(*args, **kwargs)

    if states:
        f.state.choices = states.for_forms

    teams = app.config.get('CARD_TEAMS')
    if teams:
        f.team.choices = _make_choice_field_ready(teams)

    return f
Exemplo n.º 19
0
def _init_card_form(*args, **kwargs):
    states = States()
    new = kwargs.get('new', False)
    if new:
        del kwargs['new']
    klass = get_card_form(new=new)
    f = klass(*args, **kwargs)

    if states:
        f.state.choices = states.for_forms

    teams = teams_service.setup_teams(app.config)
    if teams:
        f.team.choices = _make_choice_field_ready(teams.names)

    return f
Exemplo n.º 20
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)
Exemplo n.º 21
0
def team(team_slug=None):
    from kardboard.services.boards import TeamBoard

    teams = _get_teams()
    team = _find_team_by_slug(team_slug, teams)

    try:
        wip_limit_config = app.config['WIP_LIMITS'][team_slug]
    except KeyError:
        wip_limit_config = {}

    conwip = wip_limit_config.get('conwip', None)
    wip_limits = WIPLimits(
        name=team_slug,
        conwip=conwip,
        columns=wip_limit_config,
    )

    weeks = 4
    exclude_classes = _get_excluded_classes()
    team_stats = teams_service.TeamStats(team.name, exclude_classes)
    weekly_throughput = team_stats.weekly_throughput_ave(weeks)

    hit_sla = team_stats.hit_sla(weeks)
    hit_sla_delta = team_stats.hit_sla(weeks, weeks_offset=weeks)
    hit_sla, hit_sla_delta = zero_if_none(hit_sla), zero_if_none(hit_sla_delta)
    hit_sla_delta = hit_sla - hit_sla_delta

    total_throughput = teams_service.TeamStats(team.name).throughput(weeks)
    total_throughput_delta = teams_service.TeamStats(team.name).throughput(
        weeks, weeks_offset=weeks)
    total_throughput, total_throughput_delta = zero_if_none(
        total_throughput), zero_if_none(total_throughput_delta)
    total_throughput_delta = total_throughput - total_throughput_delta

    cycle_time = team_stats.percentile(.8, weeks)
    cycle_time_delta = team_stats.percentile(.8, weeks, weeks_offset=weeks)
    cycle_time, cycle_time_delta = zero_if_none(cycle_time), zero_if_none(
        cycle_time_delta)
    cycle_time_delta = cycle_time - cycle_time_delta

    metrics = [
        {
            'name': 'Throughput',
            'value': total_throughput,
            'delta': total_throughput_delta,
        },
        {
            'name': 'Hit SLA',
            'value': hit_sla,
            'delta': hit_sla_delta,
        },
        {
            'name': 'Cycle time',
            'value': cycle_time,
            'delta': cycle_time_delta,
        },
    ]

    board = TeamBoard(team.name, States(), wip_limits)
    backlog_limit = weekly_throughput * 4 or 30
    cards = Kard.objects.for_team_board(
        team=team.name,
        backlog_limit=backlog_limit,
        done_days=7,
    )
    board.add_cards(cards)

    backlog_marker_data, backlog_markers = _team_backlog_markers(
        team,
        board.columns[0]['cards'],
        weeks,
    )

    report_config = (
        {
            'slug': 'assignee',
            'name': "Assignee"
        },
        {
            'slug': 'cycle/distribution/all',
            'name': "Cycle time",
            'months': 1
        },
        {
            'slug': 'service-class',
            'name': 'Service class'
        },
        {
            'slug': 'blocked',
            'name': 'Blocked',
            'months': 3
        },
        {
            'slug': 'done',
            'name': 'Done',
            'months': 1
        },
        {
            'slug': 'leaderboard',
            'name': 'Leaderboard',
            'months': 3
        },
        {
            'slug': 'flow/detail',
            'name': "Cumulative Flow",
            'months': 2
        },
    )

    context = {
        'title': "%s cards" % team.name,
        'metrics': metrics,
        'wip_limits': wip_limits,
        'team': team,
        'teams': teams,
        'board': board,
        'round': round,
        'backlog_markers': backlog_markers,
        'backlog_marker_data': backlog_marker_data,
        'weekly_throughput': weekly_throughput,
        'report_config': report_config,
        'updated_at': datetime.datetime.now(),
        'version': VERSION,
        'authenticated': kardboard.auth.is_authenticated(),
    }

    return render_template('team.html', **context)
Exemplo n.º 22
0
def team(team_slug=None):
    date = datetime.datetime.now()
    date = make_end_date(date=date)
    teams = app.config.get('CARD_TEAMS', [])
    states = States()

    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,
    ])

    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]

    three_months_ago = date - relativedelta.relativedelta(months=3)
    done_past_three_months = Kard.objects.filter(
        team=target_team,
        done_date__exists=True,
        done_date__gte=three_months_ago)
    std_dev = standard_deviation(
        [k.cycle_time for k in done_past_three_months])
    ave_cycle_time = Kard.objects.filter(team=target_team).moving_cycle_time(
        year=date.year, month=date.month, day=date.day)
    confidence_cycle = round(ave_cycle_time + std_dev + std_dev)
    try:
        confidence_cycle = int(confidence_cycle)
    except ValueError:
        pass

    metrics = [
        {
            'WIP': len(wip_cards)
        },
        {
            'Ave. Cycle Time': ave_cycle_time
        },
        {
            '95% confidence level': confidence_cycle
        },
        {
            'Done this week': len(done_this_week)
        },
    ]

    title = "%s cards" % target_team

    report_config = ({
        'slug': 'assignee',
        'name': 'Assignee breakdown'
    }, {
        'slug': 'cycle/distribution',
        'name': 'Cycle time'
    }, {
        'slug': 'throughput',
        'name': 'Throughput'
    }, {
        'slug': 'leaderboard',
        'name': 'Leaderboard'
    }, {
        'slug': 'done',
        'name': 'Done'
    })

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

    return render_template('team.html', **context)
Exemplo n.º 23
0
 def setUp(self):
     from kardboard.models import States
     super(FlowReportTestCase, self).setUp()
     self.states = States()
     self._set_up_cards()
Exemplo n.º 24
0
def team(team_slug=None):
    from kardboard.services.boards import TeamBoard

    teams = _get_teams()
    team = _find_team_by_slug(team_slug, teams)

    try:
        wip_limit_config = app.config['WIP_LIMITS'][team_slug]
    except KeyError:
        wip_limit_config = {}

    conwip = wip_limit_config.get('conwip', None)
    wip_limits = WIPLimits(
        name=team_slug,
        conwip=conwip,
        columns=wip_limit_config,
    )

    weeks = 4
    exclude_classes = _get_excluded_classes()
    team_stats = teams_service.TeamStats(team.name, exclude_classes)
    weekly_throughput = team_stats.weekly_throughput_ave(weeks)
    metrics = [
        {'WIP': team_stats.wip_count()},
        {'Weekly throughput': team_stats.weekly_throughput_ave(weeks)},
    ]
    ave = team_stats.average(weeks)
    if ave:
        metrics.append({'Cycle time ave.': team_stats.average(weeks)})

    stdev = team_stats.standard_deviation(weeks)
    if stdev:
        metrics.append({'Standard deviation': stdev},)

    board = TeamBoard(team.name, States(), wip_limits)
    backlog_limit = weekly_throughput * 4 or 30
    cards = Kard.objects.for_team_board(
        team=team.name,
        backlog_limit=backlog_limit,
        done_days=7,
    )
    board.add_cards(cards)

    backlog_marker_data, backlog_markers = _team_backlog_markers(
        team,
        board.columns[0]['cards'],
        weeks,
    )
    metrics.append(
        {'80% confidence': backlog_marker_data['confidence_80']},
    )

    report_config = (
        {'slug': 'cycle/distribution/all', 'name': "Cycle time"},
        {'slug': 'flow/detail', 'name': "Cumulative Flow"},
        {'slug': 'done', 'name': 'Done'},
        {'slug': 'service-class', 'name': 'Service class'},
    )

    context = {
        'title': "%s cards" % team.name,
        'metrics': metrics,
        'wip_limits': wip_limits,
        'team': team,
        'teams': teams,
        'board': board,
        'backlog_markers': backlog_markers,
        'backlog_marker_data': backlog_marker_data,
        'weekly_throughput': weekly_throughput,
        'report_config': report_config,
        'updated_at': datetime.datetime.now(),
        'version': VERSION,
        'authenticated': kardboard.auth.is_authenticated(),
    }

    return render_template('team.html', **context)