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
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, }
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, }
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))
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, })
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 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'))
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
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 })
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, })
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, })
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
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 }
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
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, })
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, })
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
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
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, })
def test_get_active_entry_none(self): self.assertIsNone(get_active_entry(self.user))
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)
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)