From cc5a778d11bb0596582c488614b45c176ea7b123 Mon Sep 17 00:00:00 2001 From: Raphael Odini Date: Wed, 22 Nov 2023 17:40:04 +0100 Subject: [PATCH] feat: fetch product data from OpenFoodFacts (#49) * Product get_or_create: get info if created * Fetch data from OFF * Update product * Fixes --- app/crud.py | 12 +++++++++++- app/tasks.py | 16 ++++++++++++++-- app/utils.py | 40 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 63 insertions(+), 5 deletions(-) diff --git a/app/crud.py b/app/crud.py index 0ad39923..6f41d6fe 100644 --- a/app/crud.py +++ b/app/crud.py @@ -87,10 +87,20 @@ def create_product(db: Session, product: ProductCreate): def get_or_create_product(db: Session, product: ProductCreate): + created = False db_product = get_product_by_code(db, code=product.code) if not db_product: db_product = create_product(db, product=product) - return db_product + created = True + return db_product, created + + +def update_product(db: Session, product: ProductBase, update_dict: dict): + for key, value in update_dict.items(): + setattr(product, key, value) + db.commit() + db.refresh(product) + return product # Prices diff --git a/app/tasks.py b/app/tasks.py index 038f51fa..5f6a7b46 100644 --- a/app/tasks.py +++ b/app/tasks.py @@ -2,16 +2,28 @@ from app import crud from app.schemas import LocationCreate, PriceBase, ProductCreate -from app.utils import fetch_location_openstreetmap_details +from app.utils import ( + fetch_location_openstreetmap_details, + fetch_product_openfoodfacts_details, +) def create_price_product(db: Session, price: PriceBase): if price.product_code: # get or create the corresponding product product = ProductCreate(code=price.product_code) - db_product = crud.get_or_create_product(db, product=product) + db_product, created = crud.get_or_create_product(db, product=product) # link the product to the price crud.set_price_product(db, price=price, product=db_product) + # fetch data from OpenFoodFacts if created + if created: + product_openfoodfacts_details = fetch_product_openfoodfacts_details( + product=db_product + ) + if product_openfoodfacts_details: + crud.update_product( + db, product=db_product, update_dict=product_openfoodfacts_details + ) def create_price_location(db: Session, price: PriceBase): diff --git a/app/utils.py b/app/utils.py index 98d486d3..e77ffde7 100644 --- a/app/utils.py +++ b/app/utils.py @@ -1,16 +1,19 @@ import logging import sentry_sdk +from openfoodfacts import API, APIVersion, Country, Environment, Flavor from openfoodfacts.utils import get_logger from OSMPythonTools.nominatim import Nominatim from sentry_sdk.integrations import Integration from sentry_sdk.integrations.logging import LoggingIntegration -from app.schemas import LocationBase +from app.schemas import LocationBase, ProductBase logger = get_logger(__name__) +# Sentry +# ------------------------------------------------------------------------------ def init_sentry(sentry_dsn: str | None, integrations: list[Integration] | None = None): if sentry_dsn: integrations = integrations or [] @@ -26,6 +29,39 @@ def init_sentry(sentry_dsn: str | None, integrations: list[Integration] | None = ) +# OpenFoodFacts +# ------------------------------------------------------------------------------ +def openfoodfacts_product_search(code: str): + client = API( + username=None, + password=None, + country=Country.world, + flavor=Flavor.off, + version=APIVersion.v2, + environment=Environment.org, + ) + return client.product.get(code) + + +def fetch_product_openfoodfacts_details(product: ProductBase): + product_openfoodfacts_details = dict() + try: + response = openfoodfacts_product_search(code=product.code) + if response["status"]: + product_openfoodfacts_details["source"] = Flavor.off + for off_field in ["product_name", "product_quantity", "image_url"]: + if off_field in response["product"]: + product_openfoodfacts_details[off_field] = response["product"][ + off_field + ] + return product_openfoodfacts_details + except Exception: + logger.exception("Error returned from OpenFoodFacts") + return + + +# OpenStreetMap +# ------------------------------------------------------------------------------ def openstreetmap_nominatim_search(osm_id: int, osm_type: str): client = Nominatim() search_query = f"{osm_type}/{osm_id}" @@ -53,5 +89,5 @@ def fetch_location_openstreetmap_details(location: LocationBase): return location_openstreetmap_details except Exception: - logger.exception("error returned from OpenStreetMap") + logger.exception("Error returned from OpenStreetMap") return