ssenaay commited on
Commit
ede6adc
·
verified ·
1 Parent(s): c08d453

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +383 -0
app.py CHANGED
@@ -0,0 +1,383 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import numpy as np
3
+ import gradio as gr
4
+ import os
5
+ from sentence_transformers import SentenceTransformer, util
6
+ import torch
7
+ import re
8
+
9
+ print("--- Film Öneri Sistemi Başlatılıyor (Hugging Face Deploy için) ---")
10
+
11
+ # --- ADIM 1: Veri Seti Yükleniyor ve Keşfediliyor ---
12
+ print("ADIM 1: Veri Seti Yükleniyor ve Keşfediliyor...")
13
+
14
+ csv_file_name = "imdb-top-rated-movies-user-rated.csv"
15
+ file_path = os.path.join(".", csv_file_name)
16
+
17
+ if not os.path.exists(file_path):
18
+ print(f"HATA: '{csv_file_name}' dosyası '{file_path}' yolunda bulunamadı. Lütfen CSV dosyasını Space'e yüklediğinizden emin olun.")
19
+ exit(1)
20
+
21
+ try:
22
+ df = pd.read_csv(file_path)
23
+ print(f"'{csv_file_name}' başarıyla yüklendi. Toplam {len(df)} film bulundu.")
24
+ except Exception as e:
25
+ print(f"HATA: CSV dosyası yüklenirken hata oluştu: {e}")
26
+ exit(1)
27
+ print("ADIM 1: Veri Seti Keşfi Tamamlandı.")
28
+
29
+
30
+ # --- ADIM 2: Veri Temizliği ve Ön İşleme ---
31
+ print("\nADIM 2: Veri Temizliği ve Ön İşleme Başlıyor...")
32
+
33
+ df_filtered = df[['Title', 'IMDb Rating', 'Tags', 'Director', 'Stars', 'Votes', 'Description', 'Poster URL']].copy()
34
+
35
+ df_filtered['Stars'].fillna('', inplace=True)
36
+ df_filtered['Description'].fillna('', inplace=True)
37
+ df_filtered['Poster URL'].fillna('', inplace=True)
38
+
39
+ genre_mapping = {
40
+ 'action': ['action', 'action epic', 'gun fu', 'one-person army action', 'car action', 'kung fu', 'martial arts', 'martial-arts'],
41
+ 'adventure': ['adventure', 'adventure epic', 'desert adventure', 'animal adventure', 'space adventure', 'swashbuckler'],
42
+ 'comedy': ['comedy', 'romantic comedy', 'buddy comedy', 'sitcom', 'black comedy', 'satire', 'spoof', 'parody', 'slapstick', 'screwball comedy', 'dark comedy', 'body swap comedy'],
43
+ 'drama': ['drama', 'period drama', 'cop drama', 'legal drama', 'medical drama', 'teen drama', 'psychological drama', 'melodrama', 'historical drama', 'biography', 'romantic drama', 'showbiz drama', 'tragedy'],
44
+ 'thriller': ['thriller', 'crime thriller', 'spy thriller', 'psychological thriller', 'mystery thriller', 'political thriller', 'conspiracy thriller', 'erotic thriller', 'cyber thriller', 'suspense'],
45
+ 'sci-fi': ['sci-fi', 'space sci-fi', 'dystopian sci-fi', 'cyberpunk', 'alien invasion', 'mutant', 'robot', 'post-apocalyptic', 'time travel'],
46
+ 'fantasy': ['fantasy', 'dark fantasy', 'sword & sorcery', 'fairy tale', 'epic fantasy'],
47
+ 'horror': ['horror', 'slasher', 'supernatural horror', 'body horror', 'zombie', 'monster', 'vampire', 'werewolf', 'ghost'],
48
+ 'mystery': ['mystery', 'suspense mystery', 'cozy mystery', 'whodunnit', 'detective', 'police procedural'],
49
+ 'crime': ['crime', 'gangster', 'heist', 'mob', 'true crime'],
50
+ 'romance': ['romance', 'romantic comedy', 'romantic drama'],
51
+ 'animation': ['animation', 'adult animation', 'anime', 'computer animation', 'drawn animation', 'stop-motion animation'],
52
+ 'family': ['family', 'kids'],
53
+ 'western': ['western', 'classic western', 'neo-western'],
54
+ 'war': ['war', 'war drama'],
55
+ 'history': ['history', 'historical drama', 'biography'],
56
+ 'music': ['music', 'musical', 'classic musical', 'concert'],
57
+ 'documentary': ['documentary', 'docudrama', 'mockumentary']
58
+ }
59
+
60
+ reverse_genre_map = {}
61
+ for main_genre, sub_genres in genre_mapping.items():
62
+ for sub_genre in sub_genres:
63
+ reverse_genre_map[sub_genre] = main_genre
64
+
65
+ def map_to_main_genres(tag_list):
66
+ main_genres = set()
67
+ for tag in tag_list:
68
+ if tag in reverse_genre_map:
69
+ main_genres.add(reverse_genre_map[tag])
70
+ return list(main_genres)
71
+
72
+ def clean_and_split(text_series):
73
+ if pd.isna(text_series):
74
+ return []
75
+
76
+ item = str(text_series)
77
+ item = item.replace('"', '').replace("'", '').strip()
78
+ item = item.replace('sci, fi', 'sci-fi')
79
+
80
+ split_items = [s.strip().lower() for s in item.split(',') if s.strip()]
81
+ return split_items
82
+
83
+
84
+ df_filtered['Tags_cleaned_raw'] = df_filtered['Tags'].apply(clean_and_split)
85
+ df_filtered['Director_cleaned'] = df_filtered['Director'].apply(clean_and_split)
86
+ df_filtered['Stars_cleaned'] = df_filtered['Stars'].apply(clean_and_split)
87
+
88
+ df_filtered['Tags_cleaned'] = df_filtered['Tags_cleaned_raw'].apply(map_to_main_genres)
89
+
90
+
91
+ def convert_votes_to_numeric(votes_str):
92
+ if isinstance(votes_str, str):
93
+ votes_str = votes_str.replace(",", "")
94
+ if 'K' in votes_str:
95
+ return float(votes_str.replace('K', '')) * 1000
96
+ elif 'M' in votes_str:
97
+ return float(votes_str.replace('M', '')) * 1_000_000
98
+ try:
99
+ return float(votes_str)
100
+ except ValueError:
101
+ return np.nan
102
+
103
+ df_filtered['Votes_numeric'] = df_filtered['Votes'].apply(convert_votes_to_numeric)
104
+ df_filtered.drop('Votes', axis=1, inplace=True)
105
+ df_filtered.dropna(subset=['Votes_numeric'], inplace=True)
106
+
107
+ df_filtered['Combined_Text'] = df_filtered['Title'] + ". " + \
108
+ df_filtered['Description'] + ". " + \
109
+ df_filtered['Tags_cleaned'].apply(lambda x: ", ".join(x)) + ". " + \
110
+ df_filtered['Director_cleaned'].apply(lambda x: ", ".join(x)) + ". " + \
111
+ df_filtered['Stars_cleaned'].apply(lambda x: ", ".join(x))
112
+
113
+ print("\nADIM 2: Veri Temizliği ve Ön İşleme Tamamlandı.")
114
+
115
+
116
+ # --- ADIM 3: NLP Modelini Yükleme ve ÖNCEDEN OLUŞTURULMUŞ Embedding'leri Yükleme ---
117
+ print("\nADIM 3: NLP Modelini Yükleniyor ve Önceden Oluşturulmuş Embedding'ler Yükleniyor...")
118
+
119
+ model_name = 'sentence-transformers/all-MiniLM-L6-v2'
120
+ try:
121
+ sentence_model = SentenceTransformer(model_name)
122
+ print(f"'{model_name}' modeli başarıyla yüklendi.")
123
+ except Exception as e:
124
+ print(f"HATA: Sentence Transformer modeli yüklenirken hata oluştu: {e}")
125
+ exit(1)
126
+
127
+ embeddings_file_path = os.path.join(".", "film_embeddings.npy")
128
+ if not os.path.exists(embeddings_file_path):
129
+ print(f"HATA: '{embeddings_file_path}' dosyası bulunamadı. Lütfen Space'e yüklediğinizden emin olun.")
130
+ exit(1)
131
+
132
+ try:
133
+ film_embeddings = torch.from_numpy(np.load(embeddings_file_path))
134
+ print("Film embedding'leri başarıyla 'film_embeddings.npy' dosyasından yüklendi.")
135
+ except Exception as e:
136
+ print(f"HATA: film_embeddings.npy yüklenirken hata oluştu: {e}")
137
+ exit(1)
138
+
139
+ print("ADIM 3: NLP Modelini Yükleme ve Film Embedding'lerini Oluşturma Tamamlandı.")
140
+
141
+
142
+ # --- ADIM 4: Film Öneri Sistemi Mantığını Oluşturma ---
143
+ print("\nADIM 4: Film Öneri Sistemi Mantığı Oluşturuluyor...")
144
+
145
+ all_tags = sorted(list(set([tag for sublist in df_filtered['Tags_cleaned'] for tag in sublist if tag in genre_mapping])))
146
+ all_directors = sorted(list(set([director for sublist in df_filtered['Director_cleaned'] for director in sublist])))
147
+ all_stars = sorted(list(set([star for sublist in df_filtered['Stars_cleaned'] for star in sublist])))
148
+
149
+ def get_movie_recommendations(selected_tags, selected_directors, selected_stars, min_imdb_rating_slider, num_recommendations_slider, search_text=""):
150
+
151
+ print(f"\n--- Öneri İsteği ---")
152
+ print(f"Seçilen Türler: {selected_tags}")
153
+ print(f"Seçilen Yönetmenler: {selected_directors}")
154
+ print(f"Seçilen Oyuncular: {selected_stars}")
155
+ print(f"Minimum IMDb Puanı: {min_imdb_rating_slider}")
156
+ print(f"Öneri Sayısı: {num_recommendations_slider}")
157
+ print(f"Arama Metni: '{search_text}'")
158
+ print(f"Başlangıç DataFrame boyutu: {len(df_filtered)} (Poster URL'si dahil)")
159
+
160
+ selected_tags_list = list(selected_tags) if selected_tags else []
161
+ selected_directors_list = list(selected_directors) if selected_directors else []
162
+ selected_stars_list = list(selected_stars) if selected_stars else []
163
+
164
+ recommendations_df = df_filtered.copy()
165
+
166
+ recommendations_df = recommendations_df[recommendations_df['IMDb Rating'] >= min_imdb_rating_slider]
167
+ print(f"IMDb Puanı filtrelemesi sonrası: {len(recommendations_df)} film")
168
+
169
+ if selected_tags_list:
170
+ recommendations_df = recommendations_df[
171
+ recommendations_df['Tags_cleaned'].apply(lambda x: any(tag in x for tag in selected_tags_list))
172
+ ]
173
+ print(f"Tür filtrelemesi sonrası: {len(recommendations_df)} film")
174
+
175
+ if selected_directors_list:
176
+ recommendations_df = recommendations_df[
177
+ recommendations_df['Director_cleaned'].apply(lambda x: any(director in x for director in selected_directors_list))
178
+ ]
179
+ print(f"Yönetmen filtrelemesi sonrası: {len(recommendations_df)} film")
180
+
181
+ if selected_stars_list:
182
+ recommendations_df = recommendations_df[
183
+ recommendations_df['Stars_cleaned'].apply(lambda x: any(star in x for star in selected_stars_list))
184
+ ]
185
+ print(f"Oyuncu filtrelemesi sonrası: {len(recommendations_df)} film")
186
+
187
+ if search_text and len(recommendations_df) > 0:
188
+ print(f"'{search_text}' için NLP benzerlik araması yapılıyor...")
189
+
190
+ try:
191
+ query_embedding = sentence_model.encode(search_text, convert_to_tensor=True)
192
+ except Exception as e:
193
+ print(f"HATA: Arama metni embedding'i oluşturulurken hata oluştu: {e}")
194
+ return "Arama metni işlenirken bir hata oluştu. Lütfen tekrar deneyin."
195
+
196
+ filtered_indices = recommendations_df.index.tolist()
197
+ if not filtered_indices or len(film_embeddings) == 0:
198
+ print("HATA: Filtrelenmiş film indeksi bulunamadı veya embedding'ler boş.")
199
+ return "Filtreleme sonrası film bulunamadı."
200
+
201
+
202
+ try:
203
+ current_film_embeddings = film_embeddings[filtered_indices]
204
+ cosine_scores = util.cos_sim(query_embedding, current_film_embeddings)[0]
205
+ recommendations_df['Similarity_Score'] = cosine_scores.cpu().numpy()
206
+ recommendations_df = recommendations_df.sort_values(
207
+ by=['Similarity_Score', 'IMDb Rating', 'Votes_numeric'],
208
+ ascending=[False, False, False]
209
+ ).reset_index(drop=True)
210
+ print(f"NLP benzerlik filtrelemesi sonrası: {len(recommendations_df)} film")
211
+ except Exception as e:
212
+ print(f"HATA: NLP benzerlik hesaplanırken hata oluştu: {e}")
213
+ return "Benzerlik hesaplanırken bir hata oluştu. Lütfen tekrar deneyin."
214
+
215
+ if not search_text:
216
+ recommendations_df = recommendations_df.sort_values(
217
+ by=['IMDb Rating', 'Votes_numeric'],
218
+ ascending=[False, False]
219
+ ).reset_index(drop=True)
220
+
221
+ top_recommendations = recommendations_df.head(num_recommendations_slider)
222
+
223
+ if top_recommendations.empty:
224
+ print("Kriterlere uygun film bulunamadı.")
225
+ return "Üzgünüz, seçtiğiniz kriterlere uygun film bulunamadı."
226
+ else:
227
+ print(f"Toplam {len(top_recommendations)} öneri bulundu.")
228
+ html_output = ""
229
+ for idx, row in top_recommendations.iterrows():
230
+ directors_str = ", ".join([d.title() for d in row['Director_cleaned']])
231
+ stars_str = ", ".join([s.title() for s in row['Stars_cleaned']])
232
+ tags_str = ", ".join([t.title() for t in row['Tags_cleaned']])
233
+
234
+ similarity_info = ""
235
+ if 'Similarity_Score' in row and search_text:
236
+ similarity_info = f", Benzerlik: {row['Similarity_Score']:.2f}"
237
+
238
+ poster_url = row['Poster URL']
239
+ is_valid_image_url = False
240
+ if poster_url and pd.notna(poster_url):
241
+ if re.match(r".*\.(jpg|jpeg|png|gif|bmp|webp)$", poster_url.lower()):
242
+ is_valid_image_url = True
243
+
244
+ poster_html = ""
245
+ if is_valid_image_url:
246
+ poster_html = f'<img src="{poster_url}" alt="{row["Title"]} Poster" style="width: 120px; height: 180px; object-fit: cover; border-radius: 4px;">'
247
+ else:
248
+ poster_html = f"""
249
+ <div style="width: 120px; height: 180px; background-color: #2a2a2a; border-radius: 4px; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; color: #888; font-size: 0.8em; line-height: 1.2; padding: 5px;">
250
+ <span style="font-weight: bold; margin-bottom: 5px;">{row['Title']}</span>
251
+ <span>Poster Yok</span>
252
+ </div>
253
+ """
254
+
255
+ html_output += f"""
256
+ <div style="display: flex; margin-bottom: 20px; border: 1px solid #333; padding: 10px; border-radius: 8px; background-color: #1a1a1a;">
257
+ <div style="flex-shrink: 0; margin-right: 15px;">
258
+ {poster_html}
259
+ </div>
260
+ <div style="flex-grow: 1;">
261
+ <h3 style="margin-top: 0px; margin-bottom: 5px; color: #f39c12;">{row['Title']}</h3>
262
+ <p style="margin-bottom: 5px; color: #eee;">IMDb: <b>{row['IMDb Rating']:.1f}</b> ⭐, Oylar: <b>{int(row['Votes_numeric']):,}</b> 🗳️{similarity_info}</p>
263
+ <p style="margin-bottom: 5px; color: #bbb;">Yönetmen: {directors_str if directors_str else 'Bilinmiyor'}</p>
264
+ <p style="margin-bottom: 5px; color: #bbb;">Oyuncular: {stars_str if stars_str else 'Bilinmiyor'}</p>
265
+ <p style="margin-bottom: 0px; color: #bbb;">Türler: {tags_str if tags_str else 'Bilinmiyor'}</p>
266
+ </div>
267
+ </div>
268
+ """
269
+ return html_output
270
+
271
+ print("\nADIM 4: Film Öneri Sistemi Mantığı Oluşturuldu.")
272
+
273
+
274
+ # --- ADIM 5: Gradio Web Arayüzü Oluşturma ---
275
+ print("\nADIM 5: Gradio Web Arayüzü Oluşturuluyor...")
276
+
277
+ with gr.Blocks(theme=gr.themes.Soft(), css="""
278
+ .gradio-container { max-width: 1200px !important; font-family: 'Segoe UI', sans-serif; }
279
+ h1 { color: #f39c12; text-align: center; }
280
+ h3 { color: #eee; }
281
+ .gr-button.gr-button-primary { background-color: #f39c12 !important; border-color: #f39c12 !important; }
282
+ .gr-button.gr-button-primary:hover { background-color: #e67e22 !important; border-color: #e67e22 !important; }
283
+ .gr-checkbox-group label { color: #ccc; }
284
+ .gr-dropdown, .gr-slider, .gr-textbox { background-color: #2c2c2c; color: #eee; border-color: #555; }
285
+ .gr-dropdown-item { color: #eee; }
286
+ """) as demo:
287
+ gr.Markdown(
288
+ """
289
+ # 🎬 Film Öneri Sistemi
290
+ Favori film özelliklerinizi seçin, yüksek IMDb puanına sahip filmleri keşfedin!
291
+ İstediğiniz bir film veya konu hakkında yazın, benzerlerini de bulalım.
292
+ """
293
+ )
294
+
295
+ with gr.Row():
296
+ with gr.Column(scale=1):
297
+ gr.Markdown("### Film Özelliklerini Seçin:")
298
+
299
+ tags_input = gr.CheckboxGroup(
300
+ label="Film Türleri",
301
+ choices=all_tags,
302
+ value=['action', 'drama'],
303
+ interactive=True
304
+ )
305
+
306
+ directors_input = gr.Dropdown(
307
+ label="Yönetmenler",
308
+ choices=all_directors,
309
+ multiselect=True,
310
+ allow_custom_value=False,
311
+ interactive=True
312
+ )
313
+
314
+ stars_input = gr.Dropdown(
315
+ label="Oyuncular",
316
+ choices=all_stars,
317
+ multiselect=True,
318
+ allow_custom_value=False,
319
+ interactive=True
320
+ )
321
+
322
+ min_imdb_rating_slider = gr.Slider(
323
+ minimum=df_filtered['IMDb Rating'].min(),
324
+ maximum=df_filtered['IMDb Rating'].max(),
325
+ step=0.1,
326
+ value=7.6,
327
+ label="Minimum IMDb Puanı"
328
+ )
329
+
330
+ num_recommendations_slider = gr.Slider(
331
+ minimum=1,
332
+ maximum=20,
333
+ step=1,
334
+ value=10,
335
+ label="Öneri Sayısı"
336
+ )
337
+
338
+ search_text_input = gr.Textbox(
339
+ label="Film Adı veya Konu Hakkında Ara (NLP Tabanlı Benzerlik)",
340
+ placeholder="Örneğin: Batman, uzay filmi, zamanda yolculuk..."
341
+ )
342
+
343
+ recommend_btn = gr.Button("🚀 Film Önerilerini Getir", variant="primary", size="lg")
344
+
345
+ with gr.Column(scale=2):
346
+ gr.Markdown("### Önerilen Filmler:")
347
+ output_html = gr.HTML(label="Önerileriniz burada listelenecektir.", value="<p style='text-align: center; color: #bbb;'>Henüz bir öneri yapılmadı. Özellikleri seçip butona tıklayın!</p>")
348
+
349
+ recommend_btn.click(
350
+ fn=get_movie_recommendations,
351
+ inputs=[tags_input, directors_input, stars_input, min_imdb_rating_slider, num_recommendations_slider, search_text_input],
352
+ outputs=output_html
353
+ )
354
+
355
+ gr.Examples(
356
+ examples=[
357
+ [['action'], [], [], 7.6, 5, ""],
358
+ [['comedy', 'drama'], [], [], 7.8, 3, ""],
359
+ [[], ['christopher nolan'], [], 8.0, 5, ""],
360
+ [[], [], ['leonardo dicaprio'], 7.8, 3, ""],
361
+ [[], [], [], 8.0, 5, "kahramanlık ve bilim kurgu"],
362
+ [['action', 'sci-fi'], [], [], 7.8, 5, "uzaylı istilası ve kaçış"],
363
+ ],
364
+ inputs=[tags_input, directors_input, stars_input, min_imdb_rating_slider, num_recommendations_slider, search_text_input],
365
+ outputs=output_html,
366
+ fn=get_movie_recommendations,
367
+ label="Örnek Önerileri Deneyin"
368
+ )
369
+
370
+ gr.Markdown(
371
+ """
372
+ ---
373
+ ### ℹ️ Nasıl Kullanılır?
374
+ 1. **Film Türleri, Yönetmenler ve Oyuncular** bölümlerinden istediğiniz filtreleri seçin (birden fazla seçim yapabilirsiniz).
375
+ 2. **Minimum IMDb Puanı** ve **Öneri Sayısı** çubuklarını ayarlayın.
376
+ 3. İsterseniz **"Film Adı veya Konu Hakkında Ara"** kutucuğuna bir film adı, konu veya anahtar kelime yazın.
377
+ 4. **"🚀 Film Önerilerini Getir"** butonuna tıklayın.
378
+ 5. Öneriler sağ panelde görünecektir!
379
+ """
380
+ )
381
+
382
+ demo.launch(share=True)
383
+ print("\nADIM 5: Gradio Web Arayüzü Başlatıldı.")