diff --git a/doc/release/RELEASE-NOTES.md b/doc/release/RELEASE-NOTES.md index 7a2ea0a88..7c191b4b4 100644 --- a/doc/release/RELEASE-NOTES.md +++ b/doc/release/RELEASE-NOTES.md @@ -41,6 +41,7 @@ database: * [OSDEV-885](https://opensupplyhub.atlassian.net/browse/OSDEV-885) - Implement option to reset database for `Dev`, `Test` and `Pre-prod` environmet to `Deploy to AWS` pipleine ### Bugfix * [OSDEV-870](https://opensupplyhub.atlassian.net/browse/OSDEV-870) - The returning confirm/reject URLs were fixed when a facility has been matched. Changes were made to the Dedupe-Hub to prevent adding rows with empty fields to the `api_facilitymatch` and `api_facilitymatchtemp` tables when the count of matches is more than one. +* [OSDEV-744](https://opensupplyhub.atlassian.net/browse/OSDEV-744) - API. When user want to confirm/reject potential_match it didn't found a match through `id`, was fixed by provided valid `id` from `api_facilitymatch` table. ### What's new * [OSDEV-975](https://opensupplyhub.atlassian.net/browse/OSDEV-975) Reporting. Number of facilities with at least one extended field.`Facilities with Extended Field Data` report has been rewritten from Django ORM to SQL to optimize and speed up time of the report generation. Added two columns `With At Least 1 Extended Field` and `Sector`. diff --git a/src/django/api/processing.py b/src/django/api/processing.py index 119525208..4b975a4e9 100644 --- a/src/django/api/processing.py +++ b/src/django/api/processing.py @@ -639,13 +639,21 @@ def handle_external_match_process_result(id, result, request, should_create): if f_l_item is None: # Timeout return get_error_match_result(id, result) - queryset_f_m = FacilityMatchTemp.objects.filter( + if should_create: + match_object_type = FacilityMatch + else: + match_object_type = FacilityMatchTemp + queryset_f_m = match_object_type.objects.filter( facility_list_item=f_l_item.id) if queryset_f_m.count() == 0: # No Match and Geocoder Returned No Results return get_error_match_result(f_l_item.id, result) - if (queryset_f_m.count() == 1 - and queryset_f_m[0].status == FacilityMatchTemp.AUTOMATIC): + + automatic_matches = [ + match for match in queryset_f_m + if match.status == match_object_type.AUTOMATIC + ] + if (len(automatic_matches) == 1): # New Facility if f_l_item.facility is None: return get_new_facility_match_result(f_l_item.id, None, result) @@ -656,19 +664,22 @@ def handle_external_match_process_result(id, result, request, should_create): # Automatic Match return get_automatic_match_result(f_l_item.id, f_l_item.facility.id, - queryset_f_m[0].confidence, + automatic_matches[0].confidence, context, result) - for item in queryset_f_m: - if item.status == FacilityMatchTemp.PENDING: - # Potential Match - return get_potential_match_result(f_l_item, - item, - queryset_f_m.count(), - context, - should_create, - result) + pending_matches = [ + match for match in queryset_f_m + if match.status == match_object_type.PENDING + ] + for item in pending_matches: + # Potential Match + return get_potential_match_result(f_l_item, + item, + len(pending_matches), + context, + should_create, + result) return result diff --git a/src/django/api/tests/test_facility_create_api.py b/src/django/api/tests/test_facility_create_api.py new file mode 100644 index 000000000..dbb47b36a --- /dev/null +++ b/src/django/api/tests/test_facility_create_api.py @@ -0,0 +1,178 @@ +from api.models import ( + Contributor, + Facility, + FacilityListItem, + FacilityMatch, + Source, + User, +) +from api.processing import handle_external_match_process_result + +from rest_framework.test import APITestCase +from django.contrib.gis.geos import Point + + +class FacilityCreateAPITest(APITestCase): + def setUp(self): + self.user_email = "test@example.com" + self.user_password = "example123" + self.user = User.objects.create(email=self.user_email) + self.user.set_password(self.user_password) + self.user.save() + + self.contributor = Contributor.objects.create( + admin=self.user, + name="test contributor", + contrib_type=Contributor.OTHER_CONTRIB_TYPE, + ) + + self.source_one = Source.objects.create( + source_type=Source.SINGLE, + is_active=True, + is_public=True, + contributor=self.contributor, + ) + + self.list_item_one = FacilityListItem.objects.create( + name="Item", + address="Address", + country_code="US", + sector=["Apparel"], + row_index=1, + geocoded_point=Point(0, 0), + status=FacilityListItem.MATCHED, + source=self.source_one, + ) + + self.facility_one = Facility.objects.create( + name="Name", + address="Address", + country_code="US", + location=Point(0, 0), + created_from=self.list_item_one, + ) + + self.source_two = Source.objects.create( + source_type=Source.SINGLE, + is_active=True, + is_public=True, + contributor=self.contributor, + ) + + self.list_item_two = FacilityListItem.objects.create( + name="Item", + address="Address", + country_code="US", + sector=["Apparel"], + row_index=1, + geocoded_point=Point(0, 0), + status=FacilityListItem.MATCHED, + source=self.source_two, + facility=self.facility_one, + ) + + self.match_two = FacilityMatch.objects.create( + status=FacilityMatch.AUTOMATIC, + facility=self.facility_one, + facility_list_item=self.list_item_two, + confidence=0.85, + results="", + ) + + self.source_three = Source.objects.create( + source_type=Source.SINGLE, + is_active=True, + is_public=True, + contributor=self.contributor, + ) + + self.list_item_three = FacilityListItem.objects.create( + name="Item", + address="Address", + country_code="US", + sector=["Apparel"], + row_index=1, + geocoded_point=Point(0, 0), + status=FacilityListItem.POTENTIAL_MATCH, + source=self.source_three, + ) + + self.match_three = FacilityMatch.objects.create( + status=FacilityMatch.PENDING, + facility=self.facility_one, + facility_list_item=self.list_item_three, + confidence=0.75, + results="", + ) + + self.source_four = Source.objects.create( + source_type=Source.SINGLE, + is_active=True, + is_public=True, + contributor=self.contributor, + ) + + self.list_item_four = FacilityListItem.objects.create( + name="Item", + address="Address", + country_code="US", + sector=["Apparel"], + row_index=1, + geocoded_point=Point(0, 0), + status=FacilityListItem.MATCHED, + source=self.source_four, + ) + + self.match_four = FacilityMatch.objects.create( + status=FacilityMatch.AUTOMATIC, + facility=self.facility_one, + facility_list_item=self.list_item_four, + confidence=1, + results="", + ) + + def test_handle_match_process_result(self): + result_obj_two = { + 'matches': [], + 'item_id': self.list_item_two.id, + 'geocoded_geometry': None, + 'geocoded_address': None, + 'status': self.list_item_two.status, + } + + result_two = handle_external_match_process_result( + self.list_item_two.id, result_obj_two, None, True + ) + self.assertEqual(result_two['status'], 'MATCHED') + + result_obj_three = { + 'matches': [], + 'item_id': self.list_item_three.id, + 'geocoded_geometry': None, + 'geocoded_address': None, + 'status': self.list_item_three.status, + } + + result_three = handle_external_match_process_result( + self.list_item_three.id, result_obj_three, None, True + ) + self.assertEqual(result_three['status'], 'POTENTIAL_MATCH') + self.assertIsNotNone( + result_three['matches'][0]['confirm_match_url'] + ) + self.assertIsNotNone( + result_three['matches'][0]['reject_match_url'] + ) + + result_obj_four = { + 'matches': [], + 'item_id': self.list_item_four.id, + 'geocoded_geometry': None, + 'geocoded_address': None, + 'status': self.list_item_four.status, + } + + result_four = handle_external_match_process_result( + self.list_item_four.id, result_obj_four, None, True + ) + self.assertEqual(result_four['status'], 'NEW_FACILITY')