File size: 7,438 Bytes
b2034dc
f615298
 
 
 
18a2288
0a9e205
f615298
2e3e6dd
b2034dc
f615298
 
 
 
 
 
 
 
 
 
 
 
271ee91
0552543
b2034dc
0552543
 
 
b2034dc
 
 
f087cee
 
 
b2034dc
 
 
7923ff2
 
0a9e205
 
 
b2034dc
0a9e205
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f615298
5b70291
f615298
 
 
 
 
b2034dc
f087cee
 
 
b2034dc
a9293d4
b2034dc
f615298
 
5b70291
f615298
 
a9293d4
f087cee
 
 
 
a9293d4
 
 
2e3e6dd
 
 
f615298
5b70291
f615298
0552543
5b70291
0552543
18a2288
0552543
 
 
5b70291
18a2288
0a9e205
 
18a2288
0552543
 
 
 
0a9e205
7923ff2
 
 
0a9e205
7923ff2
 
 
 
 
 
 
0a9e205
 
7923ff2
 
 
 
 
 
0a9e205
 
7923ff2
 
 
 
 
 
 
0552543
18a2288
 
2e3e6dd
0552543
18a2288
 
 
0552543
e7ff532
 
 
18a2288
 
b2034dc
18a2288
 
2087868
 
 
18a2288
5b70291
2e3e6dd
 
2087868
5b70291
 
f615298
b2034dc
f615298
5b70291
 
f087cee
5b70291
f087cee
 
 
 
b2034dc
f087cee
b2034dc
 
 
 
 
2e3e6dd
b2034dc
5b70291
f087cee
2e3e6dd
5b70291
a9293d4
 
2e3e6dd
b2034dc
2e3e6dd
b2034dc
f087cee
 
b2034dc
f087cee
b2034dc
a9293d4
b2034dc
5b70291
 
b2034dc
5b70291
2e3e6dd
 
 
5b70291
f615298
 
 
 
5b70291
 
 
2e3e6dd
5b70291
 
2e3e6dd
 
 
5b70291
f615298
 
 
 
f087cee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
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"}