From 458089ec8c85648e4b08a91a3d6b64b6e209a584 Mon Sep 17 00:00:00 2001 From: Alex Bourreau <7023229+TTalex@users.noreply.github.com> Date: Sun, 12 Jan 2025 23:15:58 +0100 Subject: [PATCH] feat(Prices): API: new filter on product category_tags (#676) Co-authored-by: Raphael Odini --- open_prices/api/prices/filters.py | 3 +++ open_prices/api/prices/tests.py | 39 +++++++++++++++++++++++++++++-- open_prices/api/proofs/tests.py | 4 ++-- 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/open_prices/api/prices/filters.py b/open_prices/api/prices/filters.py index c8835a34..f525de71 100644 --- a/open_prices/api/prices/filters.py +++ b/open_prices/api/prices/filters.py @@ -35,6 +35,9 @@ class PriceFilter(django_filters.FilterSet): created__lte = django_filters.DateTimeFilter( field_name="created", lookup_expr="lte" ) + product__categories_tags__contains = django_filters.CharFilter( + field_name="product__categories_tags", lookup_expr="icontains" + ) class Meta: model = Price diff --git a/open_prices/api/prices/tests.py b/open_prices/api/prices/tests.py index a5f9a3d2..bfc27656 100644 --- a/open_prices/api/prices/tests.py +++ b/open_prices/api/prices/tests.py @@ -41,23 +41,45 @@ "osm_lon": "5.7153387", } +PRODUCT_8001505005707 = { + "code": "8001505005707", + "product_name": "Nocciolata", + "categories_tags": ["en:breakfasts", "en:spreads"], + "labels_tags": ["en:no-gluten", "en:organic"], + "brands_tags": ["rigoni-di-asiago"], + "price_count": 15, +} + class PriceListApiTest(TestCase): @classmethod def setUpTestData(cls): cls.url = reverse("api:prices-list") - PriceFactory(price=15) + location_osm = LocationFactory(**LOCATION_OSM_NODE_652825274) + proof = ProofFactory(type=proof_constants.TYPE_RECEIPT) + PriceFactory( + price=15, + proof_id=proof.id, + location_id=location_osm.id, + location_osm_id=location_osm.osm_id, + location_osm_type=location_osm.osm_type, + ) PriceFactory(price=0) PriceFactory(price=50) def test_price_list(self): # anonymous - with self.assertNumQueries(1 + 1): # thanks to select_related + # thanks to select_related, we only have 2 queries: + # - 1 to count the number of prices + # - 1 to get the prices and their associated proof/location/product + with self.assertNumQueries(1 + 1): response = self.client.get(self.url) self.assertEqual(response.data["total"], 3) self.assertEqual(len(response.data["items"]), 3) self.assertTrue("id" in response.data["items"][0]) self.assertEqual(response.data["items"][0]["price"], 15.00) # default order + self.assertTrue("proof" in response.data["items"][0]) + self.assertTrue("location" in response.data["items"][0]) class PriceListPaginationApiTest(TestCase): @@ -114,11 +136,13 @@ def setUpTestData(cls): cls.user_proof_receipt = ProofFactory( type=proof_constants.TYPE_RECEIPT, owner=cls.user_session.user.user_id ) + cls.product = ProductFactory(**PRODUCT_8001505005707) cls.user_price = PriceFactory( **PRICE_8001505005707, receipt_quantity=2, proof_id=cls.user_proof_receipt.id, owner=cls.user_session.user.user_id, + product=cls.product, ) PriceFactory( **PRICE_APPLES, @@ -296,6 +320,17 @@ def test_price_list_filter_by_created(self): response = self.client.get(url) self.assertEqual(response.data["total"], 0) + def test_price_list_filter_by_product_categories_tags(self): + self.assertEqual(Price.objects.count(), 5) + url = self.url + "?product__categories_tags__contains=en:breakfasts" + # thanks to select_related, we only have 2 queries: + # - 1 to count the number of prices + # - 1 to get the prices (even when filtering on product fields) + with self.assertNumQueries(1 + 1): + response = self.client.get(url) + self.assertEqual(response.data["total"], 1) + self.assertTrue("product" in response.data["items"][0]) + class PriceDetailApiTest(TestCase): @classmethod diff --git a/open_prices/api/proofs/tests.py b/open_prices/api/proofs/tests.py index dc9f9376..d140bfe7 100644 --- a/open_prices/api/proofs/tests.py +++ b/open_prices/api/proofs/tests.py @@ -76,7 +76,7 @@ def test_proof_list(self): # anonymous # thanks to select_related, we only have 2 queries: # - 1 to count the number of proofs of the user - # - 1 to get the proofs and their associated locations (select_related) + # - 1 to get the proofs and their associated location with self.assertNumQueries(2): response = self.client.get(self.url) self.assertEqual(response.status_code, 200) @@ -418,7 +418,7 @@ def setUpTestData(cls): def test_price_tag_list(self): # Check that we can access price tags anonymously - # We only have 2 queries: + # We only have 3 queries: # - 1 to count the number of price tags # - 1 to get the price tags and their associated proof # - 1 to get the price tag predictions (prefetch related)