Skip to content

Commit 96db96c

Browse files
Merge branch 'main' into OSDEV-1365-SLC-integrate-collecting-contribution-data-page
2 parents 04ed47c + 5c3284a commit 96db96c

14 files changed

Lines changed: 355 additions & 138 deletions

doc/release/RELEASE-NOTES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html
1919
* *Describe schema changes here.*
2020

2121
### Code/API changes
22-
* *Describe code/API changes here.*
22+
* [OSDEV-1581](https://opensupplyhub.atlassian.net/browse/OSDEV-1581) - Added support for Geohex grid aggregation to the GET `/api/v1/production-locations/` endpoint. To receive the Geohex grid aggregation list in the response, it is necessary to pass the `aggregation` parameter with a value of `geohex_grid` and optionally specify `geohex_grid_precision` with an integer between 0 and 15. If `geohex_grid_precision` is not defined, the default value of 5 will be used.
2323

2424
### Architecture/Environment changes
2525
* *Describe architecture/environment changes here.*

src/django/api/serializers/v1/production_locations_serializer.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
class ProductionLocationsSerializer(Serializer):
2626
# These params are checking considering serialize_params output
2727
size = IntegerField(required=False)
28+
address = CharField(required=False)
29+
description = CharField(required=False)
2830
number_of_workers_min = IntegerField(required=False)
2931
number_of_workers_max = IntegerField(required=False)
3032
percent_female_workers_min = FloatField(required=False)
@@ -45,6 +47,15 @@ class ProductionLocationsSerializer(Serializer):
4547
choices=['asc', 'desc'],
4648
required=False
4749
)
50+
aggregation = ChoiceField(
51+
choices=['geohex_grid'],
52+
required=False,
53+
)
54+
geohex_grid_precision = IntegerField(
55+
min_value=0,
56+
max_value=15,
57+
required=False,
58+
)
4859

4960
def validate(self, data):
5061
validators = [

src/django/api/services/opensearch/search.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,26 @@ def __prepare_opensearch_response(self, response):
4646
else:
4747
logger.warning(f"Missing '_source' in hit: {hit}")
4848

49-
return {
49+
response_data = {
5050
"count": total_hits,
51-
"data": data
51+
"data": data,
5252
}
5353

54+
geohex_buckets = (
55+
response.get("aggregations", {})
56+
.get("grouped", {})
57+
.get("buckets", [])
58+
)
59+
60+
if geohex_buckets:
61+
response_data.update({
62+
"aggregations": {
63+
"geohex_grid": geohex_buckets
64+
}
65+
})
66+
67+
return response_data
68+
5469
@staticmethod
5570
def __remove_null_values(obj):
5671
if isinstance(obj, dict):

src/django/api/tests/test_moderation_events_query_builder.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import unittest
21
from django.test import TestCase
32
from api.views.v1.opensearch_query_builder. \
43
moderation_events_query_builder import ModerationEventsQueryBuilder
@@ -88,6 +87,21 @@ def test_add_sort(self):
8887
expected = {'created_at': {'order': 'asc'}}
8988
self.assertIn(expected, self.builder.query_body['sort'])
9089

90+
def test_add_sort_with_default_order(self):
91+
self.builder.add_sort('created_at')
92+
expected = {'created_at': {'order': 'desc'}}
93+
self.assertIn(expected, self.builder.query_body['sort'])
94+
95+
def test_add_sort_name(self):
96+
self.builder.add_sort('name', 'asc')
97+
expected = {'cleaned_data.name': {'order': 'asc'}}
98+
self.assertIn(expected, self.builder.query_body['sort'])
99+
100+
def test_add_sort_address(self):
101+
self.builder.add_sort('address', 'asc')
102+
expected = {'cleaned_data.address': {'order': 'asc'}}
103+
self.assertIn(expected, self.builder.query_body['sort'])
104+
91105
def test_add_sort_country(self):
92106
self.builder.add_sort('country', 'asc')
93107
expected = {'cleaned_data.country.name': {'order': 'asc'}}
@@ -121,7 +135,3 @@ def test_get_final_query_body(self):
121135
'sort': []
122136
}
123137
self.assertEqual(final_query, expected)
124-
125-
126-
if __name__ == '__main__':
127-
unittest.main()

src/django/api/tests/test_opensearch_response_formatter.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,47 @@ def test_prepare_opensearch_response_rename_lon_field(self, mock_logger):
188188
self.assertEqual(result, expected_result)
189189
mock_logger.warning.assert_not_called()
190190

191+
@patch('api.services.opensearch.search.logger')
192+
def test_prepare_opensearch_response_with_aggregation_data(
193+
self,
194+
mock_logger
195+
):
196+
response = {
197+
"hits": {
198+
"total": {"value": 10},
199+
"hits": [
200+
{"_source": {"field1": "value1"}},
201+
{"_source": {"field2": "value2"}}
202+
]
203+
},
204+
"aggregations": {
205+
"grouped": {
206+
"buckets": [
207+
{"key": "value1"},
208+
{"key": "value2"}
209+
]
210+
}
211+
}
212+
}
213+
expected_result = {
214+
"count": 10,
215+
"data": [
216+
{"field1": "value1"},
217+
{"field2": "value2"}
218+
],
219+
"aggregations": {
220+
"geohex_grid": [
221+
{"key": "value1"},
222+
{"key": "value2"}
223+
]
224+
}
225+
}
226+
227+
result = self.service. \
228+
_OpenSearchService__prepare_opensearch_response(response)
229+
self.assertEqual(result, expected_result)
230+
mock_logger.warning.assert_not_called()
231+
191232

192233
if __name__ == '__main__':
193234
unittest.main()

src/django/api/tests/test_production_locations_query_builder.py

Lines changed: 56 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import unittest
21
from django.test import TestCase
32
from api.views.v1.opensearch_query_builder. \
43
production_locations_query_builder import ProductionLocationsQueryBuilder
@@ -26,20 +25,6 @@ def test_add_match(self):
2625
self.builder.query_body['query']['bool']['must']
2726
)
2827

29-
def test_add_multi_match(self):
30-
self.builder.add_multi_match('test query')
31-
expected = {
32-
'multi_match': {
33-
'query': 'test query',
34-
'fields': ['name^2', 'address', 'description', 'local_name'],
35-
'fuzziness': 2
36-
}
37-
}
38-
self.assertIn(
39-
expected,
40-
self.builder.query_body['query']['bool']['must']
41-
)
42-
4328
def test_add_terms_for_standard_field(self):
4429
self.builder.add_terms('country', ['US', 'CA'])
4530
expected = {'terms': {'country.alpha_2': ['US', 'CA']}}
@@ -181,6 +166,11 @@ def test_add_sort(self):
181166
expected = {'name.keyword': {'order': 'desc'}}
182167
self.assertIn(expected, self.builder.query_body['sort'])
183168

169+
def test_add_sort_with_default_order(self):
170+
self.builder.add_sort('name')
171+
expected = {'name.keyword': {'order': 'asc'}}
172+
self.assertIn(expected, self.builder.query_body['sort'])
173+
184174
def test_add_search_after(self):
185175
search_after_value = 'test_value'
186176
search_after_id = 'test_id'
@@ -214,6 +204,55 @@ def test_get_final_query_body(self):
214204
}
215205
self.assertEqual(final_query, expected)
216206

207+
def test_add_multi_match(self):
208+
self.builder.add_multi_match(
209+
'test query'
210+
)
211+
expected = {
212+
'multi_match': {
213+
'query': 'test query',
214+
'fields': ['name^2', 'address', 'description', 'local_name'],
215+
'fuzziness': 2,
216+
}
217+
}
218+
self.assertIn(
219+
expected, self.builder.query_body['query']['bool']['must']
220+
)
221+
222+
def test_add_aggregations_with_precision(self):
223+
aggregation = 'geohex_grid'
224+
geohex_grid_precision = 5
225+
self.builder.add_aggregations(
226+
aggregation,
227+
geohex_grid_precision
228+
)
229+
expected = {
230+
'grouped': {
231+
'geohex_grid': {
232+
'field': 'coordinates',
233+
'precision': geohex_grid_precision
234+
}
235+
}
236+
}
237+
self.assertIn('aggregations', self.builder.query_body)
238+
self.assertEqual(expected, self.builder.query_body['aggregations'])
239+
240+
def test_add_aggregations_without_precision(self):
241+
aggregation = 'geohex_grid'
242+
self.builder.add_aggregations(
243+
aggregation
244+
)
245+
expected = {
246+
'grouped': {
247+
'geohex_grid': {'field': 'coordinates'}
248+
}
249+
}
250+
self.assertIn('aggregations', self.builder.query_body)
251+
self.assertEqual(expected, self.builder.query_body['aggregations'])
217252

218-
if __name__ == '__main__':
219-
unittest.main()
253+
def test_add_aggregations_where_aggregation_is_not_geohex_grid(self):
254+
aggregation = 'test_aggregation'
255+
self.builder.add_aggregations(
256+
aggregation
257+
)
258+
self.assertNotIn('aggregations', self.builder.query_body)

0 commit comments

Comments
 (0)