Beispiel #1
0
    def __init__(self, board, post_data=None, initial=None):
        super(WeekSummaryFilterForm, self).__init__(data=post_data, initial=initial)
        now = timezone.now()
        year = now.year

        working_start_date = board.get_working_start_date()
        working_end_date = board.get_working_end_date()
        if working_start_date and working_end_date:
            self.fields["year"].choices = [(year_i, year_i) for year_i in range(working_start_date.year, working_end_date.year+1)]

            if working_start_date.year == working_end_date.year:
                start_week = get_iso_week_of_year(working_start_date)
                end_week = get_iso_week_of_year(working_end_date)
                self.fields["week"].choices = [(week_i, week_i) for week_i in range(start_week, end_week+1)]
            else:
                self.fields["week"].choices = [(week_i, week_i) for week_i in range(1, 53 + 1)]
        else:
            self.fields["year"].choices = [(year_i, year_i) for year_i in range(year-100, year+101)]
            self.fields["week"].choices = [(week_i, week_i) for week_i in range(1, 53+1)]

        self.fields["member"].choices = [("all", "All")] +\
            [(member.id, member.external_username) for member in board.members.filter(is_developer=True)]

        if initial is None:
            self.initial["year"] = year
            self.initial["week"] = get_iso_week_of_year(now)
            self.initial["member"] = "all"
Beispiel #2
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 #3
0
    def add(board, member, date, card, comment, description, spent_time, estimated_time):
        # In case a uuid is passed, load the Member object
        if type(member) is str or type(member) is unicode:
            try:
                member = board.members.get(uuid=member)
            except ObjectDoesNotExist:
                return False

        weekday = date.strftime("%w")
        week_of_year = get_iso_week_of_year(date)
        day_of_year = date.strftime("%j")

        adjusted_spent_time = None

        # Convert spent_time to Decimal if is a number
        if spent_time is not None:
            spent_time = Decimal(spent_time)
            adjusted_spent_time = member.adjust_spent_time(spent_time, date)

        # Convert estimated_time to Decimal if is a number
        if estimated_time is not None:
            estimated_time = Decimal(estimated_time)
        elif spent_time is not None:
            estimated_time = Decimal(spent_time)

        # Compute difference between spent_time and estimated_time
        diff_time = None
        if spent_time is not None and estimated_time is not None:
            diff_time = Decimal(estimated_time) - Decimal(spent_time)

        # Creation of daily_spent_time
        daily_spent_time = DailySpentTime(board=board, member=member, card=card, comment=comment, uuid=comment.uuid,
                                          description=description,
                                          adjusted_spent_time=adjusted_spent_time,
                                          spent_time=spent_time,
                                          estimated_time=estimated_time,
                                          diff_time=diff_time,
                                          date=date, day_of_year=day_of_year, week_of_year=week_of_year,
                                          weekday=weekday)

        # Rate amount computation
        hourly_rate = board.get_date_hourly_rate(date)
        if hourly_rate:
            if daily_spent_time.rate_amount is None:
                daily_spent_time.rate_amount = 0
            daily_spent_time.rate_amount += hourly_rate.amount

        # Saving the daily spent time
        daily_spent_time.save()
        return spent_time
Beispiel #4
0
    def send_report(self, daily_spent_times, report_recipient, subject,
                    txt_template_path, html_template_path, csv_file_name):

        week = get_iso_week_of_year(self.date)
        start_week_date = start_of_week_of_year(week, self.date.year)
        end_week_date = end_of_week_of_year(week, self.date.year)
        replacements = {
            "start_week_date":
            start_week_date,
            "end_week_date":
            end_week_date,
            "date":
            self.date,
            "day":
            self.date.day,
            "week":
            week,
            "month":
            self.date.month,
            "year":
            self.date.year,
            "report_recipient":
            report_recipient,
            "boards":
            report_recipient.boards.all(),
            "developer_members":
            Member.objects.filter(
                is_developer=True,
                on_holidays=False).order_by("trello_member_profile__username"),
            "daily_spent_times":
            daily_spent_times,
        }

        txt_message = get_template(txt_template_path).render(replacements)
        html_message = get_template(html_template_path).render(replacements)

        csv_report = get_template('daily_spent_times/csv.txt').render(
            {"spent_times": daily_spent_times})

        message = EmailMultiAlternatives(subject, txt_message,
                                         settings.EMAIL_HOST_USER,
                                         [report_recipient.email])
        message.attach_alternative(html_message, "text/html")
        message.attach(csv_file_name, csv_report, 'text/csv')
        message.send()
Beispiel #5
0
    def factory_from_comment(comment):
        card = comment.card
        board = card.board
        spent_estimated_time = comment.get_spent_estimated_time_from_content()
        date = spent_estimated_time["date"]

        weekday = date.strftime("%w")
        week_of_year = get_iso_week_of_year(date)
        day_of_year = date.strftime("%j")

        spent_time = spent_estimated_time["spent_time"]
        estimated_time = spent_estimated_time["estimated_time"]

        # Set adjusted spent time
        adjusted_spent_time = None
        if spent_time is not None:
            spent_time = Decimal(spent_time)
            adjusted_spent_time = comment.author.adjust_spent_time(spent_time, date)

        # Convert estimated_time to Decimal if is a number
        if estimated_time is not None:
            estimated_time = Decimal(estimated_time)

        elif spent_time is not None:
            estimated_time = Decimal(spent_time)

        diff_time = estimated_time - spent_time

        rate_amount = None
        hourly_rate = board.get_date_hourly_rate(date)
        if spent_time is not None and hourly_rate is not None:
            rate_amount = spent_time * hourly_rate.amount

        daily_spent_time = DailySpentTime(
            uuid=comment.uuid, board=board, card=card, comment=comment,
            date=spent_estimated_time["date"], weekday=weekday, week_of_year=week_of_year, day_of_year=day_of_year,
            spent_time=spent_time, adjusted_spent_time=adjusted_spent_time,
            estimated_time=estimated_time, diff_time=diff_time,
            description=spent_estimated_time["description"],
            member=comment.author, rate_amount=rate_amount
        )
        return daily_spent_time
Beispiel #6
0
    def handle(self, *args, **options):
        self.date = super(Command, self).handle(*args, **options)
        date_help_text = 'Send the weekly report to the administrators for the week this date belongs to'

        start = time.time()

        week = get_iso_week_of_year(self.date)
        year = self.date.year

        week_start_date = Week(year, week).monday()
        week_end_date = Week(year, week).friday()

        daily_spent_times = DailySpentTime.objects.filter(date__gte=week_start_date, date__lte=week_end_date).\
            order_by("date", "member")

        subject = "[Djanban][Reports] Weekly report of {0}/W{1}".format(
            year, week)
        txt_template_path = 'reporter/emails/weekly_report.txt'
        html_template_path = 'reporter/emails/weekly_report.html'
        csv_file_name = 'spent_times-for-month-{0}W{1}.csv'.format(year, week)

        report_recipient = self.send_reports(daily_spent_times, subject,
                                             txt_template_path,
                                             html_template_path, csv_file_name)
        self.stdout.write(
            self.style.SUCCESS(
                u"Weekly reports sent to {0} administrators".format(
                    report_recipient.count())))

        end = time.time()
        elapsed_time = end - start

        self.stdout.write(
            self.style.SUCCESS(
                u"Weekly reports for week {0}/W{1} sent successfully to {2} in {3} s"
                .format(year, week, report_recipient.count(), elapsed_time)))
Beispiel #7
0
def _daily_spent_times_by_period(current_user,
                                 board=None,
                                 time_measurement="spent_time",
                                 operation="Avg",
                                 period="month"):

    # Caching
    chart_uuid = "labels._daily_spent_times_by_period-{0}-{1}-{2}-{3}".format(
        board.id if board else "user-{0}".format(current_user.id),
        time_measurement, operation, period)
    chart = CachedChart.get(board=board, uuid=chart_uuid)
    if chart:
        return chart

    daily_spent_time_filter = {"{0}__gt".format(time_measurement): 0}
    last_activity_datetime = timezone.now()
    if board:
        last_activity_datetime = board.last_activity_datetime
        daily_spent_time_filter["board"] = board

    if operation == "Avg":
        chart_title = u"Task average {1} as of {0}".format(
            last_activity_datetime, time_measurement.replace("_", " "))
        if board:
            chart_title += u" for board {0} (fetched on {1})".format(
                board.name, board.get_human_fetch_datetime())
    elif operation == "Count":
        chart_title = u"Tasks worked on as of {0}".format(
            last_activity_datetime)
        if board:
            chart_title += u" for board {0} (fetched on {1})".format(
                board.name, board.get_human_fetch_datetime())
    else:
        raise ValueError(
            u"Operation not valid only Avg and Count values are valid")

    period_measurement_chart = pygal.StackedBar(title=chart_title,
                                                legend_at_bottom=True,
                                                print_values=True,
                                                print_zeroes=False,
                                                x_label_rotation=45,
                                                human_readable=True)
    labels = []
    if board:
        labels = board.labels.all()

    end_date = DailySpentTime.objects.filter(
        **daily_spent_time_filter).aggregate(max_date=Max("date"))["max_date"]

    date_i = DailySpentTime.objects.filter(
        **daily_spent_time_filter).aggregate(min_date=Min("date"))["min_date"]

    if date_i is None or end_date is None:
        return period_measurement_chart.render_django_response()

    month_i = date_i.month
    week_i = get_iso_week_of_year(date_i)
    year_i = date_i.year

    if operation == "Avg":
        aggregation = Avg
    elif operation == "Count":
        aggregation = Count
    else:
        ValueError(u"Operation not valid only Avg and Count values are valid")

    measurement_titles = []
    measurement_values = []

    label_measurement_titles = {label.id: [] for label in labels}
    label_measurement_values = {label.id: [] for label in labels}

    end_loop = False
    while not end_loop:
        if period == "month":
            period_filter = {"date__month": month_i, "date__year": year_i}
            measurement_title = u"{0}-{1}".format(year_i, month_i)
            label_measurement_title_suffix = u"{0}-{1}".format(year_i, month_i)
            end_loop = datetime.datetime.strptime(
                '{0}-{1}-1'.format(year_i, month_i),
                '%Y-%m-%d').date() > end_date
        elif period == "week":
            period_filter = {"week_of_year": week_i, "date__year": year_i}
            measurement_title = u"{0}W{1}".format(year_i, week_i)
            label_measurement_title_suffix = u"{0}W{1}".format(year_i, week_i)
            end_loop = start_of_week_of_year(week=week_i,
                                             year=year_i) > end_date
        else:
            raise ValueError(
                u"Period {0} not valid. Only 'month' or 'week' is valid".
                format(period))

        period_times = DailySpentTime.objects.filter(**daily_spent_time_filter).\
            filter(**period_filter)

        period_measurement = period_times.aggregate(
            measurement=aggregation(time_measurement))["measurement"]
        # For each month that have some data, add it to the chart
        if period_measurement is not None and period_measurement > 0:
            measurement_titles.append(measurement_title)
            measurement_values.append(period_measurement)

            # For each label that has a name (i.e. it is being used) and has a value, store its measurement per label
            for label in labels:
                if label.name:
                    label_measurement = period_times.filter(card__labels=label).\
                                            aggregate(measurement=aggregation(time_measurement))["measurement"]
                    if label_measurement:
                        label_measurement_titles[label.id].append(
                            measurement_title)
                        label_measurement_values[label.id].append(
                            label_measurement)

        if period == "month":
            month_i += 1
            if month_i > 12:
                month_i = 1
                year_i += 1

        elif period == "week":
            week_i += 1
            if week_i > number_of_weeks_of_year(year_i):
                week_i = 1
                year_i += 1

    # Weeks there is any measurement
    period_measurement_chart.x_labels = measurement_titles
    period_measurement_chart.add("Tasks", measurement_values)

    # For each label that has any measurement, add its measurement to the chart
    for label in labels:
        if sum(label_measurement_values[label.id]) > 0:
            period_measurement_chart.add(label.name,
                                         label_measurement_values[label.id])

    chart = CachedChart.make(
        board=board,
        uuid=chart_uuid,
        svg=period_measurement_chart.render(is_unicode=True))
    return chart.render_django_response()
Beispiel #8
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)
Beispiel #9
0
def spent_time_by_week(current_user, week_of_year=None, board=None):
    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_)

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

    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"Spent time in week {0} ({1} - {2})".format(
        week_of_year, start_of_week.strftime("%Y-%m-%d"),
        end_of_week.strftime("%Y-%m-%d"))
    if board:
        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)

    report_filter = {"date__year": y, "week_of_year": w}
    if board:
        report_filter["board_id"] = board.id

    team_spent_time = 0

    if board is None:
        boards = get_user_boards(current_user)
    else:
        boards = [board]
    members = Member.objects.filter(
        boards__in=boards, is_developer=True).distinct().order_by("id")
    for member in members:
        member_name = member.external_username
        daily_spent_times = member.daily_spent_times.filter(**report_filter)
        spent_time = daily_spent_times.aggregate(
            Sum("spent_time"))["spent_time__sum"]
        if spent_time is None:
            spent_time = 0
        team_spent_time += spent_time

        if spent_time > 0:
            spent_time_chart.add(u"{0}'s spent time".format(member_name),
                                 spent_time)

    spent_time_chart.add(u"Team spent time", team_spent_time)

    chart = CachedChart.make(board=board,
                             uuid=chart_uuid,
                             svg=spent_time_chart.render(is_unicode=True))
    return chart.render_django_response()
Beispiel #10
0
def spent_time_by_week_evolution(board, show_interruptions=False):

    # Caching
    chart_uuid = "members.spent_time_by_week_evolution-{0}-{1}".format(
        board.id, "with_interruptions"
        if show_interruptions else "without_interruptions")
    chart = CachedChart.get(board=board, uuid=chart_uuid)
    if chart:
        return chart

    chart_title = u"Evolution of each member's spent time by week"
    if show_interruptions:
        chart_title += u", including interruptions suffered by the team, "
    chart_title += u" for board {0} (fetched on {1})".format(
        board.name, board.get_human_fetch_datetime())

    evolution_chart = pygal.Line(title=chart_title,
                                 legend_at_bottom=True,
                                 print_values=False,
                                 print_zeroes=False,
                                 fill=False,
                                 human_readable=True,
                                 x_label_rotation=45)

    start_working_date = board.get_working_start_date()
    end_working_date = board.get_working_end_date()
    if start_working_date is None or end_working_date is None:
        return evolution_chart.render_django_response()

    start_week = get_iso_week_of_year(start_working_date)
    end_week = get_iso_week_of_year(end_working_date)

    members = board.members.filter(is_developer=True).order_by("id")

    member_values = {member.id: [] for member in members}
    member_values["all"] = []

    interruptions = []

    x_labels = []

    week_i = copy.deepcopy(start_week)
    year_i = start_working_date.year
    while year_i < end_working_date.year or (year_i == end_working_date.year
                                             and week_i < end_week):

        week_i_start_date = Week(year_i, week_i).monday()
        week_i_end_date = Week(year_i, week_i).sunday()

        there_is_data = board.daily_spent_times.filter(
            date__year=year_i, week_of_year=week_i).exists()
        if there_is_data:
            x_labels.append(u"{0}W{1}".format(year_i, week_i))

            team_spent_time = 0
            for member in members:
                daily_spent_times = member.daily_spent_times.filter(
                    board=board, date__year=year_i, week_of_year=week_i)
                spent_time = daily_spent_times.aggregate(
                    Sum("spent_time"))["spent_time__sum"]
                if spent_time is None:
                    spent_time = 0
                team_spent_time += spent_time

                if spent_time > 0:
                    member_values[member.id].append(spent_time)

            member_values["all"].append(team_spent_time)

            if show_interruptions:
                num_interruptions = Interruption.objects.filter(
                    datetime__year=year_i,
                    datetime__date__gte=week_i_start_date,
                    datetime__date__lte=week_i_end_date).count()
                if num_interruptions > 0:
                    interruptions.append(num_interruptions)

        week_i += 1
        if week_i > number_of_weeks_of_year(year_i):
            week_i = 1
            year_i += 1

    evolution_chart.x_labels = x_labels
    for member in members:
        evolution_chart.add(member.external_username, member_values[member.id])

    evolution_chart.add("All members", member_values["all"])
    if show_interruptions:
        evolution_chart.add("Interruptions", interruptions)

    chart = CachedChart.make(board=board,
                             uuid=chart_uuid,
                             svg=evolution_chart.render(is_unicode=True))
    return chart.render_django_response()