Example #1
0
    def get_context_data(self, *args, **kwargs):
        context = super(Dashboard, self).get_context_data(**kwargs)
        week_start = self.week_start
        week_end = week_start + relativedelta(days=7)
        initial = {'start_time': datetime.datetime.now()}
        #initial = dict([(k, request.GET[k]) for k in request.GET.keys()])
        #form = ProjectHoursSearchForm(initial=initial)

        entry = utils.get_active_entry(self.user)

        form = EntryDashboardForm(self.request.POST or None,
                                  instance=entry,
                                  initial=initial,
                                  user=self.user,
                                  acting_user=self.user)

        # Query for the user's active entry if it exists.
        active_entry = utils.get_active_entry(self.user)

        # Process this week's entries to determine assignment progress.
        week_entries = Entry.objects.filter(user=self.user)
        week_entries = week_entries.timespan(week_start, span='week')
        week_entries = week_entries.select_related('project')
        assignments = ProjectHours.objects.filter(user=self.user,
                                                  week_start__gte=week_start,
                                                  week_start__lt=week_end)
        project_progress = self.process_progress(week_entries, assignments)

        # Total hours that the user is expected to clock this week.
        #total_assigned = self.get_hours_per_week(self.user)
        total_worked = sum([p['worked'] for p in project_progress])

        # Others' active entries.
        others_active_entries = Entry.objects.filter(end_time__isnull=True)
        others_active_entries = others_active_entries.exclude(user=self.user)
        others_active_entries = others_active_entries.select_related(
            'user', 'project')

        project_entries = week_entries.order_by().values(
            'project__name').annotate(sum=Sum('hours')).order_by('-sum')

        context.update({
            #'active_tab': self.active_tab,
            'form': form,
            'active_entry': active_entry,
            'total_worked': total_worked,
            'project_progress': project_progress,
            'week_entries': week_entries,
            'others_active_entries': others_active_entries,
            'week': self.week_start,
            'prev_week': self.week_start - relativedelta(days=7),
            'next_week': self.week_start + relativedelta(days=7),
        })
        return context
    def get_context_data(self, *args, **kwargs):
        context = super(Dashboard, self).get_context_data(**kwargs)
        week_start = self.week_start
        week_end = week_start + relativedelta(days=7)

        entry = utils.get_active_entry(self.user)
        if (entry == None):
            initial = {
                'start_time': datetime.datetime.now(),
            }
        else:
            initial = None
        form = EntryDashboardForm(self.request.POST or None,
                                  instance=entry,
                                  initial=initial,
                                  user=self.user,
                                  acting_user=self.user)

        # Query for the user's active entry if it exists.
        active_entry = utils.get_active_entry(self.user)

        # Process this week's entries to determine assignment progress.
        week_entries = Entry.objects.filter(user=self.user)
        week_entries = week_entries.timespan(week_start, span='week')
        week_entries = week_entries.select_related('project')
        assignments = ProjectHours.objects.filter(user=self.user,
                                                  week_start__gte=week_start,
                                                  week_start__lt=week_end)
        project_progress = self.process_progress(week_entries, assignments)

        # Total hours that the user is expected to clock this week.
        total_worked = sum([p['worked'] for p in project_progress])

        project_entries = week_entries.order_by().values(
            'project__name').annotate(sum=Sum('hours')).order_by('-sum')

        todos = ToDo.objects.filter(user=self.user, completed=False)

        context.update({
            'form': form,
            'active_entry': active_entry,
            'total_worked': total_worked,
            'project_progress': project_progress,
            'week_entries': week_entries,
            'week': self.week_start,
            'prev_week': self.week_start - relativedelta(days=7),
            'next_week': self.week_start + relativedelta(days=7),
            'todos': todos,
        })
        return context
    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
Example #4
0
    def get_context_data(self, *args, **kwargs):
        today, week_start, week_end = self.get_dates()

        # Query for the user's active entry if it exists.
        active_entry = utils.get_active_entry(self.user)

        # Process this week's entries to determine assignment progress.
        week_entries = Entry.objects.filter(user=self.user) \
                .timespan(week_start, span='week', current=True) \
                .select_related('project')
        assignments = ProjectHours.objects.filter(user=self.user,
                week_start=week_start.date())
        project_progress = self.process_progress(week_entries, assignments)

        # Total hours that the user is expected to clock this week.
        total_assigned = self.get_hours_per_week(self.user)
        total_worked = sum([p['worked'] for p in project_progress])

        # Others' active entries.
        others_active_entries = Entry.objects.filter(end_time__isnull=True) \
                .exclude(user=self.user).select_related('user', 'project',
                'activity')

        return {
            'active_tab': self.active_tab,
            'today': today,
            'week_start': week_start.date(),
            'week_end': week_end.date(),
            'active_entry': active_entry,
            'total_assigned': total_assigned,
            'total_worked': total_worked,
            'project_progress': project_progress,
            'week_entries': week_entries,
            'others_active_entries': others_active_entries,
        }
Example #5
0
    def get_context_data(self, *args, **kwargs):
        today, week_start, week_end = self.get_dates()

        # Query for the user's active entry if it exists.
        active_entry = utils.get_active_entry(self.user)

        # Process this week's entries to determine assignment progress.
        week_entries = Entry.objects.filter(user=self.user)
        week_entries = week_entries.timespan(week_start, span='week', current=True)
        week_entries = week_entries.select_related('project')
        assignments = ProjectHours.objects.filter(
            user=self.user, week_start=week_start.date())
        project_progress = self.process_progress(week_entries, assignments)

        # Total hours that the user is expected to clock this week.
        total_assigned = self.get_hours_per_week(self.user)
        total_worked = sum([p['worked'] for p in project_progress])

        # Others' active entries.
        others_active_entries = Entry.objects.filter(end_time__isnull=True)
        others_active_entries = others_active_entries.exclude(user=self.user)
        others_active_entries = others_active_entries.select_related('user', 'project', 'activity')

        return {
            'active_tab': self.active_tab,
            'today': today,
            'week_start': week_start.date(),
            'week_end': week_end.date(),
            'active_entry': active_entry,
            'total_assigned': total_assigned,
            'total_worked': total_worked,
            'project_progress': project_progress,
            'week_entries': week_entries,
            'others_active_entries': others_active_entries,
        }
Example #6
0
 def test_get_active_entry_single(self):
     now = datetime.datetime.now()
     entry = factories.Entry(user=self.user, start_time=now)
     # not active
     factories.Entry(user=self.user, start_time=now, end_time=now)
     # different user
     factories.Entry(start_time=now)
     self.assertEqual(entry, get_active_entry(self.user))
Example #7
0
 def test_get_active_entry_single(self):
     now = datetime.datetime.now()
     entry = factories.Entry(user=self.user, start_time=now)
     # not active
     factories.Entry(user=self.user, start_time=now,
             end_time=now)
     # different user
     factories.Entry(start_time=now)
     self.assertEqual(entry, get_active_entry(self.user))
Example #8
0
 def test_get_active_entry_single(self):
     now = datetime.datetime.now()
     entry = self.create_entry({'user': self.user, 'start_time': now})
     # not active
     self.create_entry({'user': self.user, 'start_time': now,
                        'end_time': now})
     # different user
     self.create_entry({'user': self.create_user(), 'start_time': now})
     self.assertEqual(entry, get_active_entry(self.user))
def clock_out(request):
    entry = utils.get_active_entry(request.user)
    if not entry:
        message = "Not clocked in"
        messages.info(request, message)
        return HttpResponseRedirect(reverse('dashboard'))
    if request.POST:
        form = ClockOutForm(request.POST, instance=entry)
        if form.is_valid():
            #get end_time from form
            end = form.cleaned_data.get('end_time')
            #get all other fields from active entry
            entry_user = entry.user
            start = entry.start_time
            project = entry.project
            activities = entry.activities
            start_date = start.date()
            end_date = end.date()
            delta = end_date - start_date
            #start and end dates are the same
            if delta.days == 0:
                entry = form.save()
            #if end date is one day after start date, split up for 2 entries
            elif delta.days == 1:
                #make end be the end of start time day
                end_of_day = start.replace(hour=23,
                                           minute=59,
                                           second=59,
                                           microsecond=999999)
                entry.end_time = end_of_day
                entry.save()
                #start of second entry is one microsecond after end of 1st entry
                start_of_next_day = end_of_day + datetime.timedelta(
                    microseconds=1)
                entry2 = Entry(user=entry_user,
                               project=project,
                               start_time=start_of_next_day,
                               end_time=end,
                               activities=activities)
                entry2.save()
            #end date is before start date or more than one day apart
            else:
                message = 'Dates can only be one day apart at most.'
                messages.warning(request, message)
                return HttpResponseRedirect(reverse('clock_out'))
            message = 'You have clocked out of {0}.'.format(entry.project)
            messages.info(request, message)
            return HttpResponseRedirect(reverse('dashboard'))
        else:
            message = 'Please correct the errors below.'
            messages.warning(request, message)
    else:
        form = ClockOutForm(instance=entry)
    return render(request, 'timepiece/entry/clock_out.html', {
        'form': form,
        'entry': entry,
    })
Example #10
0
 def test_get_active_entry_single(self):
     now = datetime.datetime.now()
     entry = self.create_entry({'user': self.user, 'start_time': now})
     # not active
     self.create_entry({
         'user': self.user,
         'start_time': now,
         'end_time': now
     })
     # different user
     self.create_entry({'user': self.create_user(), 'start_time': now})
     self.assertEqual(entry, get_active_entry(self.user))
Example #11
0
def toggle_pause(request):
    """Allow the user to pause and unpause the active entry."""
    entry = utils.get_active_entry(request.user)
    if not entry:
        raise Http404

    # toggle the paused state
    entry.toggle_paused()
    entry.save()

    # create a message that can be displayed to the user
    action = 'paused' if entry.is_paused else 'resumed'
    message = 'Your entry, {0} on {1}, has been {2}.'.format(
        entry.activity.name, entry.project, action)
    messages.info(request, message)

    # redirect to the log entry list
    return HttpResponseRedirect(reverse('dashboard'))
Example #12
0
def toggle_pause(request):
    """Allow the user to pause and unpause the active entry."""
    entry = utils.get_active_entry(request.user)
    if not entry:
        raise Http404

    # toggle the paused state
    entry.toggle_paused()
    entry.save()

    # create a message that can be displayed to the user
    action = 'paused' if entry.is_paused else 'resumed'
    message = 'Your entry, {0} on {1}, has been {2}.'.format(
            entry.activity.name, entry.project, action)
    messages.info(request, message)

    # redirect to the log entry list
    return HttpResponseRedirect(reverse('dashboard'))
Example #13
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)
     if active and active.pk != self.instance.pk:
         start_time = self.cleaned_data.get('start_time', None)
         end_time = self.cleaned_data.get('end_time', None)
         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'),
                     }))
     return self.cleaned_data
Example #14
0
def clock_in(request):
    """This uses the same logic as timepiece.entries.views.clock_in,
       but requires a different template"""

    user = request.user
    active_entry = get_active_entry(user, select_for_update=True)

    initial = dict([(k, v) for k, v in request.GET.items()])
    data = request.POST or None
    form = ClockInForm(data, initial=initial, user=user, active=active_entry)
    if form.is_valid():
        entry = form.save()
        message = 'You have clocked into {0} on {1}'.format(
            entry.activity.name, entry.project)
        messages.info(request, message)
        return HttpResponseRedirect(reverse('dashboard'))
    return render(request, 'entry/clock_in.html', {
        'form': form,
        'active': active_entry
    })
Example #15
0
def clock_in(request):
    """For clocking the user into a project."""
    user = request.user
    # Lock the active entry for the duration of this transaction, to prevent
    # creating multiple active entries.
    active_entry = utils.get_active_entry(user, select_for_update=True)

    initial = dict([(k, v) for k, v in request.GET.items()])
    data = request.POST or None
    form = ClockInForm(data, initial=initial, user=user, active=active_entry)
    if form.is_valid():
        entry = form.save()
        message = 'You have clocked into {0}.'.format(entry.project)
        messages.info(request, message)
        return HttpResponseRedirect(reverse('dashboard'))

    return render(request, 'timepiece/entry/clock_in.html', {
        'form': form,
        'active': active_entry,
    })
Example #16
0
def clock_in(request):
    """For clocking the user into a project."""
    user = request.user
    # Lock the active entry for the duration of this transaction, to prevent
    # creating multiple active entries.
    active_entry = utils.get_active_entry(user, select_for_update=True)

    initial = dict([(k, v) for k, v in request.GET.items()])
    data = request.POST or None
    form = ClockInForm(data, initial=initial, user=user, active=active_entry)
    if form.is_valid():
        entry = form.save()
        message = 'You have clocked into {0} on {1}.'.format(
                entry.activity.name, entry.project)
        messages.info(request, message)
        return HttpResponseRedirect(reverse('dashboard'))

    return render(request, 'timepiece/entry/clock_in.html', {
        'form': form,
        'active': active_entry,
    })
Example #17
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)
     if active and active.pk != self.instance.pk:
         start_time = self.cleaned_data.get('start_time', None)
         end_time = self.cleaned_data.get('end_time', None)
         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'),
                     }))
     return self.cleaned_data
Example #18
0
    def get_context_data(self, *args, **kwargs):
        today, week_start, week_end = self.get_dates()

        active_entry = get_active_entry(self.user)
        week_entries = week_entries = Entry.objects.filter(
            user=self.user).select_related('project', 'tasks')
        assignments = ProjectHours.objects.filter(user=self.user,
                                                  week_start=week_start.date())
        project_progress = self.process_progress(week_entries, assignments)
        others_active_entries = Entry.objects.filter(
            end_time__isnull=True).exclude(user=self.user).select_related(
                'user', 'project', 'activity')
        return {
            'active_tab': self.active_tab,
            'today': today,
            'week_start': week_start.date(),
            'week_end': week_end.date(),
            'active_entry': active_entry,
            'week_entries': week_entries,
            'project_progress': project_progress,
            'others_active_entries': others_active_entries
        }
Example #19
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)
     if active and active.pk != self.instance.pk:
         start_time = self.cleaned_data.get("start_time", None)
         end_time = self.cleaned_data.get("end_time", None)
         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"),
                     }
                 )
             )
     return self.cleaned_data
Example #20
0
def clock_out(request):
    entry = utils.get_active_entry(request.user)
    if not entry:
        message = "Not clocked in"
        messages.info(request, message)
        return HttpResponseRedirect(reverse('dashboard'))
    if request.POST:
        form = ClockOutForm(request.POST, instance=entry)
        if form.is_valid():
            entry = form.save()
            message = 'You have clocked out of {0}.'.format(entry.project)
            messages.info(request, message)
            return HttpResponseRedirect(reverse('dashboard'))
        else:
            message = 'Please correct the errors below.'
            messages.error(request, message)
    else:
        form = ClockOutForm(instance=entry)
    return render(request, 'timepiece/entry/clock_out.html', {
        'form': form,
        'entry': entry,
    })
Example #21
0
def clock_out(request):
    entry = utils.get_active_entry(request.user)
    if not entry:
        message = "Not clocked in"
        messages.info(request, message)
        return HttpResponseRedirect(reverse('dashboard'))
    if request.POST:
        form = ClockOutForm(request.POST, instance=entry)
        if form.is_valid():
            entry = form.save()
            message = 'You have clocked out of {0} on {1}.'.format(
                    entry.activity.name, entry.project)
            messages.info(request, message)
            return HttpResponseRedirect(reverse('dashboard'))
        else:
            message = 'Please correct the errors below.'
            messages.error(request, message)
    else:
        form = ClockOutForm(instance=entry)
    return render(request, 'timepiece/entry/clock_out.html', {
        'form': form,
        'entry': entry,
    })
Example #22
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
Example #23
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
Example #24
0
def clock_out(request):
    """Like clock_in, needs new template, as well as new form"""
    entry = get_active_entry(request.user)
    if not entry:
        message = "Not clocked in"
        messages.info(request, message)
        return HttpresponseRedirect(reverse('dashboard'))
    if request.POST:
        form = ClockOutFormWithTasks(request.POST, instance=entry)
        if form.is_valid():
            entry = form.save()
            message = 'You have clocked out of {0} on {1}'.format(
                entry.activity.name, entry.project)
            messages.info(request, message)
            return HttpResponseRedirect(reverse('dashboard'))
        else:
            message = 'Please correct the errors below.'
            messages.error(request, message)
    else:
        form = ClockOutFormWithTasks(instance=entry)
    return render(request, 'entry/clock_out.html', {
        'form': form,
        'entry': entry,
    })
Example #25
0
 def test_get_active_entry_none(self):
     self.assertIsNone(get_active_entry(self.user))
Example #26
0
from collections import OrderedDict

from django.apps import apps
from django.contrib.auth.models import User
from django.urls import reverse
from django.db import models
from django.utils.encoding import python_2_unicode_compatible
from django.db.models.signals import post_save
from django.dispatch import receiver

from timepiece.utils import get_active_entry

# Add a utility method to the User class that will tell whether or not a
# particular user has any unclosed entries
_clocked_in = lambda user: bool(get_active_entry(user))
User.add_to_class('clocked_in', property(_clocked_in))

# Utility method to get user's name, falling back to username.
_get_name_or_username = lambda user: user.get_full_name() or user.username
User.add_to_class('get_name_or_username', _get_name_or_username)

_get_absolute_url = lambda user: reverse('view_user', args=(user.pk, ))
User.add_to_class('get_absolute_url', _get_absolute_url)


@python_2_unicode_compatible
class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    ssn = models.CharField(max_length=4, blank=True, default=' ')
    title = models.CharField(max_length=20, blank=True, default=' ')
    payroll = models.BooleanField(default=True, blank=False)
Example #27
0
 def test_get_active_entry_none(self):
     self.assertIsNone(get_active_entry(self.user))
Example #28
0
from collections import OrderedDict

from django.apps import apps
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from django.db import models
from django.utils.encoding import python_2_unicode_compatible

from timepiece.utils import get_active_entry


# Add a utility method to the User class that will tell whether or not a
# particular user has any unclosed entries
_clocked_in = lambda user: bool(get_active_entry(user))
User.add_to_class('clocked_in', property(_clocked_in))


# Utility method to get user's name, falling back to username.
_get_name_or_username = lambda user: user.get_full_name() or user.username
User.add_to_class('get_name_or_username', _get_name_or_username)


_get_absolute_url = lambda user: reverse('view_user', args=(user.pk,))
User.add_to_class('get_absolute_url', _get_absolute_url)


@python_2_unicode_compatible
class UserProfile(models.Model):
    user = models.OneToOneField(User, unique=True, related_name='profile')
    hours_per_week = models.DecimalField(
        max_digits=8, decimal_places=2, default=40)