Spaces:
Sleeping
Sleeping
| 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) | |
| 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) | |
| 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) | |
| async def health_check(): | |
| """API durumunu kontrol eder""" | |
| return {"status": "healthy"} | |