This commit is contained in:
248
test_fatsecret_api.py
Executable file
248
test_fatsecret_api.py
Executable file
@@ -0,0 +1,248 @@
|
||||
#!/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!")
|
||||
Reference in New Issue
Block a user