#!/usr/bin/env python3 """ Скрипт для тестирования API FatSecret Выполняет тестовые запросы к API FatSecret с использованием ключей из конфигурации приложения """ import os import json import time import base64 import asyncio import httpx import urllib.parse import hmac import hashlib from datetime import datetime from dotenv import load_dotenv # Загружаем .env файл current_dir = os.path.dirname(os.path.abspath(__file__)) env_path = os.path.join(current_dir, ".env") load_dotenv(env_path) print(f"✅ Loaded .env from: {env_path}") # Получаем API ключи из переменных окружения FATSECRET_CLIENT_ID = os.environ.get("FATSECRET_CLIENT_ID") FATSECRET_CLIENT_SECRET = os.environ.get("FATSECRET_CLIENT_SECRET") FATSECRET_CUSTOMER_KEY = os.environ.get("FATSECRET_CUSTOMER_KEY") if not FATSECRET_CLIENT_ID or not FATSECRET_CLIENT_SECRET: raise ValueError("FatSecret API keys not found in .env file") print(f"🔑 Using FatSecret API keys: CLIENT_ID={FATSECRET_CLIENT_ID[:8]}...") if FATSECRET_CUSTOMER_KEY: print(f"🔑 Using CUSTOMER_KEY={FATSECRET_CUSTOMER_KEY[:8]}...") class FatSecretClient: """Клиент для работы с API FatSecret""" BASE_URL = "https://platform.fatsecret.com/rest/server.api" def __init__(self, client_id, client_secret): self.client_id = client_id self.client_secret = client_secret self.access_token = None self.token_expires = 0 async def get_access_token(self): """Получение OAuth 2.0 токена для доступа к API""" now = time.time() # Если у нас уже есть токен и он не истек, используем его if self.access_token and self.token_expires > now + 60: return self.access_token print("🔄 Getting new access token...") # Подготовка запроса на получение токена auth_header = base64.b64encode(f"{self.client_id}:{self.client_secret}".encode()).decode() print(f"🔑 Using client_id: {self.client_id}") # Не печатаем секрет полностью, только первые несколько символов для отладки print(f"🔑 Using client_secret: {self.client_secret[:5]}...") async with httpx.AsyncClient() as client: response = await client.post( "https://oauth.fatsecret.com/connect/token", headers={ "Authorization": f"Basic {auth_header}", "Content-Type": "application/x-www-form-urlencoded" }, data={ "grant_type": "client_credentials", "scope": "basic premier" } ) # Проверяем успешность запроса if response.status_code != 200: print(f"❌ Error getting token: {response.status_code}") print(response.text) raise Exception(f"Failed to get token: {response.status_code}") token_data = response.json() self.access_token = token_data["access_token"] self.token_expires = now + token_data["expires_in"] print(f"✅ Got token, expires in {token_data['expires_in']} seconds") return self.access_token async def search_food(self, query, page=0, max_results=10): """Поиск продуктов по названию""" token = await self.get_access_token() async with httpx.AsyncClient() as client: response = await client.post( self.BASE_URL, headers={ "Authorization": f"Bearer {token}", "Content-Type": "application/json" }, json={ "method": "foods.search", "search_expression": query, "page_number": page, "max_results": max_results, "format": "json" } ) if response.status_code != 200: print(f"❌ Error searching food: {response.status_code}") print(response.text) raise Exception(f"Failed to search food: {response.status_code}") return response.json() async def get_food(self, food_id): """Получение детальной информации о продукте по ID""" token = await self.get_access_token() async with httpx.AsyncClient() as client: response = await client.post( self.BASE_URL, headers={ "Authorization": f"Bearer {token}", "Content-Type": "application/json" }, json={ "method": "food.get", "food_id": food_id, "format": "json" } ) if response.status_code != 200: print(f"❌ Error getting food details: {response.status_code}") print(response.text) raise Exception(f"Failed to get food details: {response.status_code}") return response.json() async def run_tests(): """Выполнение тестовых запросов к API FatSecret""" client = FatSecretClient(FATSECRET_CLIENT_ID, FATSECRET_CLIENT_SECRET) # Тест 1: Поиск продуктов print("\n🔍 Testing food search...") search_queries = ["apple", "bread", "chicken breast", "молоко"] for query in search_queries: print(f"\n📋 Searching for: {query}") try: result = await client.search_food(query) # Проверяем структуру ответа if "foods" not in result: print(f"❌ Unexpected response format: {result}") continue # Если нет результатов if "food" not in result["foods"]: print(f"⚠️ No results found for '{query}'") continue food_list = result["foods"]["food"] if not isinstance(food_list, list): food_list = [food_list] # Если только один результат, оборачиваем в список print(f"✅ Found {len(food_list)} results") # Выводим первые 3 результата first_food_id = None for i, food in enumerate(food_list[:3]): food_name = food.get("food_name", "Unknown") food_id = food.get("food_id", "Unknown") food_desc = food.get("food_description", "No description") print(f" {i+1}. [{food_id}] {food_name}") print(f" {food_desc}") # Сохраняем ID первого продукта для следующего теста if i == 0: first_food_id = food_id except Exception as e: print(f"❌ Error during search: {e}") # Тест 2: Получение информации о продукте found_food_id = None for query in search_queries: try: result = await client.search_food(query) if "foods" in result and "food" in result["foods"]: food_list = result["foods"]["food"] if not isinstance(food_list, list): food_list = [food_list] if food_list: found_food_id = food_list[0].get("food_id") break except: continue if found_food_id: print(f"\n🔍 Testing food details for ID: {found_food_id}") try: result = await client.get_food(found_food_id) if "food" not in result: print(f"❌ Unexpected response format: {result}") else: food = result["food"] food_name = food.get("food_name", "Unknown") brand = food.get("brand_name", "Generic") print(f"✅ Got details for: {food_name} [{brand}]") # Выводим информацию о пищевой ценности if "servings" in food: servings = food["servings"] if "serving" in servings: serving_data = servings["serving"] if not isinstance(serving_data, list): serving_data = [serving_data] print("\n📊 Nutrition info per serving:") for i, serving in enumerate(serving_data[:2]): # Выводим до 2 видов порций serving_desc = serving.get("serving_description", "Standard") calories = serving.get("calories", "N/A") protein = serving.get("protein", "N/A") carbs = serving.get("carbohydrate", "N/A") fat = serving.get("fat", "N/A") print(f" Serving {i+1}: {serving_desc}") print(f" Calories: {calories}") print(f" Protein: {protein}g") print(f" Carbohydrates: {carbs}g") print(f" Fat: {fat}g") except Exception as e: print(f"❌ Error getting food details: {e}") if __name__ == "__main__": print("🚀 Starting FatSecret API test...") asyncio.run(run_tests()) print("\n✅ Test completed!")