def get_context_data(self, **kwargs): context = super(BillableHours, self).get_context_data(**kwargs) entries = context['entries'] date_headers = context['date_headers'] data_map = self.get_hours_data(entries, date_headers) from_date = context['from_date'] to_date = context['to_date'] trunc = context['trunc'] kwargs = {trunc + 's': 1} # For relativedelta keys = sorted(data_map.keys()) data_list = [['Date', 'Billable', 'Non-billable']] for i in range(len(keys)): start = keys[i] start = start if start >= from_date else from_date end = start + relativedelta(**kwargs) - relativedelta(days=1) end = end if end <= to_date else to_date if start != end: label = ' - '.join([date_format_filter(d, 'M j') for d in (start, end)]) else: label = date_format_filter(start, 'M j') billable = data_map[keys[i]]['billable'] nonbillable = data_map[keys[i]]['nonbillable'] data_list.append([label, billable, nonbillable]) context.update({ 'data': json.dumps(data_list, cls=DecimalEncoder), }) return context
def date_filters(form_id, options=None, use_range=True): if not options: options = ('months', 'quarters', 'years') filters = {} date_format = DATE_FORM_FORMAT # Expected for dates used in code today = now().date() single_day = relativedelta(days=1) single_month = relativedelta(months=1) single_year = relativedelta(years=1) if 'months' in options: filters[_('Past 12 Months')] = [] from_date = today.replace(day=1) + single_month for __ in range(12): to_date = from_date from_date = to_date - single_month to_date = to_date - single_day filters[_('Past 12 Months')].append( ( date_format_filter(from_date, 'M Y'), # displayed from_date.strftime(date_format) if use_range else "", # used in code to_date.strftime(date_format) # used in code )) filters[_('Past 12 Months')].reverse() if 'years' in options: filters[_('Years')] = [] start = today.year - 3 for year in range(start, start + 4): from_date = datetime.datetime(year, 1, 1) to_date = from_date + single_year - single_day filters[_('Years')].append( ( str(from_date.year), from_date.strftime(date_format) if use_range else "", to_date.strftime(date_format) )) if 'quarters' in options: filters[_('Quarters (Calendar Year)')] = [] to_date = datetime.date(today.year - 1, 1, 1) - single_day for x in range(8): from_date = to_date + single_day to_date = from_date + relativedelta(months=3) - single_day filters[_('Quarters (Calendar Year)')].append( ( '%s %s' % ((x % 4) + 1, from_date.year), from_date.strftime(date_format) if use_range else "", to_date.strftime(date_format) )) return {'filters': filters, 'form_id': form_id}
def report_productivity(request): report = [] organize_by = None form = ProductivityReportForm(request.GET or None) if form.is_valid(): project = form.cleaned_data['project'] organize_by = form.cleaned_data['organize_by'] export = request.GET.get('export', False) actualsQ = Q(project=project, end_time__isnull=False) actuals = Entry.objects.filter(actualsQ) projections = ProjectHours.objects.filter(project=project) entry_count = actuals.count() + projections.count() if organize_by == 'week' and entry_count > 0: # Determine the project's time range. amin, amax, pmin, pmax = (None, None, None, None) if actuals.count() > 0: amin = list(actuals.aggregate(Min('start_time')).values())[0] amin = utils.get_week_start(amin).date() amax = list(actuals.aggregate(Max('start_time')).values())[0] amax = utils.get_week_start(amax).date() if projections.count() > 0: pmin = list(projections.aggregate(Min('week_start')).values())[0] pmax = list(projections.aggregate(Max('week_start')).values())[0] current = min(amin, pmin) if (amin and pmin) else (amin or pmin) latest = max(amax, pmax) if (amax and pmax) else (amax or pmax) # Report for each week during the project's time range. while current <= latest: next_week = current + relativedelta(days=7) actual_hours = actuals.filter(start_time__gte=current, start_time__lt=next_week) actual_hours = list(actual_hours.aggregate(Sum('hours')).values())[0] projected_hours = projections.filter( week_start__gte=current, week_start__lt=next_week) projected_hours = list(projected_hours.aggregate(Sum('hours')).values())[0] report.append([date_format_filter(current, 'M j, Y'), actual_hours or 0, projected_hours or 0]) current = next_week elif organize_by == 'user' and entry_count > 0: # Determine all users who worked on or were assigned to the # project. vals = ('user', 'user__first_name', 'user__last_name') ausers = list(actuals.values_list(*vals).distinct()) pusers = list(projections.values_list(*vals).distinct()) key = lambda x: (x[1] + x[2]).lower() # Sort by name users = sorted(list(set(ausers + pusers)), key=key) # Report for each user. for user in users: name = '{0} {1}'.format(user[1], user[2]) actual_hours = actuals.filter(user=user[0]) actual_hours = list(actual_hours.aggregate(Sum('hours')).values())[0] projected_hours = projections.filter(user=user[0]) projected_hours = list(projected_hours.aggregate(Sum('hours')).values())[0] report.append([name, actual_hours or 0, projected_hours or 0]) col_headers = [organize_by.title(), 'Worked Hours', 'Assigned Hours'] report.insert(0, col_headers) if export: response = HttpResponse(content_type='text/csv') filename = '{0}_productivity'.format(project.name) content_disp = 'attachment; filename={0}.csv'.format(filename) response['Content-Disposition'] = content_disp writer = csv.writer(response) for row in report: writer.writerow(row) return response return render(request, 'timepiece/reports/productivity.html', { 'form': form, 'report': json.dumps(report, cls=DecimalEncoder), 'type': organize_by or '', 'total_worked': sum([r[1] for r in report[1:]]), 'total_assigned': sum([r[2] for r in report[1:]]), })
def report_productivity(request): report = [] organize_by = None form = ProductivityReportForm(request.GET or None) if form.is_valid(): project = form.cleaned_data['project'] organize_by = form.cleaned_data['organize_by'] export = request.GET.get('export', False) actualsQ = Q(project=project, end_time__isnull=False) actuals = Entry.objects.filter(actualsQ) projections = ProjectHours.objects.filter(project=project) entry_count = actuals.count() + projections.count() if organize_by == 'week' and entry_count > 0: # Determine the project's time range. amin, amax, pmin, pmax = (None, None, None, None) if actuals.count() > 0: amin = list(actuals.aggregate(Min('start_time')).values())[0] amin = utils.get_week_start(amin).date() amax = list(actuals.aggregate(Max('start_time')).values())[0] amax = utils.get_week_start(amax).date() if projections.count() > 0: pmin = list(projections.aggregate(Min('week_start')).values())[0] pmax = list(projections.aggregate(Max('week_start')).values())[0] current = min(amin, pmin) if (amin and pmin) else (amin or pmin) latest = max(amax, pmax) if (amax and pmax) else (amax or pmax) # Report for each week during the project's time range. while current <= latest: next_week = current + relativedelta(days=7) actual_hours = actuals.filter(start_time__gte=current, start_time__lt=next_week) actual_hours = list(actual_hours.aggregate(Sum('hours')).values())[0] projected_hours = projections.filter( week_start__gte=current, week_start__lt=next_week) projected_hours = list(projected_hours.aggregate(Sum('hours')).values())[0] report.append([date_format_filter(current, 'M j, Y'), actual_hours or 0, projected_hours or 0]) current = next_week elif organize_by == 'user' and entry_count > 0: # Determine all users who worked on or were assigned to the # project. vals = ('user', 'user__first_name', 'user__last_name') ausers = list(actuals.values_list(*vals).distinct()) pusers = list(projections.values_list(*vals).distinct()) key = lambda x: (x[1] + x[2]).lower() # Sort by name users = sorted(list(set(ausers + pusers)), key=key) # Report for each user. for user in users: name = '{0} {1}'.format(user[1], user[2]) actual_hours = actuals.filter(user=user[0]) actual_hours = list(actual_hours.aggregate(Sum('hours')).values())[0] projected_hours = projections.filter(user=user[0]) projected_hours = list(projected_hours.aggregate(Sum('hours')).values())[0] report.append([name, actual_hours or 0, projected_hours or 0]) col_headers = [organize_by.title(), 'Worked Hours', 'Assigned Hours'] report.insert(0, col_headers) if export: response = HttpResponse(content_type='text/csv') filename = '{0}_productivity'.format(project.name) content_disp = 'attachment; filename={0}.csv'.format(filename) response['Content-Disposition'] = content_disp writer = csv.writer(response) for row in report: writer.writerow(row) return response return render(request, 'ems/reports/productivity.html', { 'form': form, 'report': json.dumps(report, cls=DecimalEncoder), 'type': organize_by or '', 'total_worked': sum([r[1] for r in report[1:]]), 'total_assigned': sum([r[2] for r in report[1:]]), })
def week_start(date): """Given a Python date/datetime object, return the starting day of that week as a date object formatted by the |date filter. """ return date_format_filter(utils.get_week_start(date))