def test_get(self): """Get without param gets entries for this week.""" response = self.client.get(reverse('dashboard')) self.assertEqual(response.status_code, 200) self.assertEqual(response.context['week_start'], utils.get_week_start().date()) self.assertEqual(response.context['week_end'], utils.get_week_start().date() + relativedelta(days=6))
def setUp(self): super(ProjectHoursListViewTestCase, self).setUp() self.past_week = utils.get_week_start(datetime.date(2012, 4, 1)).date() self.current_week = utils.get_week_start().date() for i in range(5): factories.ProjectHours(week_start=self.past_week, published=True) factories.ProjectHours(week_start=self.current_week, published=True) self.url = reverse('view_schedule') self.login_user(self.user) self.date_format = '%Y-%m-%d'
def dispatch(self, request, *args, **kwargs): # Since we use get param in multiple places, attach it to the class default_week = utils.get_week_start(datetime.date.today()).date() if request.method == 'GET': week_start_str = request.GET.get('week_start', '') else: week_start_str = request.POST.get('week_start', '') # Account for an empty string self.week_start = default_week if week_start_str == '' \ else utils.get_week_start(datetime.datetime.strptime( week_start_str, '%Y-%m-%d').date()) return super(ScheduleMixin, self).dispatch(request, *args, **kwargs)
def grouped_totals(entries): select = { "day": { "date": """DATE_TRUNC('day', end_time)""" }, "week": { "date": """DATE_TRUNC('week', end_time)""" }, } weekly = entries.extra(select=select["week"]).values('date', 'billable') weekly = weekly.annotate(hours=Sum('hours')).order_by('date') daily = entries.extra(select=select["day"]).values('date', 'project__name', 'billable') daily = daily.annotate(hours=Sum('hours')).order_by( 'date', 'project__name') weeks = {} for week, week_entries in groupby(weekly, lambda x: x['date']): if week is not None: week = add_timezone(week) weeks[week] = get_hours_summary(week_entries) days = [] last_week = None for day, day_entries in groupby(daily, lambda x: x['date']): week = get_week_start(day) if last_week and week > last_week: yield last_week, weeks.get(last_week, {}), days days = [] days.append((day, daily_summary(day_entries))) last_week = week yield week, weeks.get(week, {}), days
def test_no_entries(self): date = utils.get_week_start(datetime.date(2012, 3, 15)) data = { 'week_start': date.strftime('%Y-%m-%d'), 'submit': '', } response = self.client.get(self.url, data) self.assertEquals(len(response.context['projects']), 0) self.assertEquals(len(response.context['users']), 0)
def test_week_filter_midweek(self): """Filter corrects mid-week date to Monday of specified week.""" wednesday = datetime.date(2012, 7, 4) monday = utils.get_week_start(wednesday).date() data = { 'week_start': wednesday.strftime(self.date_format), 'submit': '', } response = self.client.get(self.url, data) self.assertEquals(response.context['week'].date(), monday)
def setUp(self): super(ProjectHoursEditTestCase, self).setUp() self.permission = Permission.objects.filter( codename='add_projecthours') self.manager = factories.User() self.manager.user_permissions = self.permission self.view_url = reverse('edit_schedule') self.ajax_url = reverse('ajax_schedule') self.week_start = utils.get_week_start(datetime.date.today()) self.next_week = self.week_start + relativedelta(days=7) self.future = self.week_start + relativedelta(days=14)
def setUp(self): self.today = datetime.date(2012, 11, 7) self.this_week = utils.get_week_start(self.today) self.next_week = self.this_week + relativedelta(days=7) self.user = factories.User() self.project = factories.Project() self.activity = factories.Activity() self.location = factories.Location() self.status = Entry.UNVERIFIED
def get_dates(self): today = datetime.date.today() day = today if 'week_start' in self.request.GET: param = self.request.GET.get('week_start') try: day = datetime.datetime.strptime(param, '%Y-%m-%d').date() except: pass week_start = utils.get_week_start(day) week_end = week_start + relativedelta(days=6) return today, week_start, week_end
def report_payroll_summary(request): date = timezone.now() - relativedelta(months=1) from_date = utils.get_month_start(date).date() to_date = from_date + relativedelta(months=1) year_month_form = PayrollSummaryReportForm(request.GET or None, initial={ 'month': from_date.month, 'year': from_date.year, }) if year_month_form.is_valid(): from_date, to_date = year_month_form.save() last_billable = utils.get_last_billable_day(from_date) projects = utils.get_setting('EMS_PAID_LEAVE_PROJECTS') weekQ = Q(end_time__gt=utils.get_week_start(from_date), end_time__lt=last_billable + relativedelta(days=1)) monthQ = Q(end_time__gt=from_date, end_time__lt=to_date) workQ = ~Q(project__in=projects.values()) statusQ = Q(status=Entry.INVOICED) | Q(status=Entry.APPROVED) # Weekly totals week_entries = Entry.objects.date_trunc('week').filter( weekQ, statusQ, workQ ) date_headers = generate_dates(from_date, last_billable, by='week') weekly_totals = list(get_project_totals(week_entries, date_headers, 'total', overtime=True)) # Monthly totals leave = Entry.objects.filter(monthQ, ~workQ) leave = leave.values('user', 'hours', 'project__name') extra_values = ('project__type__label',) month_entries = Entry.objects.date_trunc('month', extra_values) month_entries_valid = month_entries.filter(monthQ, statusQ, workQ) labels, monthly_totals = get_payroll_totals(month_entries_valid, leave) # Unapproved and unverified hours entries = Entry.objects.filter(monthQ).order_by() # No ordering user_values = ['user__pk', 'user__first_name', 'user__last_name'] unverified = entries.filter(status=Entry.UNVERIFIED, user__is_active=True) \ .values_list(*user_values).distinct() unapproved = entries.filter(status=Entry.VERIFIED) \ .values_list(*user_values).distinct() return render(request, 'ems/reports/payroll_summary.html', { 'from_date': from_date, 'year_month_form': year_month_form, 'date_headers': date_headers, 'weekly_totals': weekly_totals, 'monthly_totals': monthly_totals, 'unverified': unverified, 'unapproved': unapproved, 'labels': labels, })
def generate_dates(start=None, end=None, by='week'): if start: start = add_timezone(start) if end: end = add_timezone(end) if by == 'year': start = get_year_start(start) return rrule.rrule(rrule.YEARLY, dtstart=start, until=end) if by == 'month': start = get_month_start(start) return rrule.rrule(rrule.MONTHLY, dtstart=start, until=end) if by == 'week': start = get_week_start(start) return rrule.rrule(rrule.WEEKLY, dtstart=start, until=end, byweekday=0) if by == 'day': return rrule.rrule(rrule.DAILY, dtstart=start, until=end)
def setUp(self): self.today = datetime.date(2012, 11, 7) self.this_week = utils.get_week_start(self.today) self.next_week = self.this_week + relativedelta(days=7) get_params = {'week_start': self.this_week.strftime('%Y-%m-%d')} self.url = reverse('dashboard') + '?' + urlencode(get_params) self.user = factories.User() self.permission = Permission.objects.get(codename='can_clock_in') self.user.user_permissions.add(self.permission) self.login_user(self.user) self.project = factories.Project() self.activity = factories.Activity() self.location = factories.Location() self.status = Entry.UNVERIFIED
def find_start(self, **kwargs): """ Determine the starting point of the query using CLI keyword arguments """ week = kwargs.get('week', False) month = kwargs.get('month', False) year = kwargs.get('year', False) days = kwargs.get('days', 0) # If no flags are True, set to the beginning of last billing window # to assure we catch all recent violations start = timezone.now() - relativedelta(months=1, day=1) # Set the start date based on arguments provided through options if week: start = utils.get_week_start() if month: start = timezone.now() - relativedelta(day=1) if year: start = timezone.now() - relativedelta(day=1, month=1) if days: start = timezone.now() - relativedelta(days=days) start -= relativedelta(hour=0, minute=0, second=0, microsecond=0) return start
def _get_dates(): today = datetime.date.today() day = today week_start = utils.get_week_start(day) week_end = week_start + relativedelta(days=6) return today, week_start, week_end
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 clean_week_start(self): week_start = self.cleaned_data.get('week_start', None) return utils.get_week_start(week_start, False) if week_start else None
def get_week_window(day=None): """Returns (Monday, Sunday) of the requested week.""" start = get_week_start(day) return (start, start + relativedelta(days=6))
def view_user_timesheet(request, user_id, active_tab): # User can only view their own time sheet unless they have a permission. user = get_object_or_404(User, pk=user_id) has_perm = request.user.has_perm('entries.view_entry_summary') if not (has_perm or user.pk == request.user.pk): return HttpResponseForbidden('Forbidden') FormClass = UserYearMonthForm if has_perm else YearMonthForm form = FormClass(request.GET or None) if form.is_valid(): if has_perm: from_date, to_date, form_user = form.save() if form_user and request.GET.get('yearmonth', None): # Redirect to form_user's time sheet. # Do not use request.GET in urlencode to prevent redirect # loop caused by yearmonth parameter. url = reverse('view_user_timesheet', args=(form_user.pk, )) request_data = { 'month': from_date.month, 'year': from_date.year, 'user': form_user.pk, # Keep so that user appears in form. } url += '?{0}'.format(urlencode(request_data)) return HttpResponseRedirect(url) else: # User must be viewing their own time sheet; no redirect needed. from_date, to_date = form.save() from_date = utils.add_timezone(from_date) to_date = utils.add_timezone(to_date) else: # Default to showing this month. from_date = utils.get_month_start() to_date = from_date + relativedelta(months=1) entries_qs = Entry.objects.filter(user=user) month_qs = entries_qs.timespan(from_date, span='month') extra_values = ('start_time', 'end_time', 'comments', 'seconds_paused', 'id', 'location__name', 'project__name', 'activity__name', 'status') month_entries = month_qs.date_trunc('month', extra_values).order_by('start_time') # For grouped entries, back date up to the start of the week. first_week = utils.get_week_start(from_date) month_week = first_week + relativedelta(weeks=1) grouped_qs = entries_qs.timespan(first_week, to_date=to_date) intersection = grouped_qs.filter(start_time__lt=month_week, start_time__gte=from_date) # If the month of the first week starts in the previous # month and we dont have entries in that previous ISO # week, then update the first week to start at the first # of the actual month if not intersection and first_week.month < from_date.month: grouped_qs = entries_qs.timespan(from_date, to_date=to_date) totals = grouped_totals(grouped_qs) if month_entries else '' project_entries = month_qs.order_by().values('project__name').annotate( sum=Sum('hours')).order_by('-sum') summary = Entry.summary(user, from_date, to_date) show_approve = show_verify = False can_change = request.user.has_perm('entries.change_entry') can_approve = request.user.has_perm('entries.approve_timesheet') if can_change or can_approve or user == request.user: statuses = list(month_qs.values_list('status', flat=True)) total_statuses = len(statuses) unverified_count = statuses.count(Entry.UNVERIFIED) verified_count = statuses.count(Entry.VERIFIED) approved_count = statuses.count(Entry.APPROVED) if can_change or user == request.user: show_verify = unverified_count != 0 if can_approve: show_approve = all([ verified_count + approved_count == total_statuses, verified_count > 0, total_statuses != 0, ]) return render( request, 'ems/user/timesheet/view.html', { 'active_tab': active_tab or 'overview', 'year_month_form': form, 'from_date': from_date, 'to_date': to_date - relativedelta(days=1), 'show_verify': show_verify, 'show_approve': show_approve, 'timesheet_user': user, 'entries': month_entries, 'grouped_totals': totals, 'project_entries': project_entries, 'summary': summary, })
def save(self, *args, **kwargs): # Ensure that week_start is the Monday of a given week. self.week_start = utils.get_week_start(self.week_start) return super(ProjectHours, self).save(*args, **kwargs)