Example #1
0
def new(request, board_id):
    member = request.user.member
    board = get_user_board_or_404(request.user, board_id)

    requirement = Requirement(board=board)

    if request.method == "POST":
        form = NewRequirementForm(request.POST, instance=requirement)

        if form.is_valid():
            form.save(commit=True)
            return HttpResponseRedirect(
                reverse("boards:requirements:view_requirements",
                        args=(board_id, )))
    else:
        form = NewRequirementForm(instance=requirement)

    return render(request, "requirements/new.html", {
        "form": form,
        "board": board,
        "member": member
    })
Example #2
0
def delete(request, board_id):
    member = request.user.member

    # Load the board
    board = get_user_board_or_404(request.user, board_id)

    # Show delete form
    if request.method == "GET":
        replacements = {"member": member, "board": board}
        return render(request, "boards/delete.html", replacements)

    # Delete action by post
    confirmed_board_id = request.POST.get("board_id")
    if confirmed_board_id and confirmed_board_id == board_id:
        # Only archived boards can be deleted
        if board.is_archived:
            board.delete()
            return HttpResponseRedirect(reverse("boards:view_boards"))
        else:
            replacements = {"member": member, "board": board}
            return render(request, "boards/delete.html", replacements)

    raise Http404
Example #3
0
def view_identicon(request, board_id, width=40, height=40):
    board = get_user_board_or_404(request.user, board_id, is_archived=None)

    # List of colors taken from example http://pydenticon.readthedocs.io/en/0.3/usage.html#instantiating-a-generator
    foreground = [
        "#{0}".format(board.title_color), "rgb(45,79,255)", "rgb(254,180,44)",
        "rgb(226,121,234)", "rgb(30,179,253)", "rgb(232,77,65)",
        "rgb(49,203,115)", "rgb(141,69,170)"
    ]

    # Background color taken from example http://pydenticon.readthedocs.io/en/0.3/usage.html#instantiating-a-generator
    background = u"#{0}".format(board.background_color)

    identicon_hash = hashlib.sha1(board.name.encode('utf-8')).hexdigest()

    # If the identicon is already stored, return it
    if identicon_hash == board.identicon_hash:
        return HttpResponseRedirect(board.identicon.url)

    # Otherwise, its generation is needed
    board.identicon_hash = identicon_hash

    generator = pydenticon.Generator(5,
                                     5,
                                     digest=hashlib.sha1,
                                     foreground=foreground,
                                     background=background)

    identicon_png = generator.generate(board.name,
                                       int(width),
                                       int(height),
                                       output_format="png")

    board.identicon.save(u"{0}".format(identicon_hash),
                         ContentFile(identicon_png))
    board.save()
    return HttpResponseRedirect(board.identicon.url)
Example #4
0
def export_report(request, board_id):

    member = None
    if user_is_member(request.user):
        member = request.user.member

    board = get_user_board_or_404(request.user, board_id)
    cards = board.cards.all()

    response = HttpResponse(content_type='text/csv')
    response['Content-Disposition'] = u'attachment; filename="{0}-cards.csv"'.format(board.name)

    csv_template = loader.get_template('boards/cards/csv.txt')
    replacements = {
        "member": member,
        "board": board,
        "cards": cards,
        "avg_lead_time": avg(cards, "lead_time"),
        "std_dev_lead_time": std_dev(cards, "lead_time"),
        "avg_cycle_time": avg(cards, "cycle_time"),
        "std_dev_cycle_time": std_dev(cards, "cycle_time"),
    }
    response.write(csv_template.render(replacements))
    return response
Example #5
0
def view_workflow_card_report(request, board_id, workflow_id):

    member = None
    if user_is_member(request.user):
        member = request.user.member

    board = get_user_board_or_404(request.user, board_id)

    workflow = board.workflows.get(id=workflow_id)
    workflow_card_reports = board.workflow_card_reports.filter(
        workflow_id=workflow_id)

    replacements = {
        "workflow": workflow,
        "member": member,
        "board": board,
        "workflow_card_reports": workflow_card_reports,
        "avg_lead_time": avg(workflow_card_reports, "lead_time"),
        "std_dev_lead_time": std_dev(workflow_card_reports, "lead_time"),
        "avg_cycle_time": avg(workflow_card_reports, "cycle_time"),
        "std_dev_cycle_time": std_dev(workflow_card_reports, "cycle_time"),
    }
    return render(request, "boards/cards/workflow_card_report_list.html",
                  replacements)
Example #6
0
def view_week_summary(request, board_id, member_id="all", week_of_year=None):

    current_member = None
    if user_is_member(request.user):
        current_member = request.user.member
    board = get_user_board_or_404(request.user, board_id)

    replacements = {"board": board, "member": current_member}

    if request.method == "POST":
        form = WeekSummaryFilterForm(post_data=request.POST, board=board)
        if form.is_valid():
            year = form.cleaned_data.get("year")
            week = form.cleaned_data.get("week")
            member_id = form.cleaned_data.get("member")
            week_of_year = "{0}W{1}".format(year, week)
            return HttpResponseRedirect(reverse("boards:view_week_summary", args=(board_id, member_id, week_of_year,)))

    year = None
    week = None

    if week_of_year is None:
        now = timezone.now()
        year = now.year
        week = int(get_iso_week_of_year(now))
        week_of_year = "{0}W{1}".format(year, week)

    if week is None or year is None:
        matches = re.match(r"^(?P<year>\d{4})W(?P<week>\d{2})$", week_of_year)
        if matches:
            year = int(matches.group("year"))
            week = int(matches.group("week"))

    form = WeekSummaryFilterForm(initial={"year": year, "week": week, "member": member_id}, board=board)
    replacements["form"] = form
    replacements["week_of_year"] = week_of_year

    # Done cards that are of this member
    member_filter = {}
    if member_id != "all":
        member_filter = {"members": member_id}

    # Date limits of the selected week
    week_start_date = Week(year, week).monday()
    week_end_date = Week(year, week).friday()

    # Getting the cards that were completed in the selected week for the selected user
    completed_cards = board.cards.\
        filter(list__type="done",
               movements__type="forward",
               movements__destination_list__type="done",
               movements__datetime__gte=week_start_date,
               movements__datetime__lte=week_end_date)\
        .filter(**member_filter).\
        order_by("last_activity_datetime")

    replacements["completed_cards"] = completed_cards

    # Time spent developing on this week
    if member_id == "all":
        spent_time = board.get_spent_time([week_start_date, week_end_date])
        adjusted_spent_time = board.get_spent_time([week_start_date, week_end_date])
    else:
        member = board.members.get(id=member_id)
        spent_time = board.get_spent_time([week_start_date, week_end_date], member)
        adjusted_spent_time = board.get_spent_time([week_start_date, week_end_date], member)
        replacements["selected_member"] = member

    replacements["spent_time"] = spent_time
    replacements["adjusted_spent_time"] = adjusted_spent_time

    replacements["week_start_date"] = week_start_date
    replacements["week_end_date"] = week_end_date

    return render(request, "boards/week_summary.html", replacements)
Example #7
0
def view_gantt_chart(request, board_id):
    board = get_user_board_or_404(request.user, board_id)

    member = None
    visitor = None
    if user_is_member(request.user):
        member = request.user.member
    elif user_is_visitor(request.user, board):
        visitor = request.user

    board_cards = board.cards.filter(
        list__type__in=List.STARTED_CARD_LIST_TYPES)

    cards = []
    for board_card in board_cards:

        # Task start
        start_date = board_card.start_datetime
        if start_date is None:
            start_date = board_card.creation_datetime

        # Task end
        if board_card.due_datetime is not None:
            end_date = board_card.due_datetime
        elif board_card.list.type == "done":
            end_date = board_card.end_datetime
        else:
            end_date = start_date + timedelta(days=1)

        # Color of task
        task_color = "blue"

        # Percentage of completion of the task
        if board_card.list.type == "development":
            completion_percentage = 0
        elif board_card.list.type == "after_development_in_review":
            completion_percentage = 75
        elif board_card.list.type == "after_development_waiting_release":
            completion_percentage = 85
        else:
            completion_percentage = 100

        # Parent Task
        blocking_cards = board_card.blocking_cards.all().order_by(
            "creation_datetime")
        parent_card = 0
        if blocking_cards.exists():
            parent_card = blocking_cards[0].id

        # Dependant tasks
        blocked_cards = board_card.blocked_cards.all()
        dependant_cards = ""
        if blocked_cards.exists():
            for blocked_card in blocked_cards:
                dependant_cards += ",".format(blocked_card.id)
            dependant_cards = dependant_cards[:-1]

        members = board_card.members.all()
        for member in members:
            card = {
                "pID":
                board_card.id,
                "pName":
                "{0}".format(board_card.short_url, member.external_username),
                "pStart":
                start_date.strftime("%Y-%m-%d"),
                "pEnd":
                end_date.strftime("%Y-%m-%d"),
                "pClass":
                "gtask{0}".format(task_color),
                "pLink":
                reverse("boards:view_card", args=(board_id, board_card.id)),
                "pMile":
                0,
                "pRes":
                member.external_username,
                "pComp":
                completion_percentage,
                "pGroup":
                1 if blocked_cards.exists() else 0,
                "pParent":
                parent_card,
                "pOpen":
                0,
                "pDepend":
                dependant_cards,
                "pCaption":
                board_card.name,
                "pNotes":
                "{0}\\\n{1}".format(
                    board_card.name,
                    board_card.description.replace("\n", "\\\n"))
            }
            cards.append(card)

    replacements = {
        "board": board,
        "cards": cards,
        "member": member,
        "visitor": visitor,
    }
    return render(request, "boards/gantt_chart.html", replacements)