Skip to content

Commit

Permalink
Updates README
Browse files Browse the repository at this point in the history
Reverts LICENSE
Updates Django requirement to be 4.2 or higher instead of 3.2
Updates setup.cfg to ignore W503 errors
  * per W504, new best practice is to place a line break before a binary operator
Linting
Adding support for issue #884
  • Loading branch information
panatale1 committed Sep 4, 2024
1 parent 698f7da commit 6cddde9
Show file tree
Hide file tree
Showing 17 changed files with 70 additions and 51 deletions.
2 changes: 1 addition & 1 deletion LICENSE.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (c) 2024 Caktus Consulting Group, LLC and Peter Natale
Copyright (c) 2010 Caktus Consulting Group, LLC

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
15 changes: 7 additions & 8 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ Installation

- Add `timepiece` and its dependencies to ``INSTALLED_APPS``::

INSTALLED_APPS = (
INSTALLED_APPS = [
...
'bootstrap_toolkit',
'compressor',
Expand All @@ -88,17 +88,17 @@ Installation
'timepiece.crm',
'timepiece.entries',
'timepiece.reports',
)
]

- Configure your middleware::

MIDDLEWARE_CLASSES = (
MIDDLEWARE = [
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
)
]

- Add `django.core.context_processors.request` and django-timepiece context
processors to the ``context_processors`` in the ``TEMPLATES`` config::
Expand Down Expand Up @@ -192,7 +192,6 @@ Development sponsored by `Caktus Group`_.
.. _GitHub: https://github.com/caktus/django-timepiece
.. _less: http://lesscss.org
.. _pip: http://pip.openplans.org/
.. _PyPI: http://pypi.python.org/pypi/django-timepiece
.. _Read The Docs: http://django-timepiece.readthedocs.org
django-timepiece
django-timepiece
.. _PyPI: http://pypi.python.org/pypi/django-timepiece3
.. _Read The Docs: http://django-timepiece3.readthedocs.io

2 changes: 1 addition & 1 deletion requirements/base.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Django>=3.2 #>=1.7,<1.9
Django>=4.2 #>=1.7,<1.9

django-bootstrap3
django-compressor>=4.5
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[flake8]
exclude=*/*migrations/*
max-line-length=120
ignore = E501
ignore = E501, W503
2 changes: 1 addition & 1 deletion timepiece/contracts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def get_noncontract_entries(self, entries):
project__in=contract.projects.all(),
start_time__gte=contract.start_date,
end_time__lt=contract.end_date + relativedelta(days=1)
)
)
return entries

@property
Expand Down
8 changes: 4 additions & 4 deletions timepiece/contracts/tests/test_contracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ def setUp(self):
hour=0, minute=0, second=0, microsecond=0) - relativedelta(days=16),
end_date=timezone.now().replace(
hour=0, minute=0, second=0, microsecond=0) - relativedelta(days=12),
)
)

self.contract2 = factories.ProjectContract(
name='Contract 2',
Expand All @@ -310,14 +310,14 @@ def setUp(self):
hour=0, minute=0, second=0, microsecond=0) - relativedelta(days=8),
end_date=timezone.now().replace(
hour=0, minute=0, second=0, microsecond=0) - relativedelta(days=4),
)
)

for x in range(20):
factories.Entry(**{
'user': self.user_a,
'project': self.project_a,
'start_time': timezone.now() - relativedelta(days=x),
'end_time': (timezone.now() - relativedelta(days=x)) + relativedelta(hours=1),
'end_time': (timezone.now() - relativedelta(days=x)) + relativedelta(hours=1),
'seconds_paused': 0,
'status': Entry.UNVERIFIED,
})
Expand All @@ -326,7 +326,7 @@ def setUp(self):
'user': self.user_b,
'project': self.project_b,
'start_time': timezone.now() - relativedelta(days=x) + relativedelta(hours=2),
'end_time': (timezone.now() - relativedelta(days=x)) + relativedelta(hours=3),
'end_time': (timezone.now() - relativedelta(days=x)) + relativedelta(hours=3),
'seconds_paused': 0,
'status': Entry.UNVERIFIED,
})
Expand Down
5 changes: 4 additions & 1 deletion timepiece/crm/models.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
from collections import OrderedDict

from django.apps import apps
from django.contrib.auth.models import User
from django.contrib.auth import get_user_model
from django.urls import reverse
from django.db import models

from timepiece.utils import get_active_entry


User = get_user_model()


# Add a utility method to the User class that will tell whether or not a
# particular user has any unclosed entries
def _clocked_in(self):
Expand Down
5 changes: 4 additions & 1 deletion timepiece/crm/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
from six.moves.urllib.parse import urlencode

from django.contrib import messages
from django.contrib.auth import get_user_model
from django.contrib.auth.decorators import login_required, permission_required
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.contrib.auth.models import User
from django.urls import reverse, reverse_lazy
from django.db import transaction
from django.db.models import Sum
Expand All @@ -32,6 +32,9 @@
from timepiece.entries.models import Entry


User = get_user_model()


class QuickSearch(LoginRequiredMixin, FormView):
form_class = QuickSearchForm
template_name = 'timepiece/quick_search.html'
Expand Down
14 changes: 7 additions & 7 deletions timepiece/entries/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,9 +229,9 @@ def check_overlap(self, entry_b, **kwargs):
def is_overlapping(self):
if self.start_time and self.end_time:
entries = self.user.timepiece_entries.filter(
Q(end_time__range=(self.start_time, self.end_time)) |
Q(start_time__range=(self.start_time, self.end_time)) |
Q(start_time__lte=self.start_time, end_time__gte=self.end_time)
Q(end_time__range=(self.start_time, self.end_time))
| Q(start_time__range=(self.start_time, self.end_time))
| Q(start_time__lte=self.start_time, end_time__gte=self.end_time)
)

totals = entries.aggregate(max=Max('end_time'), min=Min('start_time'))
Expand Down Expand Up @@ -265,9 +265,9 @@ def clean(self):
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))
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)
Expand Down Expand Up @@ -476,7 +476,7 @@ def summary(user, date, end_date):
'billable': Decimal('0'), 'non_billable': Decimal('0'),
'invoiced': Decimal('0'), 'uninvoiced': Decimal('0'),
'total': Decimal('0')
}
}
invoiced = entries.filter(
status=Entry.INVOICED).aggregate(i=Sum('hours'))['i']
uninvoiced = entries.exclude(
Expand Down
22 changes: 11 additions & 11 deletions timepiece/entries/tests/test_timesheet.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ def setUp(self):
'user': self.user,
'project': self.project,
'start_time': timezone.now() - relativedelta(days=6),
'end_time': timezone.now() - relativedelta(days=6),
'end_time': timezone.now() - relativedelta(days=6),
'seconds_paused': 0,
'status': Entry.VERIFIED,
})
self.entry2 = factories.Entry(**{
'user': self.user,
'project': self.project,
'start_time': timezone.now() - relativedelta(days=2),
'end_time': timezone.now() - relativedelta(days=2),
'end_time': timezone.now() - relativedelta(days=2),
'seconds_paused': 0,
'status': Entry.UNVERIFIED,
})
Expand Down Expand Up @@ -400,8 +400,8 @@ def testClockInSameTime(self):
'Please enter a valid start time')
self.assertFormError(
response.context['form'], 'start_time',
'The start time is on or before the current entry: ' +
'%(project)s - %(activity)s starting at %(st_str)s' % entry1_data)
'The start time is on or before the current entry: '
+ '%(project)s - %(activity)s starting at %(st_str)s' % entry1_data)

def testClockInBeforeCurrent(self):
"""
Expand Down Expand Up @@ -876,16 +876,16 @@ def test_clocking_out_inactive(self):
'end_time_0': self.default_end_time.strftime('%m/%d/%Y'),
'end_time_1': self.default_end_time.strftime('%H:%M:%S'),
'location': self.location.pk,
}
}
response = self.client.post(
self.url, data,
follow=True,
)
)
# Do it again - make sure we redirect to the dashboard
response = self.client.post(
self.url, data,
follow=False,
)
)
self.assertRedirects(response, reverse('dashboard'),
status_code=302, target_status_code=200)

Expand Down Expand Up @@ -1601,7 +1601,7 @@ def testVerifyButton(self):
entry = factories.Entry(**{
'user': self.user,
'start_time': timezone.now() - relativedelta(hours=1),
'end_time': timezone.now(),
'end_time': timezone.now(),
})
response = self.client.get(self.sheet_url)
self.assertTrue(response.context['show_verify'])
Expand All @@ -1617,7 +1617,7 @@ def testApproveButton(self):
entry = factories.Entry(**{
'user': self.user,
'start_time': timezone.now() - relativedelta(hours=1),
'end_time': timezone.now(),
'end_time': timezone.now(),
})
response = self.client.get(self.sheet_url)
self.assertFalse(response.context['show_approve'])
Expand Down Expand Up @@ -1701,7 +1701,7 @@ def testRejectPage(self):
entry = factories.Entry(**{
'user': self.user,
'start_time': timezone.now() - relativedelta(hours=1),
'end_time': timezone.now(),
'end_time': timezone.now(),
})
reject_url = self.get_reject_url(entry.id)

Expand All @@ -1724,7 +1724,7 @@ def testNotAllowedToRejectTimesheet(self):
entry = factories.Entry(**{
'user': self.user,
'start_time': timezone.now() - relativedelta(hours=1),
'end_time': timezone.now(),
'end_time': timezone.now(),
})
reject_url = self.get_reject_url(entry.id)
response = self.client.get(reject_url)
Expand Down
6 changes: 5 additions & 1 deletion timepiece/entries/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
from six.moves.urllib.parse import urlencode

from django.contrib import messages
from django.contrib.auth import get_user_model
from django.contrib.auth.decorators import permission_required
from django.contrib.auth.mixins import PermissionRequiredMixin, LoginRequiredMixin
from django.contrib.auth.models import User, Permission
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.core import exceptions
from django.urls import reverse
Expand All @@ -31,6 +32,9 @@
from timepiece.entries.models import Entry, ProjectHours


User = get_user_model()


class Dashboard(LoginRequiredMixin, TemplateView):
template_name = 'timepiece/dashboard.html'

Expand Down
3 changes: 2 additions & 1 deletion timepiece/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
import time

from django import forms
from django.contrib.auth.models import User
from django.contrib.auth import get_user_model

from timepiece.fields import UserModelChoiceField

from timepiece.entries.models import Entry


User = get_user_model()
DATE_FORM_FORMAT = '%Y-%m-%d'
INPUT_FORMATS = [DATE_FORM_FORMAT]

Expand Down
19 changes: 12 additions & 7 deletions timepiece/management/commands/check_entries.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from dateutil.relativedelta import relativedelta

from django.contrib.auth.models import User
from django.contrib.auth import get_user_model
from django.core.management.base import BaseCommand, CommandError
from django.db.models import Q
from django.utils import timezone
Expand All @@ -11,6 +11,9 @@
from timepiece.entries.models import Entry


User = get_user_model()


class Command(BaseCommand):
"""
Management command to check entries for overlapping times.
Expand Down Expand Up @@ -103,8 +106,10 @@ def check_entry(self, entries, *args, **kwargs):
'last': user.last_name,
'total': user_total_overlaps,
}
self.stdout.write('Total overlapping entries for user ' +
'%(first)s %(last)s: %(total)d' % overlap_data)
self.stdout.write(
'Total overlapping entries for user '
+ '%(first)s %(last)s: %(total)d' % overlap_data
)
return user_total_overlaps

def find_start(self, **kwargs):
Expand Down Expand Up @@ -137,8 +142,8 @@ def find_users(self, *args):
"""
if args:
names = reduce(
lambda query, arg: query |
(Q(first_name__icontains=arg) | Q(last_name__icontains=arg)),
lambda query, arg: query
| (Q(first_name__icontains=arg) | Q(last_name__icontains=arg)),
args, Q()
) # noqa
users = User.objects.filter(names)
Expand Down Expand Up @@ -199,8 +204,8 @@ def make_output_data(entry):
if entry_b:
data_b = make_output_data(entry_b)
output = ('Entry %(entry)d for %(first_name)s %(last_name)s from '
'%(start)s to %(end)s on %(project)s overlaps ' % data_a +
'entry %(entry)d from %(start)s to %(end)s on '
'%(start)s to %(end)s on %(project)s overlaps ' % data_a
+ 'entry %(entry)d from %(start)s to %(end)s on '
'%(project)s.' % data_b)
else:
output = ('Entry %(entry)d for %(first_name)s %(last_name)s from '
Expand Down
5 changes: 4 additions & 1 deletion timepiece/reports/forms.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth import get_user_model

from selectable import forms as selectable

Expand All @@ -11,6 +11,9 @@
from timepiece.entries.models import Entry, Activity


User = get_user_model()


class BillableHoursReportForm(DateForm):
TRUNC_CHOICES = (
('day', 'Day'),
Expand Down
5 changes: 3 additions & 2 deletions timepiece/tests/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import six

from django.contrib.auth import models as auth
from django.contrib.auth import get_user_model

from timepiece.contracts import models as contracts
from timepiece.crm import models as crm
Expand All @@ -18,7 +19,7 @@
class User(factory.django.DjangoModelFactory):

class Meta:
model = auth.User
model = get_user_model()

# FIXME: Some tests depend on first_name/last_name being unique.
first_name = factory.Sequence(lambda n: 'Sam{0}'.format(n))
Expand Down Expand Up @@ -73,7 +74,7 @@ def contract_hours(self, create, extracted, **kwargs):
if create:
num_hours = extracted or random.randint(10, 400)
for i in range(2):
ContractHour(contract=self, hours=Decimal(str(num_hours/2.0)))
ContractHour(contract=self, hours=Decimal(str(num_hours / 2.0)))

@factory.post_generation
def projects(self, create, extracted, **kwargs):
Expand Down
Loading

0 comments on commit 6cddde9

Please sign in to comment.