Repository: City-of-Helsinki/linkedevents

Issue:
Add validation for end_time/start_time < now



Discussion:

Resolving patch:
diff --git a/events/api.py b/events/api.py
--- a/events/api.py
+++ b/events/api.py
@@ -20,6 +20,8 @@
 from django.db.models import Q
 from django.shortcuts import get_object_or_404
 from django.utils.translation import ugettext_lazy as _
+from django.utils import timezone
+from django.utils.encoding import force_text
 from rest_framework import (
     serializers, relations, viewsets, mixins, filters, generics, status
 )
@@ -801,9 +803,6 @@ def validate(self, data):
                 else:
                     errors[field] = _('This field must be specified before an event is published.')
 
-        if errors:
-            raise serializers.ValidationError(errors)
-
         # adjust start_time and has_start_time
 
         if 'has_start_time' not in data:
@@ -827,6 +826,15 @@ def validate(self, data):
             data['end_time'] = data['end_time'].replace(hour=0, minute=0, second=0)
             data['end_time'] += timedelta(days=1)
 
+        if data.get('start_time') and data['start_time'] < timezone.now():
+            errors['start_time'] = force_text(_('Start time cannot be in the past.'))
+
+        if data.get('end_time') and data['end_time'] < timezone.now():
+            errors['end_time'] = force_text(_('End time cannot be in the past.'))
+
+        if errors:
+            raise serializers.ValidationError(errors)
+
         # JA: TODO: Temporarily disabled new validation code
         # if data.get('short_description') and len(data['short_description']) > 160:
         #     raise serializers.ValidationError(


Test patch:
diff --git a/events/tests/conftest.py b/events/tests/conftest.py
--- a/events/tests/conftest.py
+++ b/events/tests/conftest.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+from datetime import timedelta
 
 # django
 from django.contrib.auth import get_user_model
@@ -23,7 +24,7 @@
 
 TEXT = 'testing'
 URL = "http://localhost"
-DATETIME = timezone.now().isoformat()
+DATETIME = (timezone.now() + timedelta(days=1)).isoformat()
 
 OTHER_DATA_SOURCE_ID = "testotherdatasourceid"
 
diff --git a/events/tests/test_event_post.py b/events/tests/test_event_post.py
--- a/events/tests/test_event_post.py
+++ b/events/tests/test_event_post.py
@@ -1,6 +1,8 @@
 # -*- coding: utf-8 -*-
+from datetime import timedelta
+
 import pytest
-from django.utils import timezone
+from django.utils import timezone, translation
 from .utils import versioned_reverse as reverse
 
 from events.tests.utils import assert_event_data_is_equal
@@ -44,10 +46,8 @@ def test__cannot_create_an_event_ending_before_start_time(list_url,
                                                           minimal_event_dict,
                                                           user):
     api_client.force_authenticate(user=user)
-    minimal_event_dict['end_time'] = minimal_event_dict['start_time']
-    minimal_event_dict['start_time'] = timezone.now().isoformat()
-    print(minimal_event_dict['start_time'])
-    print(minimal_event_dict['end_time'])
+    minimal_event_dict['end_time'] = (timezone.now() + timedelta(days=1)).isoformat()
+    minimal_event_dict['start_time'] = (timezone.now() + timedelta(days=2)).isoformat()
     response = api_client.post(list_url, minimal_event_dict, format='json')
     assert response.status_code == 400
     assert 'end_time' in response.data
@@ -163,3 +163,17 @@ def test__jsonld_related_field(api_client, minimal_event_dict, list_url, place,
     if expected >= 400:
         # check that there is a error message for location field
         assert 'location' in response.data
+
+
+@pytest.mark.django_db
+def test_start_time_and_end_time_validation(api_client, minimal_event_dict, user):
+    api_client.force_authenticate(user)
+
+    minimal_event_dict['start_time'] = timezone.now() - timedelta(days=2)
+    minimal_event_dict['end_time'] = timezone.now() - timedelta(days=1)
+
+    with translation.override('en'):
+        response = api_client.post(reverse('event-list'), minimal_event_dict, format='json')
+    assert response.status_code == 400
+    assert 'Start time cannot be in the past.' in response.data['start_time']
+    assert 'End time cannot be in the past.' in response.data['end_time']
diff --git a/events/tests/test_event_put.py b/events/tests/test_event_put.py
--- a/events/tests/test_event_put.py
+++ b/events/tests/test_event_put.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+from datetime import timedelta
 from django.utils import timezone
 
 import pytest
@@ -88,10 +89,10 @@ def test__reschedule_an_event_with_put(api_client, complex_event_dict, user):
     response = create_with_post(api_client, complex_event_dict)
 
     # create a new datetime
-    DATETIME = timezone.now().isoformat()
+    new_datetime = (timezone.now() + timedelta(days=3)).isoformat()
     data2 = response.data
-    data2['start_time'] = DATETIME
-    data2['end_time'] = DATETIME
+    data2['start_time'] = new_datetime
+    data2['end_time'] = new_datetime
 
     # update the event
     event_id = data2.pop('@id')
@@ -166,10 +167,10 @@ def test__reschedule_a_cancelled_event_with_put(api_client, complex_event_dict,
     assert_event_data_is_equal(data2, response2.data)
 
     # create a new datetime and remove the cancelled status
-    DATETIME = timezone.now().isoformat()
+    new_datetime = (timezone.now() + timedelta(days=3)).isoformat()
     data3 = response2.data
-    data3['start_time'] = DATETIME
-    data3['end_time'] = DATETIME
+    data3['start_time'] = new_datetime
+    data3['end_time'] = new_datetime
     data3.pop('event_status')
 
     # update the event