File size: 30,326 Bytes
fddb26d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
import pandas as pd
import re
import numpy as np


# ==============================================================================
# STAGE 1: DATA LOADING & MERGING
# ==============================================================================
# Veri setleri okunuyor (Dosyaların çalışma dizininde olduğu varsayılmıştır)
try:
    df1 = pd.read_json("first400.json")
    df2 = pd.read_json("222veri.json")
    print("Veri setleri başarıyla yüklendi.")
except FileNotFoundError:
    print("Hata: JSON dosyaları bulunamadı. Lütfen dosya yollarını kontrol edin.")

# Kaynak takibi için etiketleme ve birleştirme
df1['source_file'], df2['source_file'] = 'V1', 'V2'
df_master = pd.concat([df1, df2], ignore_index=True)

# Tüm sütun isimlerini liste halinde gör
print("--- Master Dataset Sütunları ---")
print(list(df_master.columns))

# Toplam kaç sütun olduğunu da yazdırma
print(f"\nToplam Sütun Sayısı: {len(df_master.columns)}")

# ==============================================================================
# STAGE 2: COLUMN PRUNING (CLEANING)
# ==============================================================================
# Uygulama için katma değeri olmayan teknik ve reklam odaklı sütunların temizlenmesi
redundant_columns = [
    'claimThisBusiness', 'googleFoodUrl', 'hotelAds', 'gasPrices', 
    'searchPageUrl', 'isAdvertisement', 'phoneUnformatted', 
    'additionalOpeningHours', 'updatesFromCustomers', 'checkInDate', 
    'checkOutDate', 'hotelStars', 'hotelDescription', 'reviewsDistribution',
    'scrapedAt', 'locatedIn', 'language', 'countryCode', 'price',
    'peopleAlsoSearch', 'placesTags', 'reviewsTags', 'imageCategories', 'reserveTableUrl'
]

# Mevcut olan sütunları filtrele ve sil
df_master = df_master.drop(columns=[col for col in redundant_columns if col in df_master.columns])
print(f"Gereksiz sütunlar temizlendi. Kalan sütun: {df_master.shape[1]}")

print(f"Temizlik sonrası kalan sütun sayısı: {df_master.shape[1]}")


# ==============================================================================
# STAGE 3: CATEGORICAL & KEYWORD FILTERING (NOISE REDUCTION)
# ==============================================================================
# Ticari, tıbbi ve gezi rehberi dışı kategorilerin elenmesi
excluded_categories = [
    'Travel agency', 'Tour agency', 'Sightseeing tour agency', 'Tour operator',
    'Tourist information center', 'Car rental agency', 'Limousine service', 
    'Parking lot', 'Clinic', 'Medical center', 'Corporate office', 
    'Lodging', 'Hotel', 'Real estate agency'
]

# Başlıkta (Title) geçtiğinde kaydı geçersiz kılan anahtar kelimeler
excluded_keywords = [
    'Turizm', 'Travel', 'Agency', 'Acenta', 'Clinic', 'Klinik', 
    'Rent a Car', 'Transfer', 'Rota', 'Medical', 'Hizmetleri', 
    'Dog', 'Tour Poınt', 'Toplanma Salonu', 'Silivrikapı Geçiş', 'Mother and baby'
]

# Filtreleme operasyonu
df_master = df_master[~df_master['categoryName'].isin(excluded_categories)]
pattern = '|'.join(excluded_keywords)
df_master = df_master[~df_master['title'].str.contains(pattern, case=False, na=False)]

print(f"Ticari ve ilgisiz yerler elendi. Güncel mekan sayısı: {len(df_master)}")

# ==============================================================================
# STAGE 4: GEO-NORMALIZATION (CITY MAPPING)
# ==============================================================================
def final_city_cleaner(text):
    """Karmaşık konum verilerini standart ilçe isimlerine map eder."""
    if pd.isna(text): return "Diğer"
    text = str(text).lower().strip()
    
    # Manuel düzeltme gerektiren spesifik lokasyonlar
    special_mapping = {
        "haskoy": "Beyoğlu", "bulbol area": "Beyoğlu", "galataport": "Beyoğlu",
        "molla güarani": "Fatih", "sultanahmet/fatih": "Fatih",
        "vezirhan/fatih/fatih": "Fatih", "eyüp/eyüpsultan": "Eyüpsultan"
    }
    if text in special_mapping: return special_mapping[text]
    
    # İlçe anahtar kelime taraması
    districts = ["fatih", "beyoğlu", "beyoglu", "kadıköy", "kadikoy", "beşiktaş", 
                 "besiktas", "üsküdar", "uskudar", "şişli", "sisli", "eyüpsultan", 
                 "eyüp", "zeytinburnu"]
    
    for d in districts:
        if d in text:
            return d.replace('beyoglu', 'beyoğlu').replace('besiktas', 'beşiktaş').title()
    return text.title()

df_master['city'] = df_master['city'].apply(final_city_cleaner)

# Sonuçları kontrol et
print("--- Temizlenmiş Şehir Dağılımı ---")
print(df_master['city'].value_counts())

# ==============================================================================
# STAGE 5: DATA IMPUTATION (NULL FILLING)
# ==============================================================================

# Puan ve yorum sayısı sütunlarındaki boşlukları 0 ile doldur
df_master['totalScore'] = df_master['totalScore'].fillna(0)
df_master['reviewsCount'] = df_master['reviewsCount'].fillna(0)

# Telefon ve web sitesi sütunlarını standart yer tutucularla doldur
df_master['phone'] = df_master['phone'].fillna("No contact information")
df_master['website'] = df_master['website'].fillna("Website not available")

# Eğer street hala null ise neighborhood ile doldur, o da yoksa boş bırak
df_master['street'] = df_master['street'].fillna(df_master['neighborhood']).fillna("")

# additionalInfo Null olanları boş liste ile doldurma
df_master['additionalInfo'] = df_master['additionalInfo'].apply(lambda x: x if isinstance(x, (list, dict)) else [])

# Değişikliği doğrula
print(f"Puan ve yorum sayısı boş olan satırlar 0 ile dolduruldu.")
print(f"Kalan boş totalScore sayısı: {df_master['totalScore'].isna().sum()}")
print("'phone' ve 'website' sütunlarındaki boşluklar yer tutucu metinlerle dolduruldu.")
print("Street sütunu için fallback (neighborhood) uygulandı.")


# ==============================================================================
# STAGE 6: FEATURE ENGINEERING (BAYESIAN RATING)
# ==============================================================================
def calculate_weighted_rating(df):
    """Düşük yorum sayılı yüksek puanlı mekanları dengelemek için Bayesian Average kullanır."""
    C = df['totalScore'].mean() # Global ortalama puan
    m = df['reviewsCount'].quantile(0.25) # Minimum güvenilirlik eşiği (Q1)
    
    return df.apply(lambda row: (row['totalScore'] * row['reviewsCount'] + C * m) / 
                                (row['reviewsCount'] + m), axis=1)

# Eksik puanları 0 ile normalize etme ve ağırlıklı puanı hesaplama
df_master[['totalScore', 'reviewsCount']] = df_master[['totalScore', 'reviewsCount']].fillna(0)
df_master['weighted_score'] = calculate_weighted_rating(df_master)
print("Bayesian Ağırlıklı Puanlama (weighted_score) hesaplandı.")


# ==============================================================================
# STAGE 7: DYNAMIC FEATURE EXTRACTION (ONE-HOT ENCODING)
# ==============================================================================
def discover_all_features(df):
    """additionalInfo içindeki tüm hiyerarşik özellikleri keşfeder."""
    unique_features = set()
    for info in df['additionalInfo']:
        if isinstance(info, dict):
            for category, items in info.items():
                if isinstance(items, list):
                    for item in items:
                        for feature_name in item.keys():
                            unique_features.add(f"{category}_{feature_name}")
    return list(unique_features)

# Keşfedilen her özellik için dinamik sütun oluşturma (True/False)
all_possible_columns = discover_all_features(df_master)

def dynamic_filler(row, feature_path):
    category, feature_name = feature_path.split('_', 1)
    info = row.get('additionalInfo', {})
    if not isinstance(info, dict): return False
    
    category_list = info.get(category, [])
    if isinstance(category_list, list):
        for item in category_list:
            if item.get(feature_name) is True: return True
    return False

for feature in all_possible_columns:
    clean_col_name = feature.replace(" ", "_").replace(":", "").lower()
    df_master[clean_col_name] = df_master.apply(lambda r: dynamic_filler(r, feature), axis=1)

print(f"Dinamik Özellik Çıkarımı Tamamlandı. {len(all_possible_columns)} yeni özellik eklendi.")
print("Veri işleme süreci başarıyla sonuçlandı.")


# ==============================================================================
# STAGE 8: TEMPORAL ANALYSIS & OPENING HOURS IMPUTATION
# ==============================================================================
def check_24_7(hours_list):
    """Mekanın 7/24 açık olup olmadığını kontrol eder."""
    if not isinstance(hours_list, list) or len(hours_list) == 0:
        return False
    return all("Open 24 hours" in str(day.get('hours', '')) for day in hours_list)

def impute_opening_hours(row):
    """Eksik çalışma saatlerini kategori bazlı tahmini verilerle doldurur."""
    hours = row.get('openingHours')
    if isinstance(hours, list) and len(hours) > 0:
        return hours
    
    cat = str(row.get('main_category', ''))
    # Kategori bazlı standart çalışma saatleri ataması
    mapping = {
        'Religious & Spiritual': "Open according to prayer times (Generally 05:00 - 22:00)",
        'Museum & Art': "Generally 09:00 - 17:00 (Check before visiting, may be closed on Mondays)",
        'History & Heritage': "Generally 09:00 - 17:00 (Check before visiting, may be closed on Mondays)",
        'Nature & Parks': "Open 24 hours every day of the week",
        'Squares & Plazas': "Open 24 hours every day of the week",
        'Shopping & Traditional Bazaar': "Generally 09:00 - 19:00 (May vary on Sundays)",
        'Food & Drink': "Generally 08:00 - 22:00 (Varies by establishment)"
    }
    return mapping.get(cat, "Opening hours not specified")

# 24/7 bayrağı ve UI için görüntüleme sütunları oluşturuluyor
df_master['is_24_7'] = df_master['openingHours'].apply(check_24_7)
df_master['openingHours_display'] = df_master.apply(impute_opening_hours, axis=1)
df_master['is_hours_estimated'] = df_master['openingHours'].apply(lambda x: not (isinstance(x, list) and len(x) > 0))

# ==============================================================================
# STAGE 9: LOCALIZATION & SMART TITLE SYNTHESIS
# ==============================================================================
def tr_title(text):
    """Türkçe karakter duyarlı Title Case dönüşümü yapar."""
    if pd.isna(text): return ""
    words = str(text).split()
    fixed_words = []
    for w in words:
        if w.startswith('i'): fixed_words.append('İ' + w[1:].lower())
        elif w.startswith('ı'): fixed_words.append('I' + w[1:].lower())
        else: fixed_words.append(w.capitalize())
    return " ".join(fixed_words)

def smart_title_fix(row):
    """TR ve ENG başlıkları akıllıca birleştirerek 'display_title' oluşturur."""
    eng_title = str(row.get('title', '')).strip()
    tr_title_val = str(row.get('subTitle', '')).strip()
    
    if not tr_title_val or tr_title_val.lower() == eng_title.lower() or tr_title_val == "None":
        return eng_title
    return f"{tr_title_val} ({eng_title})"

df_master['subTitle'] = df_master['subTitle'].apply(tr_title)
df_master['display_title'] = df_master.apply(smart_title_fix, axis=1)

# ==============================================================================
# STAGE 10: SPATIAL DATA EXTRACTION (COORDINATES)
# ==============================================================================
# Karmaşık location sözlüğü latitude ve longitude sütunlarına ayrıştırılıyor
coords = df_master['location'].apply(lambda x: pd.Series(x) if isinstance(x, dict) else pd.Series({'lat': np.nan, 'lng': np.nan}))
df_master['latitude'], df_master['longitude'] = coords['lat'], coords['lng']
df_master = df_master.drop(columns=['location'])

# ==============================================================================
# STAGE 11: QUALITY SCORING & DEDUPLICATION
# ==============================================================================
def calculate_record_score(row):
    """Veri doluluğuna göre kaydın kalite puanını hesaplar (Max: 35)."""
    score = 0
    if pd.notna(row.get('description')) and len(str(row['description'])) > 5: score += 10
    if isinstance(row.get('additionalInfo'), (dict, list)) and len(row['additionalInfo']) > 0: score += 8
    if isinstance(row.get('openingHours'), list) and len(row['openingHours']) > 0: score += 7
    if pd.notna(row.get('subTitle')) and len(str(row['subTitle'])) > 2: score += 5
    if pd.notna(row.get('reviewsCount')): score += 3
    if pd.notna(row.get('totalScore')): score += 2
    return score

# Skorlama ve en kaliteli kaydı üstte tutacak şekilde sıralama
df_master['quality_score'] = df_master.apply(calculate_record_score, axis=1)
df_master = df_master.sort_values(by=['placeId', 'quality_score'], ascending=[True, False])

# Mükerrer kayıtların (Duplicate) temizlenmesi
duplicate_count = df_master['placeId'].duplicated().sum()
df_master = df_master.drop_duplicates(subset=['placeId'], keep='first')

print(f"Tekilleştirme: {duplicate_count} mükerrer kayıt elendi.")
print(f"Benzersiz mekan sayısı: {len(df_master)}")

# ==============================================================================
# STAGE 12: CHARACTER SET VALIDATION (FOREIGN LANGUAGE FILTER)
# ==============================================================================
# Latin ve Türkçe alfabesi dışındaki (Arapça, Kiril vb.) karakterleri yakalayan Regex
non_latin_regex = r'[^\x00-\x7FİıĞğÜüŞşÖöÇçâîûÂÎÛ\s\d\.,\-\(\)\&\'\!]'

foreign_data = df_master[df_master['title'].str.contains(non_latin_regex, na=False, regex=True)].copy()

print(f"--- Toplam {len(foreign_data)} Adet Yabancı Alfabeli/Karakterli Veri Bulundu ---\n")

if len(foreign_data) > 0:
    # Tüm listeyi görmek için kısıtlamayı kaldıralım
    pd.set_option('display.max_rows', None)
    
    print("--- Silinecek Yabancı Kayıtlar ---")
    # Listeyi ekrana yazdır (Örn: Arapça, Çince veya Kiril isimli mekanlar)
    print(foreign_data[['title', 'city', 'source_file']])
    
    # Hangi dosyadan ne kadar yabancı veri geldiğini görelim
    print("\n--- Kaynak Dosya Dağılımı ---")
    print(foreign_data['source_file'].value_counts())

    # 3. TEMİZLİK: Bu yabancı kayıtları ana veriden (df_master) silelim
    df_master = df_master.drop(foreign_data.index)
    print(f"\n✅ Yabancı alfabeli {len(foreign_data)} kayıt temizlendi.")
    
    pd.reset_option('display.max_rows')
else:
    print("Latin dışı hiçbir karakter bulunamadı.")


# ==============================================================================
# STAGE 13: CATEGORY MAPPING & CLEANING
# ==============================================================================
# 1. Kara Liste Tanımlama (Gezi rehberinde asla olmaması gerekenler)
kara_liste = [
    'Turizm', 'Tourism', 'Agency', 'Travel', 'Clinic', 'Coiffeur', 'Halı', 'Carpet', 
    'Workshop', 'Medical', 'Acente', 'Gayrimenkul', 'Emlak', 'Acentesi', 'Global', 'AGN TURİZM','otobüsleri', 'kalkış noktası', 'bus station', 'departure'
]

# 2. Gelişmiş Mapping Kuralları (Sıralama Önceliği Korunarak Güncellendi)
# NOT: 'Food & Drink' en sondadır. Böylece tarihi bir kafe önce 'History' olarak yakalanır.
mapping_rules = {

    'Food & Drink': [
        'restaurant', 'cafe', 'coffee', 'kebab', 'bakery', 'pub', 'bar', 'breakfast', 'lokanta', 'kahve', 'fırın', 
        'tatlıcı', 'pastane', 'meyhane', 'döner', 'pizza', 'steakhouse', 'grill', 'bistro', 'patisserie', 'brasserie', 
        'cafeteria', 'mutfağı', 'sofrası', 'kebap', 'köfte', 'dürüm', 'restorant', 'winery', 'gastronomi'
    ],

    'Museum & Art': [
        'museum', 'art', 'gallery', 'exhibition', 'müze','müzesi', 'galeri', 'sergi', 'sanat', 'atolye', 'atölye',
        'theater', 'opera', 'sinema', 'kütüphane', 'library', 'kültür merkezi', 'cultural center','mural', 'murral', 'murales', 'streetart', 'graffiti', 'stairs', 'staircase', 'merdiven'
    ],
    
    'Religious & Spiritual': [
        'mosque', 'church', 'synagogue', 'tomb', 'hazire', 'cemetery', 'cami','camii' 'kilise', 'havra', 
        'türbe', 'hazire', 'mezarlık', 'dergah', 'tekke', 'namazgah', 'kabri', 'mezarı', 'kabristan', 
        'mausoleum', 'cathedral'
    ],
    
    'History & Heritage': [
        'historical', 'monument', 'castle', 'palace', 'bridge', 'tower', 'landmark', 'saray', 'kale', 'kule', 'köprü', 'anıt', 
        'tarihi', 'sarnıç', 'cistern', 'hamam', 'bath', 'aqueduct', 'su kemeri', 'fountain', 'çeşme', 'sebil', 'terazi', 
        'obelisk', 'dikilitaş', 'köşk', 'pavilion', 'mansion', 'kasrı', 'sur', 'kapısı', 'gate', 'fortress', 'walls', 
        'medrese', 'madrasa', 'taşı', 'tekfur', 'bedesten','feneri', 'lighthouse', 'anıtı', 'memorial', 'statue', 'heykel', r'\bhanı\b' # \bhanı\b ile hancı kelimesini engelledik
    ],
    
    'Shopping & Traditional Bazaar': ['bazaar', 'market', 'han', 'çarşı', 'pazar', 'shopping', 'mall', 'bedesten', 'arasta', 'pasaj'],
    
    'Nature & Parks': [
        'park', 'garden', 'scenic', 'nature', 'forest', 'island', 'bahçe', 'koru', 'ada', 'doğa', 'manzara', 
        'hill', 'tepe', 'sahil', 'coast', 'plaj', 'yürüyüş yolu', 'köy yolu','sunset', 'günbatımı', 'spot', 'seyir' # 'yol' kelimesi adreslerle karışmaması için spesifikleştirildi
    ],
    
    'Squares & Plazas': ['plaza', 'square', 'meydan', 'alanı', 'iskelesi', 'iskeleye', 'eminönü', 'beşiktaş']
    
    
}

def evliyapp_mapper(row):
   
    title = str(row.get('title', '')).lower()
    sub_title = str(row.get('subTitle', '')).lower()
    cat_main = str(row.get('categoryName', '')).lower()
    
    # Categories listesini güvenli şekilde birleştir
    cats_list = row.get('categories', [])
    cats_joined = " ".join(cats_list).lower() if isinstance(cats_list, list) else ""
    
    # --- ADIM 1: Kara Liste Kontrolü (Sadece Başlık ve subTitle üzerinden) ---
    if any(word.lower() in title or word.lower() in sub_title for word in kara_liste):
        return "DELETE", "Blacklist"

    # --- ADIM 2: Süper Birleşik Metin (Combined) ---
    # Başlık + Türkçe İsim + Ana Kategori + Kategori Listesi
    combined = f"{title} {sub_title} {cat_main} {cats_joined}"

    # --- ADIM 3: Kategori Mapping ---
    for main_cat, keywords in mapping_rules.items():
        for word in keywords:
            # Eğer kelime zaten regex (\b) içeriyorsa doğrudan kullan, yoksa oluştur
            if r'\b' in word:
                pattern = word
            elif len(word) <= 4:
                pattern = r'\b' + re.escape(word) + r'\b'
            else:
                pattern = re.escape(word)
                
            if re.search(pattern, combined):
                return main_cat, word.title()
                
    return 'Sightseeing', 'General'


results = df_master.apply(lambda r: pd.Series(evliyapp_mapper(r)), axis=1)
df_master['main_category'] = results[0]
df_master['sub_category'] = results[1]


df_master = df_master[df_master['main_category'] != "DELETE"].copy()

# ==============================================================================
# STAGE 14: UPDATING THE DESCRIPTION COLUMN
# ==============================================================================

def generate_display_description(row):
    # 1. Mevcut ve yeterli bir açıklama varsa onu koru
    original = str(row.get('description', '')).strip()
    if pd.notna(row.get('description')) and len(original) > 25:
        return original

    # 2. Temel Değişkenler
    title = row.get('title', 'This location')
    city = row.get('city', 'Istanbul')
    main_cat = str(row.get('main_category', 'point of interest')).lower()
    score = row.get('totalScore')
    
    # 3. Kategoriye Özel Estetik Cümleler
    if main_cat == 'food & drink':
        sentence = f"Discover the local flavors at {title}, a popular spot in {city} known for its inviting atmosphere."
    
    elif main_cat == 'religious & spiritual':
        sentence = f"{title} stands as a serene spiritual site in {city}, offering visitors a peaceful retreat and historical depth."
    
    elif main_cat == 'museum & art':
        sentence = f"Immerse yourself in cultural heritage at {title}, where {city}'s artistic and historical legacy comes to life."
    
    elif main_cat == 'history & heritage':
        sentence = f"{title} is a landmark of historical significance in {city}, reflecting the rich architectural tapestry of the area."
    
    elif main_cat == 'shopping & traditional bazaar':
        sentence = f"Experience the authentic vibe of {city} at {title}, a vibrant destination perfect for traditional shopping and local crafts."
    
    elif main_cat == 'nature & parks':
        sentence = f"Enjoy a breath of fresh air at {title}, a beautiful green space in {city} ideal for relaxation and outdoor moments."
    
    elif main_cat == 'squares & plazzas': # Yazım hatası olasılığına karşı kontrol
        sentence = f"{title} is a central landmark in {city}, serving as a lively meeting point and a great spot to observe city life."
    
    else:
        sentence = f"{title} is a must-visit {main_cat} in {city}, contributing to the unique and diverse charm of the district."

    # 4. SOSYAL KANIT EKLEME (Social Proof)
    # Eğer mekanın puanı yüksekse açıklamaya bir 'güven' cümlesi ekler
    if pd.notna(score) and score >= 4.2:
        sentence += f" It is highly recommended by visitors with a remarkable {score} rating."

    return sentence


df_master['description'] = df_master.apply(generate_display_description, axis=1)

# ==============================================================================
# STAGE 15: CREATING A TAGS COLUMN
# ==============================================================================

# Eşleşme garantisi için anahtarları (keys) küçük harf yapılır
raw_mapping = {
    # Accessibility
    "Accessibility_Wheelchair accessible entrance": "Wheelchair Accessible",
    "Accessibility_Wheelchair-accessible entrance": "Wheelchair Accessible",
    "Accessibility_Wheelchair accessible parking lot": "Wheelchair Parking",
    "Accessibility_Wheelchair-accessible toilet": "Wheelchair Accessible",
    "Accessibility_Wheelchair accessible restroom": "Wheelchair Accessible",
    
    # Atmosphere
    "Atmosphere_Cosy": "Cozy",
    "Atmosphere_Cozy": "Cozy",
    "Atmosphere_Trending": "Trendy",
    "Atmosphere_Trendy": "Trendy",
    "Atmosphere_Historic": "Historic",
    "Atmosphere_History": "Historic",
    
    # Amenities & Service
    "Amenities_Toilet": "Restroom",
    "Amenities_Public restroom": "Restroom",
    "Amenities_Wi-Fi": "Free Wi-Fi",
    "Service options_Takeaway": "Takeout",
    "Service options_Takeout": "Takeout",
    
    # Children
    "Children_Good for kids": "Child Friendly",
    "Children_Kid-friendly activities": "Child Friendly",
    "Children_Good for kids birthday": "Child Friendly",
    
    # Payments & Other
    "Payments_Admission fee": "Entry Fee Required", # İki girişin vardı, 'Required' olanı seçtim
    "Offerings_Halal food": "Halal Options",
    "Highlights_Rooftop seating": "Rooftop",
    "Parking_lot": "Parking Available",
    "Parking_Paid_parking_lot": "Paid Parking",
    "Payments_Credit_cards": "Credit Cards",
    "Pets_Dogs allowed inside": "Dog Friendly",
    "Pets_Dogs allowed outside": "Dog Friendly"
}

# Kodun içindeki karşılaştırma için mapping'i normalize etme
tag_mapping = {k.lower().replace(" ", "_").replace(":", ""): v for k, v in raw_mapping.items()}

# UI'da gizlenilecek ama veride tuttuğumuz liste
noise_tags = ["Credit Cards", "Debit Cards", "Nfc Mobile Payments", 
    "Dine-In", "Table Service", "Seating", "Groups", "Tourists",
    "Lunch", "Dinner", "Brunch",'Food', 'Service', 'Dining']


def generate_all_tags(row, feature_columns, rules):
    tags = set()
    
    # --- BÖLÜM A: KATEGORİ MAPPING (Kelimelerden kategori yakalama) ---
    title = str(row.get('title', '')).lower()
    sub_title = str(row.get('subTitle', '')).lower()
    cat_name = str(row.get('categoryName', '')).lower()
    cats_list = row.get('categories', [])
    cats_joined = " ".join(cats_list).lower() if isinstance(cats_list, list) else ""
    combined_text = f"{title} {sub_title} {cat_name} {cats_joined}"
    
    for main_cat, keywords in rules.items():
        for word in keywords:
            pattern = r'\b' + re.escape(word) + r'\b' if len(word) <= 4 else re.escape(word)
            if re.search(pattern, combined_text):
                tags.add(main_cat)
                break

    # --- BÖLÜM B: OPTİMİZE EDİLMİŞ DİNAMİK ÖZELLİKLER (144 Sütun İşleme) ---
    for col in feature_columns:
        if row.get(col) == True:
            # 1. ADIM: Önce sözlükte (tag_mapping) özel bir karşılığı var mı?
            # Sütun isimleri küçük harf olduğu için tag_mapping de küçük harf bakıyor
            if col in tag_mapping:
                clean_tag = tag_mapping[col]
            else:
                # 2. ADIM: Sözlükte yoksa, dinamik temizlik yap.
                if "_" in col:
                    clean_tag = col.split("_", 1)[1] 
                else:
                    clean_tag = col
                
                # Alt tireleri boşluğa çevir ve Baş Harflerini Büyüt
                clean_tag = clean_tag.replace("_", " ").title().strip()
                
                # Özel düzeltmeler (lot -> Parking Lot gibi)
                if clean_tag == "Lot": clean_tag = "Parking Lot"
                if clean_tag == "Cards": clean_tag = "Credit Cards"
            
            # 3. ADIM: Son kontrol (Anlamsız çok kısa tagleri engelle)
            if len(clean_tag) > 2:
                tags.add(clean_tag)

    return list(tags)

# --- UYGULAMA ---

# Dinamik sütun listesini hazırla - BURAYA .lower() EKLENDİ (Kritik Düzeltme)
dynamic_cols = [c.replace(" ", "_").replace(":", "").lower() for c in all_possible_columns]
existing_dynamic_cols = [col for col in dynamic_cols if col in df_master.columns]


print(f"Eşleşen dinamik sütun sayısı: {len(existing_dynamic_cols)}")

# Tags sütununu oluştur
df_master['tags'] = df_master.apply(lambda r: generate_all_tags(r, existing_dynamic_cols, mapping_rules), axis=1)

# Sadece tags listesi boş olan ve kategorisi 'General' kalan 'noise' mekanları temizle
df_master = df_master[~((df_master['tags'].map(len) == 0) & (df_master['main_category'] == 'Sightseeing (General)'))]

print("'tags' sütunu başarıyla zenginleştirildi ve temizlik yapıldı.")


# ==============================================================================
# STAGE 16: ENRICHED DESCRIPTION FOR PGVECTOR
# ==============================================================================

def synthesize_enriched_description(row):
    # 1. Temel Kimlik
    title = str(row.get('display_title', row.get('title', '')))
    main_cat = str(row.get('main_category', ''))
    sub_cat = str(row.get('sub_category', ''))
    
    # 2. Etiketler 
    tags_list = row.get('tags', [])
    tags_text = ", ".join(tags_list) if isinstance(tags_list, list) else ""
    
    # 3. Saat Bilgisi 
    hours = row.get('openingHours_display', '')
    if isinstance(hours, list):
        hours = "Open 24 hours" if row.get('is_24_7') else "Check hours online"
        
    score = str(row.get('totalScore', ''))
    original_desc = str(row.get('description', ''))
    
    # 4. SENTEZ
    enriched = f"{title}. Category: {main_cat} ({sub_cat}). "
    
    if tags_text:
        enriched += f"Features: {tags_text}. "
    
    if hours:
        enriched += f"Status: {hours}. "
    
    enriched += f"Info: {original_desc} "
    
    if score and score != '0':
        enriched += f"Rating: {score}/5."
        
    return enriched


df_master['enriched_description'] = df_master.apply(synthesize_enriched_description, axis=1)

print("enriched_description sütunu dinamik özelliklerle birlikte oluşturuldu.")

# ==============================================================================
# STAGE 17: Refined Version
# ==============================================================================


final_cols = [col for col in df_master.columns if col not in existing_dynamic_cols]
df_final = df_master[final_cols]

print(f"Eski sütun sayısı: {len(df_master.columns)}")
print(f"Yeni sütun sayısı: {len(df_final.columns)}")
print(f"Kalan Sütunlar: {df_final.columns.tolist()}")

#  AYRIŞTIRMA (Refined Version)
df_food_app = df_master[df_master['main_category'] == 'Food & Drink'].copy().reset_index(drop=True)

# Geri kalan her şeyi Sightseeing'e atıyoruz ama Food & Drink olmayanları filtreleyerek
df_sightseeing_app = df_master[df_master['main_category'] != 'Food & Drink'].copy().reset_index(drop=True)

# EKSTRA KONTROL: Kategori Atanamamış Kayıtlar
# Eğer main_category null kaldıysa bu kayıtlar 'Genel' bile olamamış demektir
null_cats = df_master[df_master['main_category'].isna()]

# RAPORLAMA VE HATA AYIKLAMA (DEBUGGING)
print(f"--- FİNAL OPERASYON RAPORU ---")
print(f"Toplam İşlenen Kayıt: {len(df_master)}")
print(f"Yemek Yerleri: {len(df_food_app)}")
print(f"Gezi Rehberi: {len(df_sightseeing_app)}")
print(f"Kategori Atanamayan (Null): {len(null_cats)}")

print(f"\n--- Gezi Rehberi Alt Kategori Detayları ---")
print(df_sightseeing_app['main_category'].value_counts())

# 8. SIGHTSEEING (GENEL) ANALİZİ
genel_sayisi = len(df_sightseeing_app[df_sightseeing_app['main_category'] == 'Sightseeing (Genel)'])
if genel_sayisi > 0:
    print(f"\n'Sightseeing (Genel)' grubunda {genel_sayisi} mekan var.")
    print("En sık rastlanan kategoriName örnekleri:")
    print(df_sightseeing_app[df_sightseeing_app['main_category'] == 'Sightseeing (Genel)']['categoryName'].value_counts().head(10))

# DOSYALARI KAYDET 
df_food_app.to_json('data_food_ready.json', orient='records', force_ascii=False)
df_sightseeing_app.to_json('data_sightseeing_ready.json', orient='records', force_ascii=False)
print("\nDosyalar 'ready' formatında kaydedildi.")