Skip to content

Commit

Permalink
Merge pull request City-of-Helsinki#88 from suutari-ai/permit-timesta…
Browse files Browse the repository at this point in the history
…mp-normalization-fixes

Permits: Normalize timestamps in bulk-creation
  • Loading branch information
suutari-ai authored Sep 17, 2019
2 parents 382adbd + 470c2af commit 23e1b3f
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 11 deletions.
14 changes: 8 additions & 6 deletions parkings/models/permit.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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


Expand Down
57 changes: 54 additions & 3 deletions parkings/tests/api/enforcement/test_permit.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')

Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -201,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,
Expand All @@ -222,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'
3 changes: 1 addition & 2 deletions parkings/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down

0 comments on commit 23e1b3f

Please sign in to comment.