| import os |
| import requests |
| import gradio as gr |
| from transformers import pipeline |
|
|
| |
| HF_TOKEN = os.environ.get("HUGGINGFACEHUB_API_TOKEN") |
| SPOONACULAR_API_KEY = os.environ.get("SPOONACULAR_API_KEY") |
|
|
| |
| |
| translator_et_en = pipeline( |
| task="translation", |
| model="Helsinki-NLP/opus-mt-et-en", |
| token=HF_TOKEN, |
| ) |
|
|
| |
| translator_en_et = pipeline( |
| task="translation", |
| model="Helsinki-NLP/opus-mt-en-et", |
| token=HF_TOKEN, |
| ) |
|
|
| SPOONACULAR_URL = "https://api.spoonacular.com/recipes/complexSearch" |
|
|
|
|
| def search_recipes(ingredients_et: str, |
| max_calories: float, |
| min_protein: float, |
| max_carbs: float, |
| max_fat: float, |
| n_results: int): |
|
|
| if not ingredients_et.strip(): |
| return "❗ Palun sisesta vähemalt üks koostisosa." |
|
|
| if not SPOONACULAR_API_KEY: |
| return "❗ SPOONACULAR_API_KEY pole seadistatud (Spaces Secrets)." |
|
|
| |
| try: |
| translated = translator_et_en(ingredients_et)[0]["translation_text"] |
| except Exception as e: |
| return f"❗ Tõlkimine eesti → inglise ebaõnnestus: {e}" |
|
|
| include_ingredients = translated |
|
|
| |
| params = { |
| "apiKey": SPOONACULAR_API_KEY, |
| "includeIngredients": include_ingredients, |
| "number": int(n_results), |
| "ignorePantry": True, |
| "addRecipeInformation": True, |
| "addRecipeNutrition": True, |
| "minProtein": min_protein, |
| "maxCalories": max_calories, |
| "maxCarbs": max_carbs, |
| "maxFat": max_fat, |
| "instructionsRequired": True, |
| "sort": "calories", |
| "sortDirection": "asc", |
| } |
|
|
| try: |
| resp = requests.get(SPOONACULAR_URL, params=params) |
| except Exception as e: |
| return f"❗ Päring Spoonacular API-sse ebaõnnestus: {e}" |
|
|
| if resp.status_code != 200: |
| return f"❗ Spoonacular veakood {resp.status_code}: {resp.text}" |
|
|
| data = resp.json() |
| results = data.get("results", []) |
|
|
| if not results: |
| return "ℹ️ Sobivaid retsepte ei leitud nende piirangutega." |
|
|
| blocks = [] |
|
|
| |
| for i, r in enumerate(results, start=1): |
| title_en = r.get("title", "Nimetu retsept") |
|
|
| |
| try: |
| title_et = translator_en_et(title_en)[0]["translation_text"] |
| except Exception: |
| title_et = title_en |
|
|
| |
| lines = [ |
| f"### {i}. {title_et}", |
| f"_({title_en})_", |
| ] |
|
|
| |
| calories = None |
| protein = None |
| carbs = None |
| fat = None |
|
|
| nutrition = r.get("nutrition", {}) |
| nutrients = nutrition.get("nutrients", []) if nutrition else [] |
|
|
| for n in nutrients: |
| name = n.get("name") |
| amount = n.get("amount") |
| unit = n.get("unit") |
|
|
| if name == "Calories": |
| calories = f"{amount:.0f} {unit}" |
| if name == "Protein": |
| protein = f"{amount:.1f} {unit}" |
| if name == "Carbohydrates": |
| carbs = f"{amount:.1f} {unit}" |
| if name == "Fat": |
| fat = f"{amount:.1f} {unit}" |
|
|
| if calories: |
| lines.append(f"- Kalorid: **{calories}**") |
| if protein: |
| lines.append(f"- Valgud: **{protein}**") |
| if carbs: |
| lines.append(f"- Süsivesikud: **{carbs}**") |
| if fat: |
| lines.append(f"- Rasvad: **{fat}**") |
|
|
| |
| url = r.get("sourceUrl") or r.get("spoonacularSourceUrl") |
| if not url and "id" in r: |
| url = f"https://spoonacular.com/recipes/{title_en.replace(' ', '-')}-{r['id']}" |
|
|
| if url: |
| lines.append(f"- Link retseptile: {url}") |
|
|
| blocks.append("\n".join(lines)) |
|
|
| return "\n\n---\n\n".join(blocks) |
|
|
|
|
| |
| with gr.Blocks(title="Retseptid eesti koostisosadest") as demo: |
| gr.Markdown( |
| """ |
| # 🥗 Retseptigeneraator |
| |
| Sisesta koostisosad **eesti keeles** (nt `kartul, porgand, kana`) |
| ning vali toitumispiirangud. Rakendus: |
| 1. Tõlgib koostisosad inglise keelest eesti keelde |
| 2. Pärib retsepte Spoonacular API-st |
| 3. Filtreerib retseptid kalorite, valgu, süsivesikute ja rasva järgi |
| 4. Kuvab retsepti nime (eesti + inglise), toitumisinfo ja lingi retseptile |
| """ |
| ) |
|
|
| with gr.Row(): |
| ingredients_input = gr.Textbox( |
| label="Koostisosad eesti keeles (komaga eraldatud)", |
| placeholder="nt: kartul, porgand, kana", |
| lines=2, |
| ) |
|
|
| with gr.Row(): |
| max_calories_input = gr.Slider( |
| minimum=100, |
| maximum=2000, |
| step=50, |
| value=800, |
| label="Maksimaalne kalorite kogus retsepti kohta (kcal)", |
| ) |
| min_protein_input = gr.Slider( |
| minimum=0, |
| maximum=80, |
| step=5, |
| value=10, |
| label="Minimaalne valgu kogus (g)", |
| ) |
|
|
| with gr.Row(): |
| max_carbs_input = gr.Slider( |
| minimum=0, |
| maximum=300, |
| step=5, |
| value=100, |
| label="Maksimaalne süsivesikute kogus (g)", |
| ) |
| max_fat_input = gr.Slider( |
| minimum=0, |
| maximum=150, |
| step=5, |
| value=50, |
| label="Maksimaalne rasvade kogus (g)", |
| ) |
| n_results_input = gr.Slider( |
| minimum=1, |
| maximum=10, |
| step=1, |
| value=3, |
| label="Mitu retsepti kuvada", |
| ) |
|
|
| output_md = gr.Markdown(label="Leitud retseptid") |
|
|
| run_btn = gr.Button("Otsi retsepte") |
|
|
| run_btn.click( |
| fn=search_recipes, |
| inputs=[ |
| ingredients_input, |
| max_calories_input, |
| min_protein_input, |
| max_carbs_input, |
| max_fat_input, |
| n_results_input, |
| ], |
| outputs=output_md, |
| ) |
|
|
| if __name__ == "__main__": |
| demo.launch() |
|
|