75 lines
2.6 KiB
Python
75 lines
2.6 KiB
Python
|
|
from model import Product, Ingredient, Recipe, Measurement
|
||
|
|
from ingredient_parser import parse_multiple_ingredients
|
||
|
|
from typing import List
|
||
|
|
from bs4 import BeautifulSoup
|
||
|
|
|
||
|
|
import httpx
|
||
|
|
import json
|
||
|
|
import db
|
||
|
|
|
||
|
|
async def find_existing_product(conn, ingredient: str) -> Product:
|
||
|
|
async for item in db.find_product_by_tag(conn, ingredient):
|
||
|
|
return item
|
||
|
|
return None
|
||
|
|
|
||
|
|
def parse_ingredient_from_nlp(ingredients: List[str]) -> Ingredient:
|
||
|
|
results = []
|
||
|
|
for ingredient in parse_multiple_ingredients(ingredients):
|
||
|
|
name = ingredient.name.text
|
||
|
|
if ingredient.amount:
|
||
|
|
measure = Measurement(ingredient.amount[0].quantity, ingredient.amount[0].unit)
|
||
|
|
else:
|
||
|
|
measure = Measurement(0, '')
|
||
|
|
|
||
|
|
results.append(Ingredient(
|
||
|
|
id=0,
|
||
|
|
source=ingredient.sentence,
|
||
|
|
name=name,
|
||
|
|
product=None,
|
||
|
|
measure=measure,
|
||
|
|
preparation=ingredient.preparation.text if ingredient.preparation else ''
|
||
|
|
))
|
||
|
|
|
||
|
|
return results
|
||
|
|
|
||
|
|
async def match_existing_products(conn, ingredients: List[Ingredient]) -> List[Ingredient]:
|
||
|
|
for ingredient in ingredients:
|
||
|
|
if not ingredient.product:
|
||
|
|
existing = await find_existing_product(conn, ingredient.name)
|
||
|
|
if existing:
|
||
|
|
ingredient.product = existing
|
||
|
|
return ingredients
|
||
|
|
|
||
|
|
async def get_recipe_from_ldata(conn, url: str, ldata: dict) -> dict:
|
||
|
|
ingredients = parse_ingredient_from_nlp(ldata['recipeIngredient'])
|
||
|
|
ingredients = await match_existing_products(conn, ingredients)
|
||
|
|
return Recipe(
|
||
|
|
id=0,
|
||
|
|
name=ldata['name'],
|
||
|
|
link=url,
|
||
|
|
ingredients=ingredients
|
||
|
|
)
|
||
|
|
|
||
|
|
async def parse_recipe(conn, url: str) -> dict:
|
||
|
|
headers = {
|
||
|
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
|
||
|
|
"Accept-Language": "en-US,en;q=0.9",
|
||
|
|
"Referer": "https://www.google.com/",
|
||
|
|
}
|
||
|
|
|
||
|
|
# Load the requested URL with headers
|
||
|
|
async with httpx.AsyncClient() as client:
|
||
|
|
response = await client.get(url, headers=headers)
|
||
|
|
response.raise_for_status()
|
||
|
|
|
||
|
|
# Extract the recipe ld+json data
|
||
|
|
soup = BeautifulSoup(response.text, 'html.parser')
|
||
|
|
for ld in soup.find_all('script', type='application/ld+json'):
|
||
|
|
try:
|
||
|
|
data = json.loads(ld.text)
|
||
|
|
if data['@type'].lower() == 'recipe':
|
||
|
|
return { 'raw': data, 'recipe': await get_recipe_from_ldata(conn, url, data) }
|
||
|
|
except (json.decoder.JSONDecodeError, KeyError):
|
||
|
|
pass
|
||
|
|
|
||
|
|
return None
|