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
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)
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)
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()
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")
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))
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
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)
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)
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
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()
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())
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()
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)
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()
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)
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)))
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)
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)))
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 ]
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 ]
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()
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()}
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()}
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})
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))
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})
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)
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))
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, )))