From 8ff90120ca7d5905bbb98f4a6b3919b4ccdb63a8 Mon Sep 17 00:00:00 2001 From: Tuomas Suutari Date: Mon, 16 Sep 2019 10:08:16 +0300 Subject: [PATCH 1/2] Revert "Permits: Interpret timestamps without timezone as UTC" This reverts commit 59f01a9d7ca811985606b20275a62a821bb0cd1d. --- parkings/tests/api/enforcement/test_permit.py | 5 ++++- parkings/validators.py | 3 +-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/parkings/tests/api/enforcement/test_permit.py b/parkings/tests/api/enforcement/test_permit.py index 52ef86f6..1d3355f1 100644 --- a/parkings/tests/api/enforcement/test_permit.py +++ b/parkings/tests/api/enforcement/test_permit.py @@ -84,6 +84,10 @@ def test_permit_is_created_with_empty_lists(staff_api_client, permit_series): 'invalid-timestamp': ( [{'start_time': TS1, 'end_time': '', 'registration_number': 'X-14'}], 'Invalid "end_time" value \'\': Not a timestamp string'), + 'no-timezone': ( + [{'start_time': TS1, 'end_time': '2019-12-31T00:00:00', + 'registration_number': 'X-14'}], + 'Invalid "end_time" value \'2019-12-31T00:00:00\': Missing timezone'), } @pytest.mark.parametrize('kind', ['single', 'bulk']) @pytest.mark.parametrize('test_case', INVALID_DATA_TEST_CASES.keys()) @@ -116,7 +120,6 @@ def test_permit_is_not_created_with_invalid_post_data( ('1995-01-01 12:00:00 UTC', '1995-01-01T12:00:00+00:00'), ('2019-05-30T12:00:00.123Z', '2019-05-30T12:00:00.123000+00:00'), ('2019-05-30T12:00:00.999999 +00:00', '2019-05-30T12:00:00.999999+00:00'), - ('2019-12-31T00:00:00', '2019-12-31T00:00:00+00:00'), # No timezone as UTC ]) @pytest.mark.django_db def test_permit_creation_normalizes_timestamps( diff --git a/parkings/validators.py b/parkings/validators.py index fd373113..b55eb45b 100644 --- a/parkings/validators.py +++ b/parkings/validators.py @@ -49,8 +49,7 @@ def clean_value(self, value): raise ValidationError(_('Not a timestamp string')) if not dt.tzinfo: - # Interpret timestamps without a timezone as UTC - dt = dt.replace(tzinfo=timezone.utc) + raise ValidationError(_('Missing timezone')) return dt.astimezone(timezone.utc).isoformat() From 470c2af84b685f5d989b06cf769f8e2f8f8d59f6 Mon Sep 17 00:00:00 2001 From: Tuomas Suutari Date: Mon, 16 Sep 2019 10:39:03 +0300 Subject: [PATCH 2/2] Permits: Normalize timestamps on bulk-create Do the full_clean calls before passing the data to bulk_create method so that the timestamps are normalized in the saved objects too. --- parkings/models/permit.py | 14 ++--- parkings/tests/api/enforcement/test_permit.py | 52 ++++++++++++++++++- 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/parkings/models/permit.py b/parkings/models/permit.py index 30c52015..4114fa36 100644 --- a/parkings/models/permit.py +++ b/parkings/models/permit.py @@ -1,3 +1,5 @@ +from itertools import chain + from django.conf import settings from django.contrib.gis.db import models as gis_models from django.db import models, router, transaction @@ -66,14 +68,14 @@ def by_area(self, area_identifier): return self.filter(lookup_items__in=lookup_items).distinct() def bulk_create(self, permits, *args, **kwargs): + for permit in permits: + assert isinstance(permit, Permit) + permit.full_clean() + with transaction.atomic(using=self.db, savepoint=False): created_permits = super().bulk_create(permits, *args, **kwargs) - lookup_items = [] - for permit in created_permits: - assert isinstance(permit, Permit) - permit.full_clean() - lookup_items.extend(permit._make_lookup_items()) - PermitLookupItem.objects.using(self.db).bulk_create(lookup_items) + PermitLookupItem.objects.using(self.db).bulk_create( + chain(*(x._make_lookup_items() for x in created_permits))) return created_permits diff --git a/parkings/tests/api/enforcement/test_permit.py b/parkings/tests/api/enforcement/test_permit.py index 1d3355f1..44ce1eea 100644 --- a/parkings/tests/api/enforcement/test_permit.py +++ b/parkings/tests/api/enforcement/test_permit.py @@ -5,7 +5,7 @@ from ....factories.permit import ( generate_areas, generate_external_ids, generate_subjects) -from ....models import PermitLookupItem +from ....models import Permit, PermitLookupItem list_url = reverse('enforcement:v1:permit-list') @@ -204,7 +204,8 @@ def test_permit_data_matches_permit_object(staff_api_client, permit): check_permit_areas_keys(response.data['areas'][0]) -def test_permit_bulk_create_creates_lookup_items(staff_api_client, permit_series): +def test_permit_bulk_create_creates_lookup_items( + staff_api_client, permit_series): permit_data = [ { "series": permit_series.id, @@ -225,3 +226,50 @@ def test_permit_bulk_create_creates_lookup_items(staff_api_client, permit_series assert response.status_code == HTTP_201_CREATED assert PermitLookupItem.objects.count() == 2 + + +def test_permit_bulk_create_normalizes_timestamps( + staff_api_client, permit_series): + permit_data = [ + { + "series": permit_series.id, + "external_id": "E1", + 'subjects': [{ + 'start_time': '1970-01-01T01:23:00+01:23', + 'end_time': '2030-06-30T12:00+03:00', + 'registration_number': 'REG-1', + }], + 'areas': [{ + 'start_time': '1970-01-01T00:00:00Z', + 'end_time': '2030-06-30T11:00+02:00', + 'area': 'AREA 51', + }], + }, + { + "series": permit_series.id, + "external_id": "E2", + 'subjects': [{ + 'start_time': '1969-12-31T22:00:00-02:00', + 'end_time': '2030-06-30T10:00+01:00', + 'registration_number': 'REG-2', + }], + 'areas': [{ + 'start_time': '1970-01-01T00:00:00+00:00', + 'end_time': '2030-06-30T09:00Z', + 'area': 'AREA 52', + }], + }, + ] + + response = staff_api_client.post(list_url, data=permit_data) + + assert response.status_code == HTTP_201_CREATED + (permit1, permit2) = list(Permit.objects.order_by('external_id')) + assert permit1.subjects[0]['start_time'] == '1970-01-01T00:00:00+00:00' + assert permit1.subjects[0]['end_time'] == '2030-06-30T09:00:00+00:00' + assert permit1.areas[0]['start_time'] == '1970-01-01T00:00:00+00:00' + assert permit1.areas[0]['end_time'] == '2030-06-30T09:00:00+00:00' + assert permit2.subjects[0]['start_time'] == '1970-01-01T00:00:00+00:00' + assert permit2.subjects[0]['end_time'] == '2030-06-30T09:00:00+00:00' + assert permit2.areas[0]['start_time'] == '1970-01-01T00:00:00+00:00' + assert permit2.areas[0]['end_time'] == '2030-06-30T09:00:00+00:00'