IvT-DS commited on
Commit
f1b6e2e
1 Parent(s): 4dee65a

Upload 2 files

Browse files
pages/02_📺_Find_my_show.py ADDED
@@ -0,0 +1,265 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import importlib.util
3
+ import torch
4
+ import streamlit as st
5
+ import pandas as pd
6
+ from PIL import Image
7
+
8
+ # Формируем абсолютный путь до файла functions.py
9
+ module_path = os.path.abspath(
10
+ os.path.join(os.path.dirname(__file__), "..", "resource", "functions.py")
11
+ )
12
+
13
+ # Загружаем модуль
14
+ spec = importlib.util.spec_from_file_location("resource.functions", module_path)
15
+ functions = importlib.util.module_from_spec(spec)
16
+ spec.loader.exec_module(functions)
17
+
18
+ # Теперь используем функции напрямую
19
+ table_maker = functions.table_maker
20
+ RecSys = functions.RecSys
21
+
22
+ poster_path = "https://resizer.mail.ru/p/"
23
+ show_path = "https://kino.mail.ru/series_"
24
+ placeholder_path = "../img/v2/nopicture/308x462.png"
25
+
26
+
27
+ @st.cache_resource(ttl=3600) # 👈 Cache data for 1 hour (=3600 seconds)
28
+ def load_model(model_path):
29
+ model = torch.load(model_path)
30
+ return model
31
+
32
+
33
+ @st.cache_data(ttl=3600) # 👈 Cache data for 1 hour (=3600 seconds)
34
+ def load_data(data_path):
35
+ df = pd.read_pickle(data_path)
36
+ return df
37
+
38
+
39
+ DATA_PATH = "data/data.pkl"
40
+ MODEL_PATH = "model/model.pt"
41
+
42
+ df = load_data(DATA_PATH)
43
+ model = load_model(MODEL_PATH)
44
+
45
+ image = Image.open("pages/tv_shows.png")
46
+ st.image(image, use_column_width=True)
47
+
48
+ # Заголовок приложения
49
+ st.markdown("### Поиск сериалов по запросу пользователя")
50
+
51
+ # Создание списка уникальных стран
52
+ all_countries = sorted(set(df["county"].tolist()))
53
+
54
+ # Создание списка уникальных жанров
55
+ all_genres = set()
56
+ for genres_set in df["tags"].dropna():
57
+ all_genres.update(genres_set)
58
+ all_genres = sorted(all_genres)
59
+
60
+ # Фильтр по наличию рейтинга
61
+ has_rating = st.sidebar.checkbox("Показывать только сериалы с рейтингом?", True)
62
+
63
+ # Виджеты для боковой панели
64
+ selected_country = st.sidebar.multiselect("Страна", all_countries)
65
+ selected_genre = st.sidebar.multiselect("Жанры", all_genres)
66
+
67
+
68
+ rating = True
69
+
70
+ search_table = table_maker(
71
+ df=df,
72
+ country=selected_country,
73
+ min_year=int(df["year"].min()),
74
+ max_year=int(df["year"].max()),
75
+ tagger=set(selected_genre),
76
+ rating=has_rating,
77
+ )
78
+
79
+ # Проверяем, пустой ли отфильтрованный DataFrame
80
+ if search_table.empty:
81
+ st.error(
82
+ "После фильтрации данных не осталось. Пожалуйста, выберите другие параметры."
83
+ )
84
+ else:
85
+ # Преобразование year в числовой формат, если возможно, и обработка NaN значений
86
+ search_table["year"] = pd.to_numeric(search_table["year"], errors="coerce").dropna()
87
+
88
+ if search_table.empty:
89
+ st.error(
90
+ "После фильтрации и обработки годов в данных не осталось записей. Пожалуйста, выберите другие параметры."
91
+ )
92
+ else:
93
+ # Теперь безопасно ищем min и max
94
+ min_year = int(search_table["year"].min())
95
+ max_year = int(search_table["year"].max())
96
+
97
+ # Если есть хотя бы два разных года, отображаем слайдер
98
+ if min_year < max_year:
99
+ selected_year_range = st.sidebar.slider(
100
+ "Выберите диапазон лет выпуска",
101
+ min_value=min_year,
102
+ max_value=max_year,
103
+ value=(min_year, max_year),
104
+ )
105
+ # Применяем фильтр по годам
106
+ search_table = search_table[
107
+ (search_table["year"] >= selected_year_range[0])
108
+ & (search_table["year"] <= selected_year_range[1])
109
+ ]
110
+
111
+ st.sidebar.markdown("---")
112
+ st.sidebar.markdown("### Дополнительные настройки")
113
+
114
+ # Позволяет пользователю выбрать количество сериалов для отображения, от 1 до 10
115
+ top_n = st.sidebar.number_input(
116
+ "Сколько сериалов показывать?", min_value=1, max_value=10, value=5
117
+ )
118
+
119
+ # Создание текстового поля для ввода пользовательского запроса
120
+ user_request = st.text_input("Введите ваш запрос:", "звездные войны")
121
+
122
+ if st.button("Найти сериалы по запросу") and len(df) > 0:
123
+
124
+ output = RecSys(search_table, user_request, model)
125
+
126
+ # top_n = 5 # мин 1 макс 10
127
+ res = output().head(top_n)
128
+
129
+ (
130
+ poster,
131
+ title,
132
+ description,
133
+ rating,
134
+ genre,
135
+ cast,
136
+ score,
137
+ year,
138
+ links,
139
+ country,
140
+ ) = (
141
+ {},
142
+ {},
143
+ {},
144
+ {},
145
+ {},
146
+ {},
147
+ {},
148
+ {},
149
+ {},
150
+ {},
151
+ )
152
+
153
+ for i, con in enumerate(res["poster"]):
154
+ # Проверяем, является ли значение в con ссылкой или путем к файлу
155
+ if "nopicture" in con:
156
+ poster[i] = placeholder_path
157
+ else:
158
+ poster[i] = poster_path + con
159
+
160
+ for i, con in enumerate(res["year"]):
161
+ year[i] = con
162
+
163
+ for i, con in enumerate(res["title"]):
164
+ title[i] = con
165
+
166
+ for i, con in enumerate(res["description"]):
167
+ description[i] = con
168
+
169
+ for i, con in enumerate(res["rating"]):
170
+ rating[i] = con
171
+
172
+ for i, con in enumerate(res["tags"]):
173
+ genre[i] = ", ".join(con)
174
+
175
+ for i, con in enumerate(res["cast"]):
176
+ cast[i] = con
177
+
178
+ for i, con in enumerate(res["score"]):
179
+ score[i] = con
180
+
181
+ for i, con in enumerate(res["url"]):
182
+ links[i] = show_path + con
183
+
184
+ for i, con in enumerate(res["county"]):
185
+ country[i] = con
186
+
187
+ st.markdown("---")
188
+
189
+ # Проверяем, пустой ли набор результатов
190
+ if len(res) == 0:
191
+ st.error(
192
+ "Сериалы по выбранным параметрам не найдены. Попробуйте изменить критерии поиска."
193
+ )
194
+ else:
195
+ # Если результаты есть, выводим их
196
+ iterations = min(len(res), top_n)
197
+
198
+ for i in range(iterations):
199
+
200
+ col1, col2 = st.columns([1, 3])
201
+ with col1:
202
+ st.image(poster[i])
203
+ # Добавляем ссылку под картинкой
204
+ st.markdown(
205
+ f"<a href='{links[i]}' target='_blank' style='display: block; text-align: center; color: grey; font-size: small; font-style: italic;'>Смотреть сериал</a>",
206
+ unsafe_allow_html=True,
207
+ )
208
+
209
+ with col2:
210
+
211
+ st.markdown(
212
+ f"<span style='font-weight:bold; font-size:22px;'>Название сериала:</span> <span style='font-size:20px;'>«{title[i]}»</span>",
213
+ unsafe_allow_html=True,
214
+ )
215
+
216
+ st.markdown(
217
+ f"<span style='font-weight:bold; font-size:16px;'>Страна:</span> <span style='font-size:16px;'>{country[i]}</span>",
218
+ unsafe_allow_html=True,
219
+ )
220
+
221
+ st.markdown(
222
+ f"<span style='font-weight:bold; font-size:16px;'>Год выпуска:</span> <span style='font-size:16px;'>{year[i]}</span>",
223
+ unsafe_allow_html=True,
224
+ )
225
+
226
+ st.markdown(
227
+ f"<span style='font-weight:bold; font-size:16px;'>Жанр:</span> <span style='font-size:16px;'>{genre[i]}</span>",
228
+ unsafe_allow_html=True,
229
+ )
230
+
231
+ rating_display = (
232
+ "Нет информации" if pd.isna(rating[i]) else rating[i]
233
+ )
234
+
235
+ st.markdown(
236
+ f"<span style='font-weight:bold; font-size:16px;'>Рейтинг:</span> <span style='font-size:16px;'>{rating_display}</span>",
237
+ unsafe_allow_html=True,
238
+ )
239
+
240
+ st.markdown(
241
+ "<h6 style='font-weight:bold;'>В ролях:</h6>",
242
+ unsafe_allow_html=True,
243
+ )
244
+
245
+ st.markdown(
246
+ f"<div style='text-align: justify; margin-bottom: 18px;'>{cast[i]}</div>",
247
+ unsafe_allow_html=True,
248
+ )
249
+
250
+ st.markdown(
251
+ "<h6 style='font-weight:bold;'>Описание:</h6>",
252
+ unsafe_allow_html=True,
253
+ )
254
+
255
+ st.markdown(
256
+ f"<div style='text-align: justify;'>{description[i]}</div>",
257
+ unsafe_allow_html=True,
258
+ )
259
+ score_display = round(score[i], 3)
260
+ st.markdown(
261
+ f"<div style='color: grey;'><hr style='margin: 2px 0;'/><span style='font-weight:bold; font-size:13px; font-style: italic;'>Коэффициент сходимости (косинусное сходство):</span> <span style='font-size:13px; font-style: italic;'>{score_display}</span><hr style='margin: 2px 0;'/></div>",
262
+ unsafe_allow_html=True,
263
+ )
264
+
265
+ st.markdown("---")
pages/03_🚀_Find_my_show_(FAISS).py ADDED
@@ -0,0 +1,274 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import importlib.util
3
+ import torch
4
+ import streamlit as st
5
+ import pandas as pd
6
+ from PIL import Image
7
+
8
+
9
+ # Формируем абсолютный путь до файла functions.py
10
+ module_path = os.path.abspath(
11
+ os.path.join(os.path.dirname(__file__), "..", "resource", "functions.py")
12
+ )
13
+
14
+ # Загружаем модуль
15
+ spec = importlib.util.spec_from_file_location("resource.functions", module_path)
16
+ functions = importlib.util.module_from_spec(spec)
17
+ spec.loader.exec_module(functions)
18
+
19
+ # Теперь используем функции напрямую
20
+ table_maker = functions.table_maker
21
+ # RecSys = functions.RecSys
22
+ FAISS_inference = functions.FAISS_inference
23
+
24
+ poster_path = "https://resizer.mail.ru/p/"
25
+ show_path = "https://kino.mail.ru/series_"
26
+ placeholder_path = "../img/v2/nopicture/308x462.png"
27
+
28
+
29
+ @st.cache_resource(ttl=3600) # 👈 Cache data for 1 hour (=3600 seconds)
30
+ def load_model(model_path):
31
+ model = torch.load(model_path)
32
+ return model
33
+
34
+
35
+ @st.cache_data(ttl=3600) # 👈 Cache data for 1 hour (=3600 seconds)
36
+ def load_data(data_path):
37
+ df = pd.read_pickle(data_path)
38
+ return df
39
+
40
+
41
+ DATA_PATH = "data/data.pkl"
42
+ MODEL_PATH = "model/model.pt"
43
+
44
+ df = load_data(DATA_PATH)
45
+ model = load_model(MODEL_PATH)
46
+
47
+
48
+ image = Image.open("pages/tv_shows.png")
49
+ st.image(image, use_column_width=True)
50
+
51
+ # Заголовок приложения
52
+ st.markdown("### Поиск сериалов по запросу пользователя (с использованием FAISS)")
53
+
54
+
55
+ # Создание списка уникальных стран
56
+ all_countries = sorted(set(df["county"].tolist()))
57
+
58
+ # Создание списка уникальных жанров
59
+ all_genres = set()
60
+ for genres_set in df["tags"].dropna():
61
+ all_genres.update(genres_set)
62
+ all_genres = sorted(all_genres)
63
+
64
+ # Фильтр по наличию рейтинга
65
+ has_rating = st.sidebar.checkbox("Показывать только сериалы с рейтингом?", True)
66
+
67
+ # Виджеты для боковой панели
68
+ selected_country = st.sidebar.multiselect("Страна", all_countries)
69
+ selected_genre = st.sidebar.multiselect("Жанры", all_genres)
70
+
71
+
72
+ rating = True
73
+
74
+ search_table = table_maker(
75
+ df=df,
76
+ country=selected_country,
77
+ min_year=int(df["year"].min()),
78
+ max_year=int(df["year"].max()),
79
+ tagger=set(selected_genre),
80
+ rating=has_rating,
81
+ )
82
+
83
+ # Проверяем, пустой ли отфильтрованный DataFrame
84
+ if search_table.empty:
85
+ st.error(
86
+ "После фильтрации данных не осталось. Пожалуйста, выберите другие параметры."
87
+ )
88
+ else:
89
+ # Преобразование year в числовой формат, если возможно, и обработка NaN значений
90
+ search_table["year"] = pd.to_numeric(search_table["year"], errors="coerce").dropna()
91
+
92
+ if search_table.empty:
93
+ st.error(
94
+ "После фильтрации и обработки годов в данных не осталось записей. Пожалуйста, выберите другие параметры."
95
+ )
96
+ else:
97
+ # Теперь безопасно ищем min и max
98
+ min_year = int(search_table["year"].min())
99
+ max_year = int(search_table["year"].max())
100
+
101
+ # Если есть хотя бы два разных года, отображаем слайдер
102
+ if min_year < max_year:
103
+ selected_year_range = st.sidebar.slider(
104
+ "Выберите диапазон лет выпуска",
105
+ min_value=min_year,
106
+ max_value=max_year,
107
+ value=(min_year, max_year),
108
+ )
109
+ # Применяем фильтр по годам
110
+ search_table = search_table[
111
+ (search_table["year"] >= selected_year_range[0])
112
+ & (search_table["year"] <= selected_year_range[1])
113
+ ]
114
+
115
+ st.sidebar.markdown("---")
116
+ st.sidebar.markdown("### Дополнительные настройки")
117
+
118
+ # Позволяет пользователю выбрать количество сериалов для отображения, от 1 до 10
119
+ top_n = st.sidebar.number_input(
120
+ "Сколько сериалов показывать?", min_value=1, max_value=10, value=5
121
+ )
122
+
123
+ # Создание текстового поля для ввода пользовательского запроса
124
+ user_request = st.text_input(
125
+ "Введите ваш запрос:",
126
+ "про ментов, мусора по коням, менты, полиция и все такое",
127
+ )
128
+
129
+ user_request_emb = model.encode(user_request)
130
+
131
+ if st.button("Найти сериалы по запросу") and len(df) > 0:
132
+
133
+ output_faiss = FAISS_inference(search_table, user_request_emb, top_n)
134
+
135
+ # top_n = 5 # мин 1 макс 10
136
+ res = output_faiss()
137
+
138
+ (
139
+ poster,
140
+ title,
141
+ description,
142
+ rating,
143
+ genre,
144
+ cast,
145
+ score,
146
+ year,
147
+ links,
148
+ country,
149
+ ) = (
150
+ {},
151
+ {},
152
+ {},
153
+ {},
154
+ {},
155
+ {},
156
+ {},
157
+ {},
158
+ {},
159
+ {},
160
+ )
161
+
162
+ for i, con in enumerate(res["poster"]):
163
+ # Проверяем, является ли значение в con ссылкой или путем к файлу
164
+ if "nopicture" in con:
165
+ poster[i] = placeholder_path
166
+ else:
167
+ poster[i] = poster_path + con
168
+
169
+ for i, con in enumerate(res["year"]):
170
+ year[i] = con
171
+
172
+ for i, con in enumerate(res["title"]):
173
+ title[i] = con
174
+
175
+ for i, con in enumerate(res["description"]):
176
+ description[i] = con
177
+
178
+ for i, con in enumerate(res["rating"]):
179
+ rating[i] = con
180
+
181
+ for i, con in enumerate(res["tags"]):
182
+ genre[i] = ", ".join(con)
183
+
184
+ for i, con in enumerate(res["cast"]):
185
+ cast[i] = con
186
+
187
+ for i, con in enumerate(res["score"]):
188
+ score[i] = con
189
+
190
+ for i, con in enumerate(res["url"]):
191
+ links[i] = show_path + con
192
+
193
+ for i, con in enumerate(res["county"]):
194
+ country[i] = con
195
+
196
+ st.markdown("---")
197
+
198
+ # Проверяем, пустой ли набор результатов
199
+ if len(res) == 0:
200
+ st.error(
201
+ "Сериалы по выбранным параметрам не найдены. Попробуйте изменить критерии поиска."
202
+ )
203
+ else:
204
+ # Если результаты есть, выводим их
205
+ iterations = min(len(res), top_n)
206
+
207
+ for i in range(iterations):
208
+
209
+ col1, col2 = st.columns([1, 3])
210
+ with col1:
211
+ st.image(poster[i])
212
+ # Добавляем ссылку под картинкой
213
+ st.markdown(
214
+ f"<a href='{links[i]}' target='_blank' style='display: block; text-align: center; color: grey; font-size: small; font-style: italic;'>Смотреть сериал</a>",
215
+ unsafe_allow_html=True,
216
+ )
217
+
218
+ with col2:
219
+
220
+ st.markdown(
221
+ f"<span style='font-weight:bold; font-size:22px;'>Название сериала:</span> <span style='font-size:20px;'>«{title[i]}»</span>",
222
+ unsafe_allow_html=True,
223
+ )
224
+
225
+ st.markdown(
226
+ f"<span style='font-weight:bold; font-size:16px;'>Страна:</span> <span style='font-size:16px;'>{country[i]}</span>",
227
+ unsafe_allow_html=True,
228
+ )
229
+
230
+ st.markdown(
231
+ f"<span style='font-weight:bold; font-size:16px;'>Год выпуска:</span> <span style='font-size:16px;'>{year[i]}</span>",
232
+ unsafe_allow_html=True,
233
+ )
234
+
235
+ st.markdown(
236
+ f"<span style='font-weight:bold; font-size:16px;'>Жанр:</span> <span style='font-size:16px;'>{genre[i]}</span>",
237
+ unsafe_allow_html=True,
238
+ )
239
+
240
+ rating_display = (
241
+ "Нет информации" if pd.isna(rating[i]) else rating[i]
242
+ )
243
+
244
+ st.markdown(
245
+ f"<span style='font-weight:bold; font-size:16px;'>Рейтинг:</span> <span style='font-size:16px;'>{rating_display}</span>",
246
+ unsafe_allow_html=True,
247
+ )
248
+
249
+ st.markdown(
250
+ "<h6 style='font-weight:bold;'>В ролях:</h6>",
251
+ unsafe_allow_html=True,
252
+ )
253
+
254
+ st.markdown(
255
+ f"<div style='text-align: justify; margin-bottom: 18px;'>{cast[i]}</div>",
256
+ unsafe_allow_html=True,
257
+ )
258
+
259
+ st.markdown(
260
+ "<h6 style='font-weight:bold;'>Описание:</h6>",
261
+ unsafe_allow_html=True,
262
+ )
263
+
264
+ st.markdown(
265
+ f"<div style='text-align: justify;'>{description[i]}</div>",
266
+ unsafe_allow_html=True,
267
+ )
268
+ score_display = round(score[i], 3)
269
+ st.markdown(
270
+ f"<div style='color: grey;'><hr style='margin: 2px 0;'/><span style='font-weight:bold; font-size:13px; font-style: italic;'>Оценка FAISS (расстояние):</span> <span style='font-size:13px; font-style: italic;'>{score_display}</span><hr style='margin: 2px 0;'/></div>",
271
+ unsafe_allow_html=True,
272
+ )
273
+
274
+ st.markdown("---")