Add date intersection validation for models with start and end dates.

This commit is contained in:
Christopher Charbonneau Wells 2017-11-05 14:18:30 -05:00
parent 08d6efa98b
commit e6acfe4e75
2 changed files with 66 additions and 4 deletions

View File

@ -26,6 +26,22 @@ def validate_duration(model, max_duration=timedelta(hours=24)):
raise ValidationError('Duration too long.', code='max_duration') raise ValidationError('Duration too long.', code='max_duration')
def validate_unique_period(queryset, model):
"""
Confirm that model's start and end date do not intersect with other
instances.
:param queryset: a queryset of instances to check against.
:param model: a model instance with 'start' and 'end' attributes
:return:
"""
if model.id:
queryset = queryset.exclude(id=model.id)
if queryset.filter(start__lte=model.end, end__gte=model.start):
raise ValidationError(
'Another entry already exists within the specified time period.',
code='period_intersection')
def validate_time(time, field_name): def validate_time(time, field_name):
""" """
Confirm that a time is not in the future. Confirm that a time is not in the future.
@ -149,6 +165,7 @@ class Feeding(models.Model):
validate_time(self.start, 'start') validate_time(self.start, 'start')
validate_time(self.end, 'end') validate_time(self.end, 'end')
validate_duration(self) validate_duration(self)
validate_unique_period(Feeding.objects.filter(child=self.child), self)
# "Formula" Type may only be associated with "Bottle" Method. # "Formula" Type may only be associated with "Bottle" Method.
if self.type == 'formula'and self.method != 'bottle': if self.type == 'formula'and self.method != 'bottle':
@ -216,6 +233,7 @@ class Sleep(models.Model):
validate_time(self.start, 'start') validate_time(self.start, 'start')
validate_time(self.end, 'end') validate_time(self.end, 'end')
validate_duration(self) validate_duration(self)
validate_unique_period(Sleep.objects.filter(child=self.child), self)
class Timer(models.Model): class Timer(models.Model):
@ -304,3 +322,4 @@ class TummyTime(models.Model):
validate_time(self.start, 'start') validate_time(self.start, 'start')
validate_time(self.end, 'end') validate_time(self.end, 'end')
validate_duration(self) validate_duration(self)
validate_unique_period(TummyTime.objects.filter(child=self.child), self)

View File

@ -97,6 +97,9 @@ class FormsTestCase(TestCase):
page = self.c.post('/feedings/add/?timer={}'.format(timer.id), params) page = self.c.post('/feedings/add/?timer={}'.format(timer.id), params)
self.assertEqual(page.status_code, 302) self.assertEqual(page.status_code, 302)
# Change start and end to prevent intersection validation errors.
params['start'] = '2000-01-01 2:01'
params['end'] = '2000-01-01 2:31'
page = self.c.post('/feedings/{}/'.format(entry.id), params) page = self.c.post('/feedings/{}/'.format(entry.id), params)
self.assertEqual(page.status_code, 302) self.assertEqual(page.status_code, 302)
@ -107,7 +110,7 @@ class FormsTestCase(TestCase):
page, 'form', 'method', page, 'form', 'method',
'Only "Bottle" method is allowed with "Formula" type.') 'Only "Bottle" method is allowed with "Formula" type.')
def test_sleeping_forms(self): def test_sleep_forms(self):
params = { params = {
'child': 1, 'child': 1,
'start': '2000-01-01 1:01', 'start': '2000-01-01 1:01',
@ -119,6 +122,9 @@ class FormsTestCase(TestCase):
page = self.c.post('/sleep/add/?timer={}'.format(timer.id), params) page = self.c.post('/sleep/add/?timer={}'.format(timer.id), params)
self.assertEqual(page.status_code, 302) self.assertEqual(page.status_code, 302)
# Change start and end to prevent intersection validation errors.
params['start'] = '2000-01-01 4:01'
params['end'] = '2000-01-01 6:01'
entry = models.Sleep.objects.first() entry = models.Sleep.objects.first()
page = self.c.post('/sleep/{}/'.format(entry.id), params) page = self.c.post('/sleep/{}/'.format(entry.id), params)
self.assertEqual(page.status_code, 302) self.assertEqual(page.status_code, 302)
@ -167,11 +173,14 @@ class FormsTestCase(TestCase):
'/tummy-time/add/?timer={}'.format(timer.id), params) '/tummy-time/add/?timer={}'.format(timer.id), params)
self.assertEqual(page.status_code, 302) self.assertEqual(page.status_code, 302)
# Change start and end to prevent intersection validation errors.
params['start'] = '2000-01-01 2:01'
params['end'] = '2000-01-01 2:11'
entry = models.TummyTime.objects.first() entry = models.TummyTime.objects.first()
page = self.c.post('/tummy-time/{}/'.format(entry.id), params) page = self.c.post('/tummy-time/{}/'.format(entry.id), params)
self.assertEqual(page.status_code, 302) self.assertEqual(page.status_code, 302)
def test_validators(self): def test_validate_duration(self):
params = { params = {
'child': 1, 'child': 1,
'start': '2001-01-01 1:01', 'start': '2001-01-01 1:01',
@ -190,9 +199,43 @@ class FormsTestCase(TestCase):
self.assertEqual(page.status_code, 200) self.assertEqual(page.status_code, 200)
self.assertFormError(page, 'form', None, 'Duration too long.') self.assertFormError(page, 'form', None, 'Duration too long.')
tomorrow = (timezone.localtime() + timezone.timedelta(days=1)) def test_validate_time(self):
params['end'] = tomorrow.strftime('%Y-%m-%d %H:%M:%S') future = (timezone.localtime() + timezone.timedelta(hours=1))
params = {
'child': 1,
'start': timezone.localtime().strftime('%Y-%m-%d %H:%M:%S'),
'end': future.strftime('%Y-%m-%d %H:%M:%S'),
'milestone': ''
}
entry = models.TummyTime.objects.first()
page = self.c.post('/tummy-time/{}/'.format(entry.id), params) page = self.c.post('/tummy-time/{}/'.format(entry.id), params)
self.assertEqual(page.status_code, 200) self.assertEqual(page.status_code, 200)
self.assertFormError(page, 'form', 'end', self.assertFormError(page, 'form', 'end',
'Date/time can not be in the future.') 'Date/time can not be in the future.')
def test_validate_unique_period(self):
entry = models.TummyTime.objects.first()
base_time = timezone.localtime()
new_entry = models.TummyTime.objects.create(
child=entry.child,
start=base_time - timezone.timedelta(minutes=45),
end=base_time - timezone.timedelta(minutes=15),
)
new_entry.save()
params = {
'child': 1,
'start': (base_time - timezone.timedelta(minutes=35)).strftime(
'%Y-%m-%d %H:%M'),
'end': (base_time - timezone.timedelta(minutes=5)).strftime(
'%Y-%m-%d %H:%M'),
'milestone': ''
}
page = self.c.post('/tummy-time/{}/'.format(entry.id), params)
self.assertEqual(page.status_code, 200)
self.assertFormError(
page,
'form',
None,
'Another entry already exists within the specified time period.')