Пример #1
0
 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))
Пример #2
0
 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'
Пример #3
0
    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)
Пример #4
0
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
Пример #5
0
 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)
Пример #6
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)
Пример #7
0
 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)
Пример #8
0
    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
Пример #9
0
 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
Пример #10
0
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,
    })
Пример #11
0
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)
Пример #12
0
    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
Пример #13
0
 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
Пример #14
0
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
Пример #15
0
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:]]),
    })
Пример #16
0
 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
Пример #17
0
def get_week_window(day=None):
    """Returns (Monday, Sunday) of the requested week."""
    start = get_week_start(day)
    return (start, start + relativedelta(days=6))
Пример #18
0
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,
        })
Пример #19
0
 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)