54 lines
No EOL
1.4 KiB
Python
54 lines
No EOL
1.4 KiB
Python
from dataclasses import dataclass
|
|
from logging import getLogger
|
|
|
|
import httpx
|
|
|
|
logger = getLogger(__name__)
|
|
|
|
|
|
class NotFound(Exception):
|
|
pass
|
|
|
|
|
|
class ParseError(Exception):
|
|
pass
|
|
|
|
|
|
@dataclass
|
|
class ExternalProduct:
|
|
name: str
|
|
kcal: float
|
|
fat: float
|
|
protein: float
|
|
carb: float
|
|
fiber: float
|
|
|
|
|
|
async def find(barcode: str) -> ExternalProduct:
|
|
async with httpx.AsyncClient() as client:
|
|
response = await client.get(
|
|
f"https://world.openfoodfacts.org/api/v2/product/{barcode}.json",
|
|
headers={"User-Agent": "fooder/1.0"},
|
|
)
|
|
|
|
if response.status_code == 404:
|
|
raise NotFound()
|
|
|
|
try:
|
|
data = response.json()
|
|
product_data = data["product"]
|
|
name = product_data.get("product_name_pl") or product_data.get("product_name")
|
|
if brands := product_data.get("brands"):
|
|
name = f"{brands} {name}"
|
|
n = product_data["nutriments"]
|
|
return ExternalProduct(
|
|
name=name,
|
|
kcal=n.get("energy-kcal_100g") or 0.0,
|
|
fat=n.get("fat_100g") or 0.0,
|
|
protein=n.get("proteins_100g") or 0.0,
|
|
carb=n.get("carbohydrates_100g") or 0.0,
|
|
fiber=n.get("fiber_100g") or 0.0,
|
|
)
|
|
except (KeyError, TypeError) as e:
|
|
logger.error("Failed to parse product %s: %s", barcode, e)
|
|
raise ParseError() from e |