mt / app /main.py
CihanYakar's picture
debug
271ee91
from fastapi import FastAPI, HTTPException, Request, Response
from fastapi.middleware.cors import CORSMiddleware
import json
from datetime import datetime
import os
import requests
import pytz
from typing import Dict
import traceback
import hashlib
app = FastAPI()
# CORS ayarları
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
CACHE_FILE = "cache/menu_cache.json"
CLAUDE_API_KEY = os.getenv("CLAUDE_API_KEY")
if not CLAUDE_API_KEY:
raise Exception("CLAUDE_API_KEY environment variable is not set")
def calculate_etag(data: Dict) -> str:
"""Verilen data için ETag hesaplar"""
# Sadece menu kısmını hash'le, date değişse bile aynı menu için aynı ETag dönsün
menu_data = data.get("menu", {})
data_str = json.dumps(menu_data, sort_keys=True)
return hashlib.md5(data_str.encode()).hexdigest()
def get_current_date() -> str:
"""Günün tarihini YYYY-MM-DD formatında döndürür"""
tz = pytz.timezone('Europe/Istanbul')
return datetime.now(tz).strftime("%Y-%m-%d")
def get_current_date_and_season() -> tuple:
"""Günün tarihini ve mevsimini döndürür"""
# Türkiye saat dilimini ayarla
tz = pytz.timezone('Europe/Istanbul')
current_date = datetime.now(tz)
# Türkçe ay isimleri
months = {
1: "Ocak", 2: "Şubat", 3: "Mart", 4: "Nisan",
5: "Mayıs", 6: "Haziran", 7: "Temmuz", 8: "Ağustos",
9: "Eylül", 10: "Ekim", 11: "Kasım", 12: "Aralık"
}
# Mevsimi belirle
month = current_date.month
if month in [12, 1, 2]:
season = "Kış"
elif month in [3, 4, 5]:
season = "İlkbahar"
elif month in [6, 7, 8]:
season = "Yaz"
else:
season = "Sonbahar"
# Tarih stringini formatla
date_str = f"{current_date.day} {months[current_date.month]} {current_date.year}"
return date_str, season
def load_cache() -> Dict:
"""Cache dosyasını okur"""
try:
if os.path.exists(CACHE_FILE):
with open(CACHE_FILE, "r", encoding="utf-8") as f:
cache_data = json.load(f)
current_date = get_current_date()
# Sadece bugünün cache'ini döndür
if cache_data.get("date") == current_date:
return cache_data
except Exception as e:
print(f"Cache okuma hatası: {str(e)}\nTrace: {traceback.format_exc()}")
return {}
def save_cache(cache_data: Dict) -> None:
"""Cache'i dosyaya kaydeder"""
try:
# date field'ının olduğundan emin ol
if "date" not in cache_data:
cache_data["date"] = get_current_date()
with open(CACHE_FILE, "w", encoding="utf-8") as f:
json.dump(cache_data, f, ensure_ascii=False, indent=2)
except Exception as e:
error_detail = f"Cache yazma hatası: {str(e)}\nTrace: {traceback.format_exc()}"
print(error_detail)
raise Exception(error_detail)
async def generate_menu() -> Dict:
"""Claude API kullanarak menü oluşturur"""
try:
API_URL = "https://api.anthropic.com/v1/messages"
headers = {
"Content-Type": "application/json",
"anthropic-version": "2023-06-01",
"x-api-key": CLAUDE_API_KEY
}
current_date, current_season = get_current_date_and_season()
data = {
"model": "claude-3-opus-20240229",
"max_tokens": 1024,
"messages": [{
"role": "user",
"content": f"""Günlük türk yemeği menüsü yaz JSON formatında, 3 öğün olsun. Bugün {current_date} ({current_season} mevsimi), dolayısıyla mevsime dikkat et. Sadece JSON ı yaz.
Lütfen mevsime uygun ve sağlıklı seçimler yap. Sadece menüyü liste olarak ver ve menüyü Türkçe hazırla.
ÖRNEK:
{{
"kahvalti": [
"Menemen",
"Beyaz Peynir",
"Zeytin",
"Domates",
"Taze Ekmek",
"Çay"
]
,
"ogle": [
"Mercimek Çorbası",
"Tavuk Sote",
"Pilav",
"Mevsim Salatası",
"Ayran"
]
,
"aksam": [
"Yayla Çorbası",
"Etli Kuru Fasulye",
"Bulgur Pilavı",
"Cacık",
"Baklava"
]
}}"""
}]
}
print("API isteği gönderiliyor...")
response = requests.post(API_URL, json=data, headers=headers)
print(f"API yanıtı status: {response.status_code}")
if response.status_code == 200:
menu_text = response.json()['content'][0]['text']
# JSON string'i parse et
menu_json = json.loads(menu_text)
return {
"date": get_current_date(),
"menu": menu_json
}
else:
error_msg = f"API hata kodu: {response.status_code}, Yanıt: {response.text}"
print(error_msg)
raise Exception(error_msg)
except Exception as e:
error_detail = f"Menü oluşturma hatası: {str(e)}\nTrace: {traceback.format_exc()}"
print(error_detail)
raise Exception(error_detail)
@app.get("/menu")
async def get_menu(request: Request, response: Response):
"""Günün menüsünü getirir, yoksa oluşturur"""
try:
cache = load_cache()
current_date = get_current_date()
# Cache varsa ve bugüne aitse
if cache and cache.get("date") == current_date:
# ETag hesapla ve header'a ekle
etag = f'W/"{calculate_etag(cache)}"' # Weak ETag formatı
response.headers["ETag"] = etag
response.headers["Cache-Control"] = "public, must-revalidate"
# If-None-Match header'ı kontrol et
if_none_match = request.headers.get("if-none-match")
if if_none_match and if_none_match == etag:
return Response(status_code=304) # Not Modified
return cache
# Cache yoksa veya eski ise yeni menü oluştur
print("Yeni menü oluşturuluyor...")
menu_data = await generate_menu()
try:
print("Cache'e kaydediliyor...")
save_cache(menu_data)
print("Cache güncellendi")
# Yeni ETag hesapla ve header'a ekle
etag = f'W/"{calculate_etag(menu_data)}"'
response.headers["ETag"] = etag
response.headers["Cache-Control"] = "public, must-revalidate"
except Exception as e:
print(f"Cache kaydetme hatası: {str(e)}\nTrace: {traceback.format_exc()}")
return menu_data
except Exception as e:
error_detail = f"Genel hata: {str(e)}\nTrace: {traceback.format_exc()}"
print(error_detail)
raise HTTPException(status_code=500, detail=error_detail)
@app.post("/reset")
async def reset_cache():
"""Cache'i temizler"""
try:
if os.path.exists(CACHE_FILE):
os.remove(CACHE_FILE)
print("Cache dosyası silindi")
return {"message": "Cache temizlendi"}
except Exception as e:
error_detail = f"Cache temizleme hatası: {str(e)}\nTrace: {traceback.format_exc()}"
print(error_detail)
raise HTTPException(status_code=500, detail=error_detail)
@app.get("/health")
async def health_check():
"""API durumunu kontrol eder"""
return {"status": "healthy"}