def get_context_data(self, **kwargs):
     context = super(ReportRecipientListView, self).get_context_data(**kwargs)
     if user_is_member(self.request.user):
         context["member"] = self.request.user.member
     context["report_recipients"] = ReportRecipient.objects.all().order_by("email")
     context["user_boards"] = get_user_boards(self.request.user)
     context["user_boards_ids"] = {board.id: board for board in get_user_boards(self.request.user)}
     return context
Beispiel #2
0
def view_list(request):
    member = None
    if user_is_member(request.user):
        member = request.user.member
    boards = get_user_boards(request.user).order_by("name")
    archived_boards = get_user_boards(request.user,
                                      is_archived=True).order_by("name")
    replacements = {
        "member": member,
        "boards": boards,
        "archived_boards": archived_boards,
        "user": request.user
    }
    return render(request, "boards/list.html", replacements)
Beispiel #3
0
def view_calendar(request):
    member = None
    if user_is_member(request.user):
        member = request.user.member

    boards = get_user_boards(request.user)
    members = Member.objects.filter(boards__in=boards).distinct().filter(
        is_developer=True).order_by("id")

    min_date = DailyMemberMood.objects.filter(member__in=members).aggregate(
        min_date=Min("date"))["min_date"]
    max_date = DailyMemberMood.objects.filter(member__in=members).aggregate(
        max_date=Max("date"))["max_date"]

    dates = []
    if min_date and max_date:
        date_i = copy.deepcopy(min_date)
        while date_i <= max_date:
            # Only add date when there are mood measurements
            if DailyMemberMood.objects.filter(date=date_i,
                                              member__in=members).exists():
                dates.append(date_i)
            date_i += timedelta(days=1)

    replacements = {"member": member, "members": members, "dates": dates}
    return render(request, "niko_niko_calendar/calendar.html", replacements)
Beispiel #4
0
 def viewable_members(self):
     boards = []
     if self.user:
         boards = get_user_boards(self.user)
     return Member.objects.filter(
         Q(boards__in=boards) | Q(creator=self)
         | Q(is_public=True)).distinct()
Beispiel #5
0
 def get_user_team_mates(user):
     boards = get_user_boards(user)
     if user_is_administrator(user):
         return Member.objects.all().exclude(
             user=user).distinct().order_by("id")
     return Member.objects.filter(boards__in=boards).exclude(
         user=user).distinct().order_by("id")
Beispiel #6
0
def add_attachment(request, board_id, card_id):
    if request.method != "POST":
        return JsonResponseMethodNotAllowed(
            {"message": "HTTP method not allowed."})

    member = request.user.member
    try:
        board = get_user_boards(request.user).get(id=board_id)
        card = board.cards.get(id=card_id)
    except (Board.DoesNotExist, Card.DoesNotExist) as e:
        return JsonResponseNotFound({"message": "Not found."})

    uploaded_file_content = request.body
    if uploaded_file_content is None:
        return JsonResponseNotFound({"message": "No file sent."})

    with tempfile.TemporaryFile() as uploaded_file:
        uploaded_file.write(uploaded_file_content)
        uploaded_file_name = request.GET.get("uploaded_file_name",
                                             custom_uuid())
        attachment = card.add_new_attachment(member, uploaded_file,
                                             uploaded_file_name)

    serializer = Serializer(board=card.board)
    return JsonResponse(serializer.serialize_card_attachment(attachment))
Beispiel #7
0
 def get_all_from_member(member):
     user = member.user
     boards = get_user_boards(user)
     teammates = Member.get_user_team_members(user)
     forecasters = Forecaster.objects.filter(
         Q(last_updater=member) | Q(board__in=boards)
         | Q(member__in=list(teammates) + [member]))
     return forecasters
Beispiel #8
0
def cumulative_flow_diagram(request, board_id, day_step=1):
    board = get_user_boards(request.user).get(id=board_id)

    if day_step is None:
        day_step = 1

    day_step = min(int(day_step), 30)
    return cards.cumulative_flow_diagram(board, day_step)
Beispiel #9
0
def number_of_comments_by_member(request, board_id=None, card_id=None):
    board = None
    card = None
    if board_id is not None:
        board = get_user_boards(request.user).get(id=board_id)
        if card_id is not None:
            card = board.cards.get(id=card_id)
    return members.number_of_comments(request.user, board, card)
Beispiel #10
0
def delete(request, board_id, workflow_id):
    member = request.user.member
    board = get_user_boards(request.user).get(id=board_id)
    confirmed_workflow_id = request.POST.get("workflow_id")
    if confirmed_workflow_id and confirmed_workflow_id == workflow_id:
        workflow = board.workflows.get(id=confirmed_workflow_id)
        workflow.delete()
        return HttpResponseRedirect(reverse("boards:view_workflows", args=(board_id,)))

    raise Http404
Beispiel #11
0
def number_of_cards(current_user, board=None):

    # Caching
    chart_uuid = "members.number_of_cards-{0}-{1}".format(
        current_user.id, board.id if board else "None")
    chart = CachedChart.get(board=board, uuid=chart_uuid)
    if chart:
        return chart

    chart_title = u"Number of cards by member as of {0}".format(timezone.now())

    if board:
        chart_title += u" for board {0}".format(board.name)
        chart_title += u" (fetched on {0})".format(
            board.get_human_fetch_datetime())

    number_of_cards_chart = pygal.Bar(title=chart_title,
                                      legend_at_bottom=True,
                                      print_values=False,
                                      print_zeroes=False,
                                      fill=False,
                                      margin=0,
                                      human_readable=True)

    card_comment_filter = {}
    if board:
        card_comment_filter["board"] = board

    cards = Card.objects.filter(**card_comment_filter)

    # If there are no cards, render an empty chart
    if not cards.exists():
        return number_of_cards_chart.render_django_response()

    if board:
        boards = [board]
    else:
        boards = get_user_boards(current_user)

    members = Member.objects.filter(
        boards__in=boards).distinct().order_by("id")

    total_number_of_cards = 0
    for member in members:
        number_of_cards_by_member = cards.filter(members=member).count()
        total_number_of_cards += number_of_cards_by_member
        number_of_cards_chart.add(member.external_username,
                                  number_of_cards_by_member)

    number_of_cards_chart.add("Total number of cards", total_number_of_cards)

    chart = CachedChart.make(board=board,
                             uuid=chart_uuid,
                             svg=number_of_cards_chart.render(is_unicode=True))
    return chart.render_django_response()
Beispiel #12
0
def get_board(request, board_id):
    if request.method != "GET":
        return JsonResponseMethodNotAllowed(
            {"message": "HTTP method not allowed."})
    try:
        board = get_user_boards(request.user).get(id=board_id)
    except Board.DoesNotExist:
        return JsonResponseNotFound({"message": "Not found."})

    serializer = Serializer(board=board)
    return JsonResponse(serializer.serialize_board())
Beispiel #13
0
def avg_estimated_times(request, board=None):

    # Caching
    chart_uuid = "labels.avg_estimated_times-{0}".format(
        board.id if board else "user-{0}".format(request.user.id))
    chart = CachedChart.get(board=board, uuid=chart_uuid)
    if chart:
        return chart

    chart_title = u"Average task estimated time as of {0}".format(
        timezone.now())
    if board:
        chart_title += u" for board {0}".format(board.name)

    avg_times_chart = pygal.HorizontalBar(title=chart_title,
                                          legend_at_bottom=True,
                                          print_values=True,
                                          print_zeroes=False,
                                          human_readable=True)

    if board:
        cards = board.cards.all()
        total_avg_estimated_time = cards.aggregate(
            Avg("estimated_time"))["estimated_time__avg"]
        avg_times_chart.add(u"Average estimated time",
                            total_avg_estimated_time)
    else:
        boards = get_user_boards(request.user)
        cards = Card.objects.filter(board__in=boards)
        total_avg_estimated_time = cards.aggregate(
            Avg("estimated_time"))["estimated_time__avg"]
        avg_times_chart.add(u"All boards", total_avg_estimated_time)
        for board in boards:
            board_avg_estimated_time = board.cards.aggregate(
                Avg("estimated_time"))["estimated_time__avg"]
            if board_avg_estimated_time > 0:
                avg_times_chart.add(u"{0}".format(board.name),
                                    board_avg_estimated_time)

    if board:
        labels = board.labels.all()

        for label in labels:
            if label.name:
                label_avg_estimated_time = label.avg_estimated_time()
                if label_avg_estimated_time > 0:
                    avg_times_chart.add(
                        u"{0} - {1}".format(board.name, label.name),
                        label_avg_estimated_time)

    chart = CachedChart.make(board=board,
                             uuid=chart_uuid,
                             svg=avg_times_chart.render(is_unicode=True))
    return chart.render_django_response()
Beispiel #14
0
def cumulative_card_value_evolution(request, board_id="all", day_step=1):
    if board_id != "all":
        board = get_user_boards(request.user).get(id=board_id)
    else:
        board = None

    if day_step is None:
        day_step = 1

    day_step = min(int(day_step), 30)
    return cards.cumulative_value_evolution(request.user, board, day_step)
Beispiel #15
0
def spent_time_by_day_of_the_week(request,
                                  member_id=None,
                                  week_of_year=None,
                                  board_id=None):
    user_boards = get_user_boards(request.user)
    if member_id is None:
        if user_is_member(request.user):
            member = request.user.member
        else:
            member = Member.objects.filter(boards__in=user_boards)[0]
    else:
        member = Member.objects.filter(boards__in=user_boards).distinct().get(
            id=member_id)

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

    y, w = week_of_year.split("W")
    week = Week(int(y), int(w))
    start_of_week = week.monday()
    end_of_week = week.sunday()

    chart_title = u"{0}'s spent time in week {1} ({2} - {3})".format(
        member.external_username, week_of_year,
        start_of_week.strftime("%Y-%m-%d"), end_of_week.strftime("%Y-%m-%d"))
    board = None
    if board_id:
        board = user_boards.get(id=board_id)
        chart_title += u" for board {0}".format(board.name)

    spent_time_chart = pygal.HorizontalBar(title=chart_title,
                                           legend_at_bottom=True,
                                           print_values=True,
                                           print_zeroes=False,
                                           human_readable=True)

    try:
        day = start_of_week
        while day <= end_of_week:
            member_spent_time = member.get_spent_time(day, board)
            spent_time_chart.add(u"{0}".format(day.strftime("%A")),
                                 member_spent_time)
            day += datetime.timedelta(days=1)
    except AssertionError:
        spent_time_chart.no_data_text = u"No developers for this board.\nCheck members' attributes."
        spent_time_chart.style = DefaultStyle(no_data_font_size=20)
        return spent_time_chart.render_django_response()

    return spent_time_chart.render_django_response()
Beispiel #16
0
def number_of_code_errors_per_loc(request,
                                  grouped_by,
                                  board_id,
                                  repository_id=None,
                                  language="python"):
    board = get_user_boards(request.user).get(id=board_id)
    repository = None
    if repository_id:
        repository = board.repositories.get(id=repository_id)
    if language is None:
        language = "python"
    return repositories.number_of_code_errors_per_loc(grouped_by, board,
                                                      repository, language)
Beispiel #17
0
def add_spent_estimated_time(request, board_id, card_id):
    if request.method != "POST":
        raise Http404

    member = request.user.member
    try:
        board = get_user_boards(request.user).get(id=board_id)
        card = board.cards.get(id=card_id)
    except (Board.DoesNotExist, Card.DoesNotExist) as e:
        raise Http404

    # Getting the date
    selected_date = request.POST.get("date")

    # Getting spent and estimated time
    spent_time = request.POST.get("spent_time")
    estimated_time = request.POST.get("estimated_time")

    # If the description is not present, get the name of tha card as description
    description = request.POST.get("description", card.name)
    if not description:
        description = card.name

    # Checking if spent time and estimated time floats
    if spent_time != "":
        try:
            spent_time = float(spent_time.replace(",", "."))
        except ValueError:
            raise Http404
    else:
        spent_time = None

    if estimated_time != "":
        try:
            estimated_time = float(estimated_time.replace(",", "."))
        except ValueError:
            raise Http404
    else:
        estimated_time = None

    if spent_time is None and estimated_time is None:
        raise Http404

    # Optional days ago parameter
    days_ago = None
    matches = re.match(r"^\-(?P<days_ago>\d+)$", selected_date)
    if matches:
        days_ago = int(matches.group("days_ago"))

    card.add_spent_estimated_time(member, spent_time, estimated_time, days_ago=days_ago, description=description)
    return HttpResponseRedirect(reverse("boards:view_card", args=(board_id, card_id)))
Beispiel #18
0
def download_attachment(request, board_id, card_id, attachment_id):
    if request.method != "GET":
        raise Http404
    try:
        board = get_user_boards(request.user).get(id=board_id)
        card = board.cards.get(id=card_id)
        attachment = card.attachments.get(id=attachment_id)
    except (Board.DoesNotExist, Card.DoesNotExist, CardAttachment.DoesNotExist) as e:
        raise Http404

    if not attachment.file:
        attachment.fetch_external_file()

    return HttpResponseRedirect(attachment.file.url)
Beispiel #19
0
def delete_comment(request, board_id, card_id, comment_id):
    if request.method != "POST":
        raise Http404
    member = request.user.member
    try:
        board = get_user_boards(request.user).get(id=board_id)
        card = board.cards.get(id=card_id)
        comment = card.comments.get(id=comment_id)
    except (Board.DoesNotExist, Card.DoesNotExist, CardComment.DoesNotExist) as e:
        raise Http404

    # Delete the comment
    card.delete_comment(member, comment)
    return HttpResponseRedirect(reverse("boards:view_card", args=(board_id, card_id)))
Beispiel #20
0
 def __init__(self, *args, **kwargs):
     super(MultiboardForm, self).__init__(*args, **kwargs)
     self.fields["description"].widget = CKEditorWidget()
     current_request = CrequestMiddleware.get_request()
     current_user = current_request.user
     # Available boards for this user
     self.fields["boards"].choices = [
         (board.id, board.name) for board in get_user_boards(current_user).filter(is_archived=False).order_by("name")
     ]
     # Members of a multiboard
     current_member = current_user.member
     self.fields["members"].choices = [
         (member.id, member.external_username) for member in current_member.team_mates
     ]
Beispiel #21
0
 def __init__(self, *args, **kwarsg):
     super(FilterForecastersForm, self).__init__(*args, **kwarsg)
     # Regression model choices
     self.fields["model"].choices = REGRESSION_MODELS
     # Board and members
     current_request = CrequestMiddleware.get_request()
     current_user = current_request.user
     boards = get_user_boards(current_user)
     members = Member.get_user_team_members(current_user)
     self.fields["board"].choices = [("", "All")] + [(board.id, board.name)
                                                     for board in boards]
     self.fields["member"].choices = [("", "None")] + [
         (member.id, member.external_username) for member in members
     ]
Beispiel #22
0
def spent_time(current_user, board=None):

    # Caching
    chart_uuid = "members.spent_time-{0}-{1}".format(
        current_user.id, board.id if board else "None")
    chart = CachedChart.get(board=board, uuid=chart_uuid)
    if chart:
        return chart

    chart_title = u"Spent time by member as of {0}".format(timezone.now())

    if board:
        chart_title += u" for board {0}".format(board.name)
        chart_title += u" (fetched on {0})".format(
            board.get_human_fetch_datetime())

    if board:
        boards = [board]
    else:
        boards = get_user_boards(current_user)

    spent_time_chart = pygal.Bar(title=chart_title,
                                 legend_at_bottom=True,
                                 print_values=False,
                                 print_zeroes=False,
                                 fill=False,
                                 margin=0,
                                 human_readable=True)

    members = Member.objects.filter(
        boards__in=boards).distinct().order_by("id")

    spent_times = DailySpentTime.objects.filter(board__in=boards)

    total_spent_time_sum = 0
    for member in members:
        member_spent_time_sum = spent_times.filter(member=member).aggregate(
            sum=Sum("spent_time"))["sum"]
        if member_spent_time_sum is not None and member_spent_time_sum > 0:
            spent_time_chart.add(member.external_username,
                                 member_spent_time_sum)
            total_spent_time_sum += member_spent_time_sum

    spent_time_chart.add("Total spent time", total_spent_time_sum)

    chart = CachedChart.make(board=board,
                             uuid=chart_uuid,
                             svg=spent_time_chart.render(is_unicode=True))
    return chart.render_django_response()
Beispiel #23
0
    def __init__(self, *args, **kwargs):
        super(NewUserForm, self).__init__(*args, **kwargs)
        self.fields["password1"] = forms.CharField(label=u"Password", widget=forms.PasswordInput(),
                                                   required=True)
        self.fields["password2"] = forms.CharField(label=u"Repeat password", widget=forms.PasswordInput(),
                                                   required=True)

        current_request = CrequestMiddleware.get_request()
        boards = get_user_boards(current_request.user).filter(is_archived=False).order_by("name")
        self.fields["boards"] = forms.MultipleChoiceField(
            label=u"Boards",
            choices=[(board.id, board.name) for board in boards],
            help_text=u"Boards this visitor will have access",
            required=False
        )
        self.fields["boards"].widget.attrs = {'size': boards.count()}
Beispiel #24
0
 def __init__(self, *args, **kwargs):
     super(EditUserForm, self).__init__(*args, **kwargs)
     self.fields["password1"].required = False
     self.fields["password1"].help_text = u"Keep this field blank if you don't want to change the password"
     self.fields["password2"].required = False
     self.fields["password2"].help_text = u"Keep this field blank if you don't want to change the password"
     current_request = CrequestMiddleware.get_request()
     boards = get_user_boards(current_request.user).filter(is_archived=False).order_by("name")
     self.fields["boards"] = forms.MultipleChoiceField(
         label=u"Boards",
         choices=[(board.id, board.name) for board in boards],
         initial=[board.id for board in self.instance.boards.all().order_by("name")],
         help_text=u"Boards this visitor will have access",
         required=False,
     )
     self.fields["boards"].widget.attrs={'size': boards.count()}
Beispiel #25
0
def new(request, board_id):
    member = request.user.member
    board = get_user_boards(request.user).get(id=board_id)
    workflow = Workflow(name=u"New workflow", board=board)

    if request.method == "POST":
        form = NewWorkflowForm(workflow, request.POST)

        if form.is_valid():
            form.save(commit=True)
            return HttpResponseRedirect(reverse("boards:view_workflows", args=(board_id,)))

    else:
        form = NewWorkflowForm(workflow)

    return render(request, "workflows/new.html", {"form": form, "board": board, "member": member})
Beispiel #26
0
def remove_requirement(request, board_id, card_id, requirement_id):
    if request.method != "DELETE":
        return JsonResponseMethodNotAllowed(
            {"message": "HTTP method not allowed."})

    member = request.user.member
    try:
        board = get_user_boards(request.user).get(id=board_id)
        card = board.cards.get(id=card_id)
        requirement = card.requirements.get(id=requirement_id)
    except (Board.DoesNotExist, Card.DoesNotExist) as e:
        return JsonResponseNotFound({"message": "Not found."})

    card.remove_requirement(member, requirement)
    serializer = Serializer(board=card.board)
    return JsonResponse(serializer.serialize_card(card))
Beispiel #27
0
def edit(request, board_id, workflow_id):
    member = request.user.member
    board = get_user_boards(request.user).get(id=board_id)
    workflow = board.workflows.get(id=workflow_id)

    if request.method == "POST":
        form = EditWorkflowForm(workflow, request.POST, instance=workflow)

        if form.is_valid():
            form.save(commit=True)
            return HttpResponseRedirect(reverse("boards:view_workflows", args=(board_id,)))

    else:
        form = EditWorkflowForm(workflow, instance=workflow)

    return render(request, "workflows/edit.html", {"form": form, "board": board, "workflow": workflow, "member": member})
Beispiel #28
0
def view_list(request, board_id):
    member = None
    if user_is_member(request.user):
        member = request.user.member
    board = get_user_boards(request.user).get(id=board_id)
    workflows = Workflow.objects.all().order_by("name")
    # Ordered workflow lists
    for workflow in workflows:
        workflow.ordered_lists = workflow.workflow_lists.all().order_by("order")
        workflow_card_reports = workflow.workflow_card_reports.all()
        workflow.avg_lead_time = avg(workflow_card_reports, "lead_time")
        workflow.std_dev_lead_time = std_dev(workflow_card_reports, "lead_time")
        workflow.avg_cycle_time = avg(workflow_card_reports, "cycle_time")
        workflow.std_dev_cycle_time = std_dev(workflow_card_reports, "cycle_time")

    replacements = {"board": board, "workflows": workflows, "member": member}
    return render(request, "workflows/list.html", replacements)
Beispiel #29
0
def remove_blocking_card(request, board_id, card_id, blocking_card_id):
    if request.method != "DELETE":
        return JsonResponseBadRequest(
            {"message": "Bad request: some parameters are missing."})

    member = request.user.member
    try:
        board = get_user_boards(request.user).get(id=board_id)
        card = board.cards.get(id=card_id)
        blocking_card = card.blocking_cards.exclude(id=card_id).get(
            id=blocking_card_id)
    except (Board.DoesNotExist, Card.DoesNotExist) as e:
        return JsonResponseNotFound({"message": "Not found."})

    card.remove_blocking_card(member, blocking_card)
    serializer = Serializer(board=card.board)
    return JsonResponse(serializer.serialize_card(card))
Beispiel #30
0
def create_default_labels(request, board_id):
    member = request.user.member

    # Only members with credentials can sync their boards
    if member.has_trello_profile:
        return HttpResponseRedirect(reverse("boards:view", args=(board_id, )))

    if request.method != "POST":
        return HttpResponseRedirect(reverse("boards:view", args=(board_id, )))

    board = get_user_boards(request.user).get(id=board_id)
    if board.labels.all().exists():
        return HttpResponseRedirect(reverse("boards:view", args=(board_id, )))

    Label.create_default_labels(board)

    return HttpResponseRedirect(
        reverse("boards:view_label_report", args=(board_id, )))