Esempio n. 1
0
 def get_context_data(self, **kwargs):
     context = super(ProjectTimesheet, self).get_context_data(**kwargs)
     project = self.object
     year_month_form = YearMonthForm(self.request.GET or None)
     if self.request.GET and year_month_form.is_valid():
         from_date, to_date = year_month_form.save()
     else:
         date = utils.add_timezone(datetime.datetime.today())
         from_date = utils.get_month_start(date).date()
         to_date = from_date + relativedelta(months=1)
     entries_qs = Entry.objects
     entries_qs = entries_qs.timespan(from_date,
                                      span='month').filter(project=project)
     extra_values = ('start_time', 'end_time', 'comments', 'seconds_paused',
                     'id', 'location__name', 'project__name',
                     'activity__name', 'status')
     month_entries = entries_qs.date_trunc('month', extra_values)
     total = entries_qs.aggregate(hours=Sum('hours'))['hours']
     user_entries = entries_qs.order_by().values(
         'user__first_name',
         'user__last_name').annotate(sum=Sum('hours')).order_by('-sum')
     activity_entries = entries_qs.order_by().values(
         'activity__name').annotate(sum=Sum('hours')).order_by('-sum')
     context.update({
         'project': project,
         'year_month_form': year_month_form,
         'from_date': from_date,
         'to_date': to_date - relativedelta(days=1),
         'entries': month_entries,
         'total': total,
         'user_entries': user_entries,
         'activity_entries': activity_entries,
     })
     return context
Esempio n. 2
0
def invoice_projects(request):
    to_date = utils.get_month_start(datetime.datetime.today()).date()
    from_date = None
    defaults = {
        'to_date': (to_date - relativedelta(days=1)).strftime('%m/%d/%Y'),
    }
    date_form = timepiece_forms.DateForm(request.GET or defaults)
    if request.GET and date_form.is_valid():
        from_date, to_date = date_form.save()
    datesQ = Q()
    datesQ &= Q(end_time__gte=from_date) if from_date else Q()
    datesQ &= Q(end_time__lt=to_date) if to_date else Q()
    entries = timepiece.Entry.objects.filter(datesQ)
    project_totals = entries.filter(
        status='approved',
        project__type__billable=True,
        project__status__billable=True).values(
            'project__type__pk', 'project__type__label', 'project__name',
            'hours', 'project__pk', 'status',
            'project__status__label').annotate(s=Sum('hours')).order_by(
                'project__type__label', 'project__name', 'status')
    return render_to_response(
        'timepiece/time-sheet/invoice/make_invoice.html', {
            'date_form': date_form,
            'project_totals': project_totals if to_date else [],
            'to_date': to_date - relativedelta(days=1) if to_date else '',
            'from_date': from_date,
        },
        context_instance=RequestContext(request))
Esempio n. 3
0
    def clean(self):
        """
        If we're not editing the active entry, ensure that this entry doesn't
        conflict with or come after the active entry.
        """
        active = utils.get_active_entry(self.user)
        start_time = self.cleaned_data.get('start_time', None)
        end = self.cleaned_data.get('end', None)
        if (end != None and active != None):
            end_time = datetime.datetime.combine(active.start_time.date(), end)
        elif (end != None and active == None):
            end_time = datetime.datetime.combine(start_time.date(), end)
        else:
            end_time = None
        if active and active.pk != self.instance.pk:
            if (start_time and start_time > active.start_time) or \
                    (end_time and end_time > active.start_time):
                raise forms.ValidationError(
                    'The start time or end time conflict with the active '
                    'entry: {project} starting at '
                    '{start_time}.'.format(
                        project=active.project,
                        start_time=active.start_time.strftime('%H:%M:%S'),
                    ))

        month_start = utils.get_month_start(start_time)
        next_month = month_start + relativedelta(months=1)
        entries = self.instance.user.timepiece_entries.filter(
            start_time__gte=month_start, end_time__lt=next_month)
        entry = self.instance

        return self.cleaned_data
Esempio n. 4
0
def invoice_projects(request):
    to_date = utils.get_month_start(datetime.datetime.today()).date()
    from_date = None
    defaults = {
        'to_date': (to_date - relativedelta(days=1)).strftime('%m/%d/%Y'),
    }
    date_form = timepiece_forms.DateForm(request.GET or defaults)
    if request.GET and date_form.is_valid():
        from_date, to_date = date_form.save()
    datesQ = Q()
    datesQ &= Q(end_time__gte=from_date)  if from_date else Q()
    datesQ &= Q(end_time__lt=to_date)  if to_date else Q()
    entries = timepiece.Entry.objects.filter(datesQ)
    project_totals = entries.filter(status='approved',
        project__type__billable=True, project__status__billable=True).values(
        'project__type__pk', 'project__type__label', 'project__name', 'hours',
        'project__pk', 'status', 'project__status__label'
    ).annotate(s=Sum('hours')).order_by('project__type__label',
                                        'project__name', 'status')
    return render_to_response(
        'timepiece/time-sheet/invoice/make_invoice.html', {
        'date_form': date_form,
        'project_totals': project_totals if to_date else [],
        'to_date': to_date - relativedelta(days=1) if to_date else '',
        'from_date': from_date,
    }, context_instance=RequestContext(request))
Esempio n. 5
0
 def get_context_data(self, **kwargs):
     context = super(ProjectTimesheet, self).get_context_data(**kwargs)
     project = self.object
     year_month_form = timepiece_forms.YearMonthForm(self.request.GET
                                                     or None)
     if self.request.GET and year_month_form.is_valid():
         from_date, to_date, user = year_month_form.save()
     else:
         from_date = utils.get_month_start(datetime.datetime.today()).date()
         to_date = from_date + relativedelta(months=1)
     entries_qs = timepiece.Entry.objects
     entries_qs = entries_qs.timespan(from_date,
                                      span='month').filter(project=project)
     month_entries = entries_qs.date_trunc('month',
                                           True).order_by('start_time')
     total = entries_qs.aggregate(hours=Sum('hours'))['hours']
     user_entries = entries_qs.order_by().values(
         'user__first_name',
         'user__last_name').annotate(sum=Sum('hours')).order_by('-sum')
     activity_entries = entries_qs.order_by().values(
         'activity__name').annotate(sum=Sum('hours')).order_by('-sum')
     return {
         'project': project,
         'year_month_form': year_month_form,
         'from_date': from_date,
         'to_date': to_date - datetime.timedelta(days=1),
         'entries': month_entries,
         'total': total,
         'user_entries': user_entries,
         'activity_entries': activity_entries,
     }
Esempio n. 6
0
def hourly_report(request, date_form, from_date, to_date, status, activity):
    if not from_date:
        from_date = utils.get_month_start(datetime.datetime.today()).date()
    if not to_date:
        to_date = from_date + relativedelta(months=1)
    header_to = to_date - relativedelta(days=1)
    trunc = timepiece_forms.ProjectFiltersForm.DEFAULT_TRUNC
    query = Q(end_time__gt=utils.get_week_start(from_date),
              end_time__lt=to_date)
    if 'ok' in request.GET or 'export' in request.GET:
        form = timepiece_forms.ProjectFiltersForm(request.GET)
        if form.is_valid():
            trunc = form.cleaned_data['trunc']
            if not form.cleaned_data['paid_leave']:
                projects = getattr(settings, 'TIMEPIECE_PROJECTS', {})
                query &= ~Q(project__in=projects.values())
            if form.cleaned_data['pj_select']:
                query &= Q(project__in=form.cleaned_data['pj_select'])
    else:
        form = timepiece_forms.ProjectFiltersForm()
    hour_type = form.get_hour_type()
    entries = timepiece.Entry.objects.date_trunc(trunc).filter(query)
    date_headers = utils.generate_dates(from_date, header_to, by=trunc)
    project_totals = utils.project_totals(entries, date_headers, hour_type,
                                          total_column=True) if entries else ''
    if not request.GET.get('export', False):
        return {
            'date_form': date_form,
            'from_date': from_date,
            'date_headers': date_headers,
            'pj_filters': form,
            'trunc': trunc,
            'project_totals': project_totals,
        }
    else:
        from_date_str = from_date.strftime('%m-%d')
        to_date_str = to_date.strftime('%m-%d')
        response = HttpResponse(mimetype='text/csv')
        response['Content-Disposition'] = \
            'attachment; filename="%s_hours_%s_to_%s_by_%s.csv"' % (
            hour_type, from_date_str, to_date_str, trunc)
        writer = csv.writer(response)
        headers = ['Name']
        headers.extend([date.strftime('%m/%d/%Y') for date in date_headers])
        headers.append('Total')
        writer.writerow(headers)
        for rows, totals in project_totals:
            for name, hours in rows:
                data = [name]
                data.extend(hours)
                writer.writerow(data)
            total = ['Totals']
            total.extend(totals)
            writer.writerow(total)
        return response
Esempio n. 7
0
def hourly_report(request, date_form, from_date, to_date, status, activity):
    if not from_date:
        from_date = utils.get_month_start(datetime.datetime.today()).date()
    if not to_date:
        to_date = from_date + relativedelta(months=1)
    header_to = to_date - relativedelta(days=1)
    trunc = timepiece_forms.ProjectFiltersForm.DEFAULT_TRUNC
    query = Q(end_time__gt=utils.get_week_start(from_date),
              end_time__lt=to_date)
    if 'ok' in request.GET or 'export' in request.GET:
        form = timepiece_forms.ProjectFiltersForm(request.GET)
        if form.is_valid():
            trunc = form.cleaned_data['trunc']
            if not form.cleaned_data['paid_leave']:
                projects = getattr(settings, 'TIMEPIECE_PROJECTS', {})
                query &= ~Q(project__in=projects.values())
            if form.cleaned_data['pj_select']:
                query &= Q(project__in=form.cleaned_data['pj_select'])
    else:
        form = timepiece_forms.ProjectFiltersForm()
    hour_type = form.get_hour_type()
    entries = timepiece.Entry.objects.date_trunc(trunc).filter(query)
    date_headers = utils.generate_dates(from_date, header_to, by=trunc)
    project_totals = utils.project_totals(
        entries, date_headers, hour_type, total_column=True) if entries else ''
    if not request.GET.get('export', False):
        return {
            'date_form': date_form,
            'from_date': from_date,
            'date_headers': date_headers,
            'pj_filters': form,
            'trunc': trunc,
            'project_totals': project_totals,
        }
    else:
        from_date_str = from_date.strftime('%m-%d')
        to_date_str = to_date.strftime('%m-%d')
        response = HttpResponse(mimetype='text/csv')
        response['Content-Disposition'] = \
            'attachment; filename="%s_hours_%s_to_%s_by_%s.csv"' % (
            hour_type, from_date_str, to_date_str, trunc)
        writer = csv.writer(response)
        headers = ['Name']
        headers.extend([date.strftime('%m/%d/%Y') for date in date_headers])
        headers.append('Total')
        writer.writerow(headers)
        for rows, totals in project_totals:
            for name, hours in rows:
                data = [name]
                data.extend(hours)
                writer.writerow(data)
            total = ['Totals']
            total.extend(totals)
            writer.writerow(total)
        return response
Esempio n. 8
0
 def  test_month_start(self):
     """ Test that any day returns the first day of the month"""
     days = [datetime.date(2011, 1, 1),
             datetime.date(2011, 1, 16),
             datetime.date(2011, 1, 17),
             datetime.date(2011, 1, 22),
             datetime.date(2011, 1, 31),
             ]
     for day in days:
         self.assertEqual(utils.get_month_start(day),
                          datetime.date(2011, 1, 1))
Esempio n. 9
0
    def __init__(self, *args, **kwargs):
        super(OutstandingHoursFilterForm, self).__init__(*args, **kwargs)

        # Check all statuses by default.
        statuses = Attribute.statuses.all()
        self.fields['statuses'].queryset = statuses
        self.fields['statuses'].initial = statuses

        month_start = utils.get_month_start().date()
        self.fields['to_date'].required = True
        self.fields['to_date'].initial = month_start - relativedelta(days=1)
        self.fields['from_date'].initial = None
Esempio n. 10
0
 def  test_month_start(self):
     """ Test that any day returns the first day of the month"""
     days = [
         datetime.date(2011, 1, 1),
         datetime.date(2011, 1, 16),
         datetime.date(2011, 1, 17),
         datetime.date(2011, 1, 22),
         datetime.date(2011, 1, 31),
     ]
     date = datetime.datetime(2011, 1, 1, tzinfo=timezone.get_current_timezone())
     for day in days:
         self.assertEqual(utils.get_month_start(day), date)
Esempio n. 11
0
    def __init__(self, *args, **kwargs):
        super(OutstandingHoursFilterForm, self).__init__(*args, **kwargs)

        # Check all statuses by default.
        statuses = Attribute.statuses.all()
        self.fields['statuses'].queryset = statuses
        self.fields['statuses'].initial = statuses

        month_start = utils.get_month_start().date()
        self.fields['to_date'].required = True
        self.fields['to_date'].initial = month_start - relativedelta(days=1)
        self.fields['from_date'].initial = None
Esempio n. 12
0
 def test_month_start(self):
     """ Test that any day returns the first day of the month"""
     days = [
         datetime.date(2011, 1, 1),
         datetime.date(2011, 1, 16),
         datetime.date(2011, 1, 17),
         datetime.date(2011, 1, 22),
         datetime.date(2011, 1, 31),
     ]
     for day in days:
         self.assertEqual(utils.get_month_start(day),
                          datetime.date(2011, 1, 1))
Esempio n. 13
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('TIMEPIECE_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).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, 'timepiece/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,
        })
Esempio n. 14
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('TIMEPIECE_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, 'timepiece/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,
    })
Esempio n. 15
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)
Esempio n. 16
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)
Esempio n. 17
0
def payroll_summary(request):
    year_month_form = timepiece_forms.YearMonthForm(request.GET or None)
    if request.GET and year_month_form.is_valid():
        from_date, to_date, user = year_month_form.save()
    else:
        from_date = utils.get_month_start(datetime.datetime.today()).date()
        to_date = from_date + relativedelta(months=1)
    last_billable = utils.get_last_billable_day(from_date)
    projects = getattr(settings, 'TIMEPIECE_PROJECTS', {})
    weekQ = Q(end_time__gt=utils.get_week_start(from_date),
              end_time__lt=last_billable + datetime.timedelta(days=1))
    monthQ = Q(end_time__gt=from_date, end_time__lt=to_date)
    workQ = ~Q(project__in=projects.values())
    statusQ = Q(status='invoiced') | Q(status='approved')
    # Weekly totals
    week_entries = timepiece.Entry.objects.date_trunc('week')
    week_entries = week_entries.filter(weekQ, statusQ, workQ)
    date_headers = utils.generate_dates(from_date, last_billable, by='week')
    weekly_totals = list(
        utils.project_totals(week_entries,
                             date_headers,
                             'total',
                             overtime=True))
    # Monthly totals
    leave = timepiece.Entry.objects.filter(monthQ, ~workQ).values(
        'user', 'hours', 'project__name')
    month_entries = timepiece.Entry.objects.date_trunc('month')
    month_entries_valid = month_entries.filter(monthQ, statusQ, workQ)
    monthly_totals = list(
        utils.payroll_totals(month_entries_valid, from_date, leave))
    # Unapproved and unverified hours
    entries = timepiece.Entry.objects.filter(monthQ)
    user_values = ['user__pk', 'user__first_name', 'user__last_name']
    unverified = entries.filter(monthQ,
                                status='unverified',
                                user__is_active=True)
    unapproved = entries.filter(monthQ, status='verified')
    return {
        'from_date': from_date,
        'year_month_form': year_month_form,
        'date_headers': date_headers,
        'weekly_totals': weekly_totals,
        'monthly_totals': monthly_totals,
        'unverified': unverified.values_list(*user_values).distinct(),
        'unapproved': unapproved.values_list(*user_values).distinct(),
    }
Esempio n. 18
0
    def get_context_data(self, **kwargs):
        context = super(ProjectTimesheet, self).get_context_data(**kwargs)
        project = self.object
        year_month_form = YearMonthForm(self.request.GET or None)
        if self.request.GET and year_month_form.is_valid():
            from_date, to_date = year_month_form.save()
        else:
            date = utils.add_timezone(datetime.datetime.today())
            from_date = utils.get_month_start(date).date()
            to_date = from_date + relativedelta(months=1)
        entries_qs = Entry.objects
        entries_qs = entries_qs.timespan(from_date, span='month').filter(
            project=project
        )
        extra_values = ('start_time', 'end_time', 'comments', 'seconds_paused',
                        'id', 'location__name', 'project__name',
                        'activity__name', 'status')

        month_entries = entries_qs.date_trunc('month', extra_values).order_by('start_time')
        if month_entries:
            format_totals(month_entries, "hours")

        total = entries_qs.aggregate(hours=Sum('hours'))['hours']
        if total:
            total = "{0:.2f}".format(total)
        user_entries = entries_qs.order_by().values('user__first_name', 'user__last_name')
        user_entries = user_entries.annotate(sum=Sum('hours')).order_by('-sum')
        if user_entries:
            format_totals(user_entries)
        activity_entries = entries_qs.order_by().values('activity__name')
        activity_entries = activity_entries.annotate(sum=Sum('hours')).order_by('-sum')
        if activity_entries:
            format_totals(activity_entries)

        context.update({
            'project': project,
            'year_month_form': year_month_form,
            'from_date': from_date,
            'to_date': to_date - relativedelta(days=1),
            'entries': month_entries,
            'total': total,
            'user_entries': user_entries,
            'activity_entries': activity_entries,
        })
        return context
Esempio n. 19
0
def payroll_summary(request):
    year_month_form = timepiece_forms.YearMonthForm(request.GET or None)
    if request.GET and year_month_form.is_valid():
        from_date, to_date = year_month_form.save()
    else:
        from_date = utils.get_month_start(datetime.datetime.today()).date()
        to_date = from_date + relativedelta(months=1)
    last_billable = utils.get_last_billable_day(from_date)
    projects = getattr(settings, 'TIMEPIECE_PROJECTS', {})
    weekQ = Q(end_time__gt=utils.get_week_start(from_date),
              end_time__lt=last_billable + datetime.timedelta(days=1))
    monthQ = Q(end_time__gt=from_date, end_time__lt=to_date)
    workQ = ~Q(project__in=projects.values())
    statusQ = Q(status='invoiced') | Q(status='approved')
    # Weekly totals
    week_entries = timepiece.Entry.objects.date_trunc('week')
    week_entries = week_entries.filter(weekQ, statusQ, workQ)
    date_headers = utils.generate_dates(from_date, last_billable, by='week')
    weekly_totals = list(utils.project_totals(week_entries, date_headers,
                                              'total', overtime=True))
    # Monthly totals
    leave = timepiece.Entry.objects.filter(monthQ, ~workQ
                                  ).values('user', 'hours', 'project__name')
    month_entries = timepiece.Entry.objects.date_trunc('month')
    month_entries_valid = month_entries.filter(monthQ, statusQ, workQ)
    monthly_totals = list(utils.payroll_totals(month_entries_valid, from_date,
                                               leave))
    # Unapproved and unverified hours
    entries = timepiece.Entry.objects.filter(monthQ)
    user_values = ['user__pk', 'user__first_name', 'user__last_name']
    unverified = entries.filter(monthQ, status='unverified',
                                user__is_active=True)
    unapproved = entries.filter(monthQ, status='verified')
    return {
        'from_date': from_date,
        'year_month_form': year_month_form,
        'date_headers': date_headers,
        'weekly_totals': weekly_totals,
        'monthly_totals': monthly_totals,
        'unverified': unverified.values_list(*user_values).distinct(),
        'unapproved': unapproved.values_list(*user_values).distinct(),
    }
Esempio n. 20
0
    def clean(self):
        """
        If we're not editing the active entry, ensure that this entry doesn't
        conflict with or come after the active entry.
        """
        active = utils.get_active_entry(self.user)
        start_time = self.cleaned_data.get('start_time', None)
        end_time = self.cleaned_data.get('end_time', None)

        if active and active.pk != self.instance.pk:
            if (start_time and start_time > active.start_time) or \
                    (end_time and end_time > active.start_time):
                raise forms.ValidationError(
                    'The start time or end time conflict with the active '
                    'entry: {activity} on {project} starting at '
                    '{start_time}.'.format(
                        project=active.project,
                        activity=active.activity,
                        start_time=active.start_time.strftime('%H:%M:%S'),
                    ))

        month_start = utils.get_month_start(start_time)
        next_month = month_start + relativedelta(months=1)
        entries = self.instance.user.timepiece_entries.filter(
            Q(status=Entry.APPROVED) | Q(status=Entry.INVOICED),
            start_time__gte=month_start,
            end_time__lt=next_month)
        entry = self.instance

        if not self.acting_user.is_superuser:
            if (entries.exists() and not entry.id
                    or entry.id and entry.status == Entry.INVOICED):
                message = 'You cannot add/edit entries after a timesheet has been ' \
                    'approved or invoiced. Please correct the start and end times.'
                raise forms.ValidationError(message)

        return self.cleaned_data
Esempio n. 21
0
    def clean(self):
        """
        If we're not editing the active entry, ensure that this entry doesn't
        conflict with or come after the active entry.
        """
        active = utils.get_active_entry(self.user)
        start_time = self.cleaned_data.get('start_time', None)
        end_time = self.cleaned_data.get('end_time', None)

        if active and active.pk != self.instance.pk:
            if (start_time and start_time > active.start_time) or \
                    (end_time and end_time > active.start_time):
                raise forms.ValidationError(
                    'The start time or end time conflict with the active '
                    'entry: {activity} on {project} starting at '
                    '{start_time}.'.format(
                        project=active.project,
                        activity=active.activity,
                        start_time=active.start_time.strftime('%H:%M:%S'),
                    ))

        month_start = utils.get_month_start(start_time)
        next_month = month_start + relativedelta(months=1)
        entries = self.instance.user.timepiece_entries.filter(
            Q(status=Entry.APPROVED) | Q(status=Entry.INVOICED),
            start_time__gte=month_start,
            end_time__lt=next_month
        )
        entry = self.instance

        if not self.acting_user.is_superuser:
            if (entries.exists() and not entry.id or entry.id and entry.status == Entry.INVOICED):
                message = 'You cannot add/edit entries after a timesheet has been ' \
                    'approved or invoiced. Please correct the start and end times.'
                raise forms.ValidationError(message)

        return self.cleaned_data
Esempio n. 22
0
 def get_context_data(self, **kwargs):
     context = super(ProjectTimesheet, self).get_context_data(**kwargs)
     project = self.object
     year_month_form = timepiece_forms.YearMonthForm(self.request.GET or
                                                     None)
     if self.request.GET and year_month_form.is_valid():
         from_date, to_date = year_month_form.save()
     else:
         from_date = utils.get_month_start(datetime.datetime.today()).date()
         to_date = from_date + relativedelta(months=1)
     entries_qs = timepiece.Entry.objects
     entries_qs = entries_qs.timespan(from_date, span='month').filter(
         project=project
     )
     month_entries = entries_qs.date_trunc('month', True).order_by(
         'start_time'
     )
     total = entries_qs.aggregate(hours=Sum('hours'))['hours']
     user_entries = entries_qs.order_by().values(
         'user__first_name', 'user__last_name').annotate(
         sum=Sum('hours')).order_by('-sum'
     )
     activity_entries = entries_qs.order_by().values(
         'activity__name').annotate(
         sum=Sum('hours')).order_by('-sum'
     )
     return {
         'project': project,
         'year_month_form': year_month_form,
         'from_date': from_date,
         'to_date': to_date - datetime.timedelta(days=1),
         'entries': month_entries,
         'total': total,
         'user_entries': user_entries,
         'activity_entries': activity_entries,
     }
Esempio n. 23
0
 def clean(self):
     if not self.user_id:
         raise ValidationError('An unexpected error has occured')
     if not self.start_time:
         raise ValidationError('Please enter a valid start time')
     start = self.start_time
     if self.end_time:
         end = self.end_time
     #Current entries have no end_time
     else:
         end = start + datetime.timedelta(seconds=1)
     entries = self.user.timepiece_entries.filter(
         Q(end_time__range=(start, end)) | \
         Q(start_time__range=(start, end)) | \
         Q(start_time__lte=start, end_time__gte=end))
     #An entry can not conflict with itself so remove it from the list
     if self.id:
         entries = entries.exclude(pk=self.id)
     for entry in entries:
         entry_data = {
             'project': entry.project,
             'activity': entry.activity,
             'start_time': entry.start_time,
             'end_time': entry.end_time
         }
         #Conflicting saved entries
         if entry.end_time:
             if entry.start_time.date() == start.date() \
             and entry.end_time.date() == end.date():
                 entry_data['start_time'] = entry.start_time.strftime(
                     '%H:%M:%S')
                 entry_data['end_time'] = entry.end_time.strftime(
                     '%H:%M:%S')
                 output = 'Start time overlaps with: ' + \
                 '%(project)s - %(activity)s - ' % entry_data + \
                 'from %(start_time)s to %(end_time)s' % entry_data
                 raise ValidationError(output)
             else:
                 entry_data['start_time'] = entry.start_time.strftime(
                     '%H:%M:%S on %m\%d\%Y')
                 entry_data['end_time'] = entry.end_time.strftime(
                     '%H:%M:%S on %m\%d\%Y')
                 output = 'Start time overlaps with: ' + \
                 '%(project)s - %(activity)s - ' % entry_data + \
                 'from %(start_time)s to %(end_time)s' % entry_data
                 raise ValidationError(output)
     try:
         act_group = self.project.activity_group
         if act_group:
             activity = self.activity
             if not act_group.activities.filter(pk=activity.pk).exists():
                 name = activity.name
                 err_msg = '%s is not allowed for this project. ' % name
                 allowed = act_group.activities.filter()
                 allowed = allowed.values_list('name', flat=True)
                 allowed_names = ['among ']
                 if len(allowed) > 1:
                     for index, activity in enumerate(allowed):
                         allowed_names += activity
                         if index < len(allowed) - 2:
                             allowed_names += ', '
                         elif index < len(allowed) - 1:
                             allowed_names += ', and '
                     allowed_activities = ''.join(allowed_names)
                 else:
                     allowed_activities = allowed[0]
                 err_msg += 'Please choose %s' % allowed_activities
                 raise ValidationError(err_msg)
     except (Project.DoesNotExist, Activity.DoesNotExist):
         # Will be caught by field requirements
         pass
     if end <= start:
         raise ValidationError('Ending time must exceed the starting time')
     delta = (end - start)
     delta_secs = (delta.seconds + delta.days * 24 * 60 * 60)
     limit_secs = 60 * 60 * 12
     if delta_secs > limit_secs or self.seconds_paused > limit_secs:
         err_msg = 'Ending time exceeds starting time by 12 hours or more '\
             'for {0} on {1} at {2} to {3} at {4}.'.format(
                 self.project,
                 start.strftime('%m/%d/%Y'),
                 start.strftime('%H:%M:%S'),
                 end.strftime('%m/%d/%Y'),
                 end.strftime('%H:%M:%S')
             )
         raise ValidationError(err_msg)
     month_start = utils.get_month_start(start)
     next_month = month_start + relativedelta(months=1)
     entries = self.user.timepiece_entries.filter(
         Q(status='approved') | Q(status='invoiced'),
         start_time__gte=month_start,
         end_time__lt=next_month)
     if (entries.exists() and not self.id
             or self.id and self.status == 'invoiced'):
         msg = 'You cannot add/edit entries after a timesheet has been ' \
             'approved or invoiced. Please correct the start and end times.'
         raise ValidationError(msg)
     return True
Esempio n. 24
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(urllib.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)
    # 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 = verified_count + approved_count == total_statuses \
                and verified_count > 0 and total_statuses != 0

    return render(request, 'timepiece/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,
    })
Esempio n. 25
0
def view_person_time_sheet(request, user_id):
    user = get_object_or_404(User, pk=user_id)
    if not (request.user.has_perm('timepiece.view_entry_summary') or \
        user.pk == request.user.pk):
        return HttpResponseForbidden('Forbidden')
    from_date = utils.get_month_start(datetime.datetime.today()).date()
    to_date = from_date + relativedelta(months=1)
    initial = {
        'request_user': request.user,
        'user': request.GET.get('user', user_id)
    }
    year_month_form = timepiece_forms.YearMonthForm(request.GET or None,
                                                    initial=initial)
    if year_month_form.is_valid():
        from_date, to_date, form_user = year_month_form.save()
        if form_user:
            url = reverse('view_person_time_sheet', args=(form_user.pk, ))
            # Do not use request.GET in urlencode in case it has the
            # user parameter (redirect loop otherwise)
            request_data = {
                'month': request.GET.get('month', from_date.month),
                'year': request.GET.get('year', from_date.year)
            }
            url += '?{0}'.format(urllib.urlencode(request_data))
            return HttpResponseRedirect(url)
    entries_qs = timepiece.Entry.objects.filter(user=user)
    month_qs = entries_qs.timespan(from_date, span='month')
    month_entries = month_qs.date_trunc('month', True)
    # For grouped entries, back date up to the start of the week.
    first_week = utils.get_week_start(from_date)
    month_week = first_week + datetime.timedelta(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)
    grouped_totals = utils.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 = timepiece.Entry.summary(user, from_date, to_date)
    show_approve = show_verify = False
    if request.user.has_perm('timepiece.change_entry') or \
        user == request.user:
        statuses = list(entries_qs.values_list('status', flat=True))
        total_statuses = len(statuses)
        unverified_count = statuses.count('unverified')
        verified_count = statuses.count('verified')
        approved_count = statuses.count('approved')
        show_verify = unverified_count != 0
    if request.user.has_perm('timepiece.change_entry'):
        show_approve = verified_count + approved_count == total_statuses \
        and verified_count > 0 and total_statuses != 0
    context = {
        'year_month_form': year_month_form,
        'from_date': from_date,
        'to_date': to_date - datetime.timedelta(days=1),
        'show_verify': show_verify,
        'show_approve': show_approve,
        'timesheet_user': user,
        'entries': month_entries,
        'grouped_totals': grouped_totals,
        'project_entries': project_entries,
        'summary': summary,
    }
    return render_to_response('timepiece/time-sheet/people/view.html',
                              context,
                              context_instance=RequestContext(request))
Esempio n. 26
0
 def get_previous_month(self):
     """Returns date range for the previous full month."""
     end = utils.get_month_start() - relativedelta(days=1)
     end = utils.to_datetime(end)
     start = utils.get_month_start(end)
     return start, end
Esempio n. 27
0
 def clean(self):
     if not self.user_id:
         raise ValidationError("An unexpected error has occured")
     if not self.start_time:
         raise ValidationError("Please enter a valid start time")
     start = self.start_time
     if self.end_time:
         end = self.end_time
     # Current entries have no end_time
     else:
         end = start + relativedelta(seconds=1)
     entries = self.user.timepiece_entries.filter(
         Q(end_time__range=(start, end))
         | Q(start_time__range=(start, end))
         | Q(start_time__lte=start, end_time__gte=end)
     )
     # An entry can not conflict with itself so remove it from the list
     if self.id:
         entries = entries.exclude(pk=self.id)
     for entry in entries:
         entry_data = {
             "project": entry.project,
             "activity": entry.activity,
             "start_time": entry.start_time,
             "end_time": entry.end_time,
         }
         # Conflicting saved entries
         if entry.end_time:
             if entry.start_time.date() == start.date() and entry.end_time.date() == end.date():
                 entry_data["start_time"] = entry.start_time.strftime("%H:%M:%S")
                 entry_data["end_time"] = entry.end_time.strftime("%H:%M:%S")
                 raise ValidationError(
                     "Start time overlaps with "
                     "{activity} on {project} from {start_time} to "
                     "{end_time}.".format(**entry_data)
                 )
             else:
                 entry_data["start_time"] = entry.start_time.strftime("%H:%M:%S on %m\%d\%Y")
                 entry_data["end_time"] = entry.end_time.strftime("%H:%M:%S on %m\%d\%Y")
                 raise ValidationError(
                     "Start time overlaps with "
                     "{activity} on {project} from {start_time} to "
                     "{end_time}.".format(**entry_data)
                 )
     try:
         act_group = self.project.activity_group
         if act_group:
             activity = self.activity
             if not act_group.activities.filter(pk=activity.pk).exists():
                 name = activity.name
                 err_msg = "%s is not allowed for this project. " % name
                 allowed = act_group.activities.filter()
                 allowed = allowed.values_list("name", flat=True)
                 allowed_names = ["among "]
                 if len(allowed) > 1:
                     for index, activity in enumerate(allowed):
                         allowed_names += activity
                         if index < len(allowed) - 2:
                             allowed_names += ", "
                         elif index < len(allowed) - 1:
                             allowed_names += ", and "
                     allowed_activities = "".join(allowed_names)
                 else:
                     allowed_activities = allowed[0]
                 err_msg += "Please choose %s" % allowed_activities
                 raise ValidationError(err_msg)
     except (Project.DoesNotExist, Activity.DoesNotExist):
         # Will be caught by field requirements
         pass
     if end <= start:
         raise ValidationError("Ending time must exceed the starting time")
     delta = end - start
     delta_secs = delta.seconds + delta.days * 24 * 60 * 60
     limit_secs = 60 * 60 * 12
     if delta_secs > limit_secs or self.seconds_paused > limit_secs:
         err_msg = (
             "Ending time exceeds starting time by 12 hours or more "
             "for {0} on {1} at {2} to {3} at {4}.".format(
                 self.project,
                 start.strftime("%m/%d/%Y"),
                 start.strftime("%H:%M:%S"),
                 end.strftime("%m/%d/%Y"),
                 end.strftime("%H:%M:%S"),
             )
         )
         raise ValidationError(err_msg)
     month_start = utils.get_month_start(start)
     next_month = month_start + relativedelta(months=1)
     entries = self.user.timepiece_entries.filter(
         Q(status=Entry.APPROVED) | Q(status=Entry.INVOICED), start_time__gte=month_start, end_time__lt=next_month
     )
     if entries.exists() and not self.id or self.id and self.status == Entry.INVOICED:
         msg = (
             "You cannot add/edit entries after a timesheet has been "
             "approved or invoiced. Please correct the start and end times."
         )
         raise ValidationError(msg)
     return True
def show_cal(from_date, offset=0):
    date = get_month_start(from_date)
    date = date + relativedelta(months=offset)
    html_cal = calendar.HTMLCalendar(calendar.SUNDAY)
    return html_cal.formatmonth(date.year, date.month)
Esempio n. 29
0
def view_person_time_sheet(request, user_id):
    user = get_object_or_404(User, pk=user_id)
    if not (request.user.has_perm('timepiece.view_entry_summary') or \
        user.pk == request.user.pk):
        return HttpResponseForbidden('Forbidden')
    today_reset = datetime.datetime.today()
    today_reset = today_reset.replace(hour=0, minute=0, second=0, \
        microsecond=0)
    from_date = utils.get_month_start(today_reset)
    to_date = from_date + relativedelta(months=1)
    can_view_summary = request.user and \
        request.user.has_perm('timepiece.view_entry_summary')
    form = timepiece_forms.UserYearMonthForm if can_view_summary else \
        timepiece_forms.YearMonthForm
    year_month_form = form(request.GET or None)
    if year_month_form.is_valid():
        if can_view_summary:
            from_date, to_date, form_user = year_month_form.save()
            is_update = request.GET.get('yearmonth', None)
            if form_user and is_update:
                url = reverse('view_person_time_sheet', args=(form_user.pk,))
                # Do not use request.GET in urlencode in case it has the
                # yearmonth parameter (redirect loop otherwise)
                request_data = {
                    'month': from_date.month,
                    'year': from_date.year,
                    'user': form_user.pk
                }
                url += '?{0}'.format(urllib.urlencode(request_data))
                return HttpResponseRedirect(url)
        else:
            from_date, to_date = year_month_form.save()
    entries_qs = timepiece.Entry.objects.filter(user=user)
    month_qs = entries_qs.timespan(from_date, span='month')
    month_entries = month_qs.date_trunc('month', True)
    # For grouped entries, back date up to the start of the week.
    first_week = utils.get_week_start(from_date)
    month_week = first_week + datetime.timedelta(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)
    grouped_totals = utils.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 = timepiece.Entry.summary(user, from_date, to_date)
    show_approve = show_verify = False
    if request.user.has_perm('timepiece.change_entry') or \
        user == request.user:
        statuses = list(month_qs.values_list('status', flat=True))
        total_statuses = len(statuses)
        unverified_count = statuses.count('unverified')
        verified_count = statuses.count('verified')
        approved_count = statuses.count('approved')
        show_verify = unverified_count != 0
    if request.user.has_perm('timepiece.change_entry'):
        show_approve = verified_count + approved_count == total_statuses \
        and verified_count > 0 and total_statuses != 0
    context = {
        'year_month_form': year_month_form,
        'from_date': from_date,
        'to_date': to_date - datetime.timedelta(days=1),
        'show_verify': show_verify,
        'show_approve': show_approve,
        'timesheet_user': user,
        'entries': month_entries,
        'grouped_totals': grouped_totals,
        'project_entries': project_entries,
        'summary': summary,
    }
    return render_to_response('timepiece/time-sheet/people/view.html',
        context, context_instance=RequestContext(request))
Esempio n. 30
0
 def clean(self):
     if not self.user_id:
         raise ValidationError('An unexpected error has occured')
     if not self.start_time:
         raise ValidationError('Please enter a valid start time')
     start = self.start_time
     if self.end_time:
         end = self.end_time
     #Current entries have no end_time
     else:
         end = start + datetime.timedelta(seconds=1)
     entries = self.user.timepiece_entries.filter(
         Q(end_time__range=(start, end)) | \
         Q(start_time__range=(start, end)) | \
         Q(start_time__lte=start, end_time__gte=end))
     #An entry can not conflict with itself so remove it from the list
     if self.id:
         entries = entries.exclude(pk=self.id)
     for entry in entries:
         entry_data = {
             'project': entry.project,
             'activity': entry.activity,
             'start_time': entry.start_time,
             'end_time': entry.end_time
         }
         #Conflicting saved entries
         if entry.end_time:
             if entry.start_time.date() == start.date() \
             and entry.end_time.date() == end.date():
                 entry_data['start_time'] = entry.start_time.strftime(
                     '%H:%M:%S')
                 entry_data['end_time'] = entry.end_time.strftime(
                     '%H:%M:%S')
                 output = 'Start time overlaps with: ' + \
                 '%(project)s - %(activity)s - ' % entry_data + \
                 'from %(start_time)s to %(end_time)s' % entry_data
                 raise ValidationError(output)
             else:
                 entry_data['start_time'] = entry.start_time.strftime(
                     '%H:%M:%S on %m\%d\%Y')
                 entry_data['end_time'] = entry.end_time.strftime(
                     '%H:%M:%S on %m\%d\%Y')
                 output = 'Start time overlaps with: ' + \
                 '%(project)s - %(activity)s - ' % entry_data + \
                 'from %(start_time)s to %(end_time)s' % entry_data
                 raise ValidationError(output)
     try:
         act_group = self.project.activity_group
         if act_group:
             activity = self.activity
             if not act_group.activities.filter(pk=activity.pk).exists():
                 name = activity.name
                 err_msg = '%s is not allowed for this project. ' % name
                 allowed = act_group.activities.filter()
                 allowed = allowed.values_list('name', flat=True)
                 allowed_names = ['among ']
                 if len(allowed) > 1:
                     for index, activity in enumerate(allowed):
                         allowed_names += activity
                         if index < len(allowed) - 2:
                             allowed_names += ', '
                         elif index < len(allowed) - 1:
                             allowed_names += ', and '
                     allowed_activities = ''.join(allowed_names)
                 else:
                     allowed_activities = allowed[0]
                 err_msg += 'Please choose %s' % allowed_activities
                 raise ValidationError(err_msg)
     except (Project.DoesNotExist, Activity.DoesNotExist):
         # Will be caught by field requirements
         pass
     if end <= start:
         raise ValidationError('Ending time must exceed the starting time')
     delta = (end - start)
     delta_secs = (delta.seconds + delta.days * 24 * 60 * 60)
     limit_secs = 60 * 60 * 12
     if delta_secs > limit_secs or self.seconds_paused > limit_secs:
         err_msg = 'Ending time exceeds starting time by 12 hours or more '\
             'for {0} on {1} at {2} to {3} at {4}.'.format(
                 self.project.name,
                 start.strftime('%m/%d/%Y'),
                 start.strftime('%H:%M:%S'),
                 end.strftime('%m/%d/%Y'),
                 end.strftime('%H:%M:%S')
             )
         raise ValidationError(err_msg)
     month_start = utils.get_month_start(start)
     next_month = month_start + relativedelta(months=1)
     entries = self.user.timepiece_entries.filter(
         Q(status='approved') | Q(status='invoiced'),
         start_time__gte=month_start,
         end_time__lt=next_month
     )
     if (entries.exists() and not self.id
             or self.id and self.status == 'invoiced'):
         msg = 'You cannot add/edit entries after a timesheet has been ' \
             'approved or invoiced. Please correct the start and end times.'
         raise ValidationError(msg)
     return True
Esempio n. 31
0
 def get_previous_month(self):
     """Returns date range for the previous full month."""
     end = utils.get_month_start() - relativedelta(days=1)
     end = utils.to_datetime(end)
     start = utils.get_month_start(end)
     return start, end
def show_cal(from_date, offset=0):
    date = get_month_start(from_date)
    date = date + relativedelta(months=offset)
    html_cal = calendar.HTMLCalendar(calendar.SUNDAY)
    return html_cal.formatmonth(date.year, date.month)
Esempio n. 33
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(urllib.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)
    # 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 = verified_count + approved_count == total_statuses \
                and verified_count > 0 and total_statuses != 0

    return render(
        request, 'timepiece/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,
        })