Skip to content
Snippets Groups Projects
Commit 49433c0d authored by Bastien Abadie's avatar Bastien Abadie
Browse files

Merge branch 'list-filter-rotation-mirror' into 'master'

Filter by rotation_angle and mirroring

Closes #893

See merge request !1536
parents d1d35349 798fbb40
No related branches found
No related tags found
1 merge request!1536Filter by rotation_angle and mirroring
......@@ -181,6 +181,22 @@ class ElementsListAutoSchema(AutoSchema):
type=UUID,
required=False,
),
OpenApiParameter(
'rotation_angle',
description='Restrict to elements with the given rotation angle.',
type={
'type': 'integer',
'minimum': 0,
'maximum': 359,
},
required=False,
),
OpenApiParameter(
'mirrored',
description='Restrict to or exclude mirrored elements.',
type=bool,
required=False,
)
]
# Add method-specific parameters
......@@ -450,9 +466,21 @@ class ElementsListBase(CorpusACLMixin, DestroyModelMixin, ListAPIView):
}
errors = {}
if 'name' in self.request.query_params:
if 'name' in self.clean_params:
filters['name__icontains'] = self.clean_params['name']
if 'rotation_angle' in self.clean_params:
try:
rotation_angle = int(self.clean_params['rotation_angle'])
assert 0 <= rotation_angle <= 359, 'A rotation angle must be between 0 and 359 degrees'
except (AssertionError, TypeError, ValueError) as e:
errors['rotation_angle'] = [str(e)]
else:
filters['rotation_angle'] = rotation_angle
if 'mirrored' in self.clean_params:
filters['mirrored'] = self.clean_params['mirrored'].lower() not in ('false', '0')
if self.type_filter:
filters['type'] = self.type_filter
elif self.folder_filter is not None:
......
......@@ -498,6 +498,86 @@ class TestChildrenElements(FixtureAPITestCase):
]
)
def test_children_filter_rotation_angle(self):
element_type = self.corpus.types.first()
not_rotated = self.corpus.elements.create(type=element_type, name='not rotated')
rotated = self.corpus.elements.create(type=element_type, name='rotated', rotation_angle=180)
not_rotated.add_parent(self.vol)
rotated.add_parent(self.vol)
cases = [
(0, ['not rotated']),
(1, []),
(180, ['rotated']),
(181, [])
]
for rotation_angle, expected_elements in cases:
with self.subTest(rotation_angle=rotation_angle):
# Requests that return no elements only make 3 SQL queries
num_queries = 5 if len(expected_elements) else 3
with self.assertNumQueries(num_queries):
response = self.client.get(
reverse('api:elements-children', kwargs={'pk': str(self.vol.id)}),
data={
'name': 'rotated',
'rotation_angle': rotation_angle,
}
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertListEqual(
[element['name'] for element in response.json()['results']],
expected_elements
)
def test_children_filter_rotation_angle_invalid(self):
cases = [
('to the left', "invalid literal for int() with base 10: 'to the left'"),
(-1, 'A rotation angle must be between 0 and 359 degrees'),
(360, 'A rotation angle must be between 0 and 359 degrees'),
(54.2, "invalid literal for int() with base 10: '54.2'"),
(4.14j, "invalid literal for int() with base 10: '4.14j'"),
]
for rotation_angle, expected_error in cases:
with self.subTest(rotation_angle=rotation_angle):
with self.assertNumQueries(1):
response = self.client.get(
reverse('api:elements-children', kwargs={'pk': str(self.vol.id)}),
data={
'rotation_angle': rotation_angle,
}
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.json(), {
'rotation_angle': [expected_error]
})
def test_children_filter_mirrored(self):
element_type = self.corpus.types.first()
mirrored = self.corpus.elements.create(type=element_type, name='mirrored', mirrored=True)
not_mirrored = self.corpus.elements.create(type=element_type, name='not mirrored', mirrored=False)
mirrored.add_parent(self.vol)
not_mirrored.add_parent(self.vol)
cases = [
(False, ['not mirrored']),
(True, ['mirrored']),
]
for mirrored, expected_elements in cases:
with self.subTest(mirrored=mirrored):
with self.assertNumQueries(5):
response = self.client.get(
reverse('api:elements-children', kwargs={'pk': str(self.vol.id)}),
data={
'name': 'mirrored',
'mirrored': mirrored,
}
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertListEqual(
[element['name'] for element in response.json()['results']],
expected_elements
)
def test_children_invalid_sort(self):
cases = [
({'order': 'blah', 'order_direction': 'asc'}, {'order': ['Unknown sorting field']}),
......
......@@ -536,6 +536,82 @@ class TestListElements(FixtureAPITestCase):
['Volume 1, page 1v', 'Volume 2, page 1v']
)
def test_list_elements_filter_rotation_angle(self):
element_type = self.corpus.types.first()
self.corpus.elements.create(type=element_type, name='not rotated')
self.corpus.elements.create(type=element_type, name='rotated', rotation_angle=180)
cases = [
(0, ['not rotated']),
(1, []),
(180, ['rotated']),
(181, [])
]
for rotation_angle, expected_elements in cases:
with self.subTest(rotation_angle=rotation_angle):
# Requests that return no elements only make 2 SQL queries
num_queries = 4 if len(expected_elements) else 2
with self.assertNumQueries(num_queries):
response = self.client.get(
reverse('api:corpus-elements', kwargs={'corpus': self.corpus.id}),
data={
'name': 'rotated',
'rotation_angle': rotation_angle,
}
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertListEqual(
[element['name'] for element in response.json()['results']],
expected_elements
)
def test_list_elements_filter_rotation_angle_invalid(self):
cases = [
('to the left', "invalid literal for int() with base 10: 'to the left'"),
(-1, 'A rotation angle must be between 0 and 359 degrees'),
(360, 'A rotation angle must be between 0 and 359 degrees'),
(54.2, "invalid literal for int() with base 10: '54.2'"),
(4.14j, "invalid literal for int() with base 10: '4.14j'"),
]
for rotation_angle, expected_error in cases:
with self.subTest(rotation_angle=rotation_angle):
with self.assertNumQueries(1):
response = self.client.get(
reverse('api:corpus-elements', kwargs={'corpus': self.corpus.id}),
data={
'rotation_angle': rotation_angle,
}
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.json(), {
'rotation_angle': [expected_error]
})
def test_list_elements_filter_mirrored(self):
element_type = self.corpus.types.first()
self.corpus.elements.create(type=element_type, name='mirrored', mirrored=True)
self.corpus.elements.create(type=element_type, name='not mirrored', mirrored=False)
cases = [
(False, ['not mirrored']),
(True, ['mirrored']),
]
for mirrored, expected_elements in cases:
with self.subTest(mirrored=mirrored):
with self.assertNumQueries(4):
response = self.client.get(
reverse('api:corpus-elements', kwargs={'corpus': self.corpus.id}),
data={
'name': 'mirrored',
'mirrored': mirrored,
}
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertListEqual(
[element['name'] for element in response.json()['results']],
expected_elements
)
def test_list_elements_with_corpus_false(self):
with self.assertNumQueries(5):
response = self.client.get(
......
......@@ -317,6 +317,86 @@ class TestParentsElements(FixtureAPITestCase):
['Element with metadata']
)
def test_parents_filter_rotation_angle(self):
element_type = self.corpus.types.first()
not_rotated = self.corpus.elements.create(type=element_type, name='not rotated')
rotated = self.corpus.elements.create(type=element_type, name='rotated', rotation_angle=180)
self.page.add_parent(not_rotated)
self.page.add_parent(rotated)
cases = [
(0, ['not rotated']),
(1, []),
(180, ['rotated']),
(181, [])
]
for rotation_angle, expected_elements in cases:
with self.subTest(rotation_angle=rotation_angle):
# Requests that return no elements only make 2 SQL queries
num_queries = 4 if len(expected_elements) else 2
with self.assertNumQueries(num_queries):
response = self.client.get(
reverse('api:elements-parents', kwargs={'pk': str(self.page.id)}),
data={
'name': 'rotated',
'rotation_angle': rotation_angle,
}
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertListEqual(
[element['name'] for element in response.json()['results']],
expected_elements
)
def test_parents_filter_rotation_angle_invalid(self):
cases = [
('to the left', "invalid literal for int() with base 10: 'to the left'"),
(-1, 'A rotation angle must be between 0 and 359 degrees'),
(360, 'A rotation angle must be between 0 and 359 degrees'),
(54.2, "invalid literal for int() with base 10: '54.2'"),
(4.14j, "invalid literal for int() with base 10: '4.14j'"),
]
for rotation_angle, expected_error in cases:
with self.subTest(rotation_angle=rotation_angle):
with self.assertNumQueries(1):
response = self.client.get(
reverse('api:elements-parents', kwargs={'pk': str(self.page.id)}),
data={
'rotation_angle': rotation_angle,
}
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.json(), {
'rotation_angle': [expected_error]
})
def test_parents_filter_mirrored(self):
element_type = self.corpus.types.first()
mirrored = self.corpus.elements.create(type=element_type, name='mirrored', mirrored=True)
not_mirrored = self.corpus.elements.create(type=element_type, name='not mirrored', mirrored=False)
self.page.add_parent(mirrored)
self.page.add_parent(not_mirrored)
cases = [
(False, ['not mirrored']),
(True, ['mirrored']),
]
for mirrored, expected_elements in cases:
with self.subTest(mirrored=mirrored):
with self.assertNumQueries(4):
response = self.client.get(
reverse('api:elements-parents', kwargs={'pk': str(self.page.id)}),
data={
'name': 'mirrored',
'mirrored': mirrored,
}
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertListEqual(
[element['name'] for element in response.json()['results']],
expected_elements
)
def test_parents_invalid_sort(self):
cases = [
({'order': 'blah', 'order_direction': 'asc'}, {'order': ['Unknown sorting field']}),
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment