Skip to content

Commit

Permalink
fix: create a product if needed after a redis update event (#622)
Browse files Browse the repository at this point in the history
  • Loading branch information
raphael0202 authored Dec 12, 2024
1 parent 20e238e commit 66010fe
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 5 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/container-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ jobs:
echo "CSRF_TRUSTED_ORIGINS=https://prices.openfoodfacts.net" >> $GITHUB_ENV
# Triton server is on the same datacenter as the staging server, so we use the internal IP
echo "TRITON_URI=10.1.0.200:5504" >> $GITHUB_ENV
echo "REDIS_HOST=redis.po_webnet" >> $GITHUB_ENV
# Open Prices fetches fetch data from Production Product Opener,
# so we use the production redis server
echo "REDIS_HOST=10.1.0.113" >> $GITHUB_ENV
echo "REDIS_PORT=6379" >> $GITHUB_ENV
- name: Set various variable for production deployment
if: matrix.env == 'open-prices-org'
Expand Down
22 changes: 18 additions & 4 deletions open_prices/products/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,35 @@ def fetch_and_save_data_from_openfoodfacts(product: Product):
product.save()


def process_update(code: str, flavor: Flavor):
def process_update(code: str, flavor: Flavor) -> None:
"""Process an update of a product from Product Opener.
We update the product table with the latest information from Open Food
Facts. In case the product does not exist, we create it.
:param code: The code of the product
:param flavor: The flavor of the product
"""
product_openfoodfacts_details = common_openfoodfacts.get_product_dict(code, flavor)

if product_openfoodfacts_details:
does_not_exist = False
try:
product = Product.objects.get(code=code)
except Product.DoesNotExist:
logger.info(
"Product %s does not exist in the database, cannot update product table",
code,
)
return
does_not_exist = True

if does_not_exist:
product_openfoodfacts_details["code"] = code
product = Product(**product_openfoodfacts_details)
else:
for key, value in product_openfoodfacts_details.items():
setattr(product, key, value)

for key, value in product_openfoodfacts_details.items():
setattr(product, key, value)
product.save()


Expand Down
80 changes: 80 additions & 0 deletions open_prices/products/tests.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
from decimal import Decimal
from unittest.mock import patch

from django.core.exceptions import ValidationError
from django.test import TestCase, TransactionTestCase
from django.utils import timezone
from openfoodfacts import Flavor

from open_prices.locations.factories import LocationFactory
from open_prices.prices.factories import PriceFactory
from open_prices.products import constants as product_constants
from open_prices.products.factories import ProductFactory
from open_prices.products.models import Product
from open_prices.products.tasks import process_update
from open_prices.proofs.factories import ProofFactory
from open_prices.users.factories import UserFactory

Expand Down Expand Up @@ -189,3 +193,79 @@ def test_update_proof_count(self):
# update_proof_count() should fix proof_count
self.product.update_proof_count()
self.assertEqual(self.product.proof_count, 1)


class TestProcessUpdate(TestCase):
@classmethod
def setUpTestData(cls):
cls.product = ProductFactory(code="0123456789100")

@classmethod
def tearDownClass(cls):
Product.objects.all().delete()
super().tearDownClass()

@patch("open_prices.common.openfoodfacts.get_product")
def test_process_update_product_exists(self, mock_get_product):
product_details = {
"product_name": "Test Product",
"code": self.product.code,
"image_url": "https://images.openfoodfacts.org/images/products/123/1.jpg",
"product_quantity": 1000,
"product_quantity_unit": "g",
"categories_tags": ["en:apples"],
"unique_scans_n": 42,
"owner": "test",
}
fields_to_ignore = ["owner"]
mock_get_product.return_value = product_details

before = timezone.now()
process_update(self.product.code, Flavor.obf)
after = timezone.now()

updated_product = Product.objects.get(code=self.product.code)
for key, value in product_details.items():
if key in fields_to_ignore:
self.assertNotIn(key, updated_product.__dict__)
else:
self.assertEqual(getattr(updated_product, key), value)

self.assertEqual(updated_product.source, Flavor.obf)
self.assertGreater(updated_product.source_last_synced, before)
self.assertLess(updated_product.source_last_synced, after)

@patch("open_prices.common.openfoodfacts.get_product")
def test_process_update_new_product(self, mock_get_product):
# create a new product
code = "1234567891011"
product_details = {
"product_name": "Test Product",
"code": code,
"image_url": "https://images.openfoodfacts.org/images/products/123/1.jpg",
"product_quantity": 1000,
"product_quantity_unit": "g",
"categories_tags": ["en:apples"],
"unique_scans_n": 42,
"owner": "test",
}
fields_to_ignore = ["owner"]
mock_get_product.return_value = product_details

before = timezone.now()
process_update(code, Flavor.opff)
after = timezone.now()

results = Product.objects.filter(code=code)
self.assertEqual(results.count(), 1)
create_product = results.first()

for key, value in product_details.items():
if key in fields_to_ignore:
self.assertNotIn(key, create_product.__dict__)
else:
self.assertEqual(getattr(create_product, key), value)

self.assertEqual(create_product.source, Flavor.opff)
self.assertGreater(create_product.source_last_synced, before)
self.assertLess(create_product.source_last_synced, after)

0 comments on commit 66010fe

Please sign in to comment.