Update app.py
Browse files
app.py
CHANGED
|
@@ -13,7 +13,7 @@ from huggingface_hub import hf_hub_download
|
|
| 13 |
# ==========================
|
| 14 |
|
| 15 |
st.set_page_config(
|
| 16 |
-
page_title="Поиск
|
| 17 |
layout="wide",
|
| 18 |
)
|
| 19 |
|
|
@@ -61,14 +61,12 @@ if HF_TOKEN is None:
|
|
| 61 |
)
|
| 62 |
st.stop()
|
| 63 |
|
| 64 |
-
|
| 65 |
# ==========================
|
| 66 |
# ЗАГРУЗКА ДАННЫХ И МОДЕЛИ
|
| 67 |
# ==========================
|
| 68 |
|
| 69 |
@st.cache_data(show_spinner="Скачиваю и загружаю данные из приватного датасета...")
|
| 70 |
def load_data():
|
| 71 |
-
# Скачиваем файлы из приватного датасета с помощью токена
|
| 72 |
all_data_path = hf_hub_download(
|
| 73 |
repo_id=HF_DATA_REPO,
|
| 74 |
filename=ALL_DATA_FILENAME,
|
|
@@ -83,12 +81,10 @@ def load_data():
|
|
| 83 |
token=HF_TOKEN,
|
| 84 |
)
|
| 85 |
|
| 86 |
-
# Загружаем Excel с метаданными
|
| 87 |
df_all = pd.read_excel(all_data_path)
|
| 88 |
df_all["registration_number"] = df_all["registration_number"].astype(str)
|
| 89 |
df_all = df_all.set_index("registration_number", drop=False)
|
| 90 |
|
| 91 |
-
# Загружаем эмбеддинги
|
| 92 |
with open(embeddings_path, "r", encoding="utf-8") as f:
|
| 93 |
emb_data = json.load(f)
|
| 94 |
|
|
@@ -98,7 +94,6 @@ def load_data():
|
|
| 98 |
dtype="float32",
|
| 99 |
)
|
| 100 |
|
| 101 |
-
# Нормируем эмбеддинги
|
| 102 |
norms = np.linalg.norm(emb_matrix, axis=1, keepdims=True)
|
| 103 |
emb_matrix = emb_matrix / np.maximum(norms, 1e-8)
|
| 104 |
|
|
@@ -188,21 +183,17 @@ def run_search(query: str, top_k: int):
|
|
| 188 |
if not query:
|
| 189 |
empty_df = pd.DataFrame(columns=["№", "score"] + COLUMNS_ORDER)
|
| 190 |
empty_df_ru = empty_df.rename(columns=COLUMN_LABELS_RU)
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
return df_display, None
|
| 195 |
|
| 196 |
results = search_core(query, top_k)
|
| 197 |
df_res = build_result_df(results)
|
| 198 |
|
| 199 |
-
# Переименовываем колонки в русский вариант
|
| 200 |
df_res_ru = df_res.rename(columns=COLUMN_LABELS_RU)
|
| 201 |
-
|
| 202 |
-
# Для отображения: используем № как индекс, чтобы не было второй нумерации
|
| 203 |
df_display = df_res_ru.set_index("№")
|
| 204 |
|
| 205 |
-
# Формируем Excel в памяти (с русскими заголовками и явным столбцом №)
|
| 206 |
output = io.BytesIO()
|
| 207 |
with pd.ExcelWriter(output, engine="xlsxwriter") as writer:
|
| 208 |
df_res_ru.to_excel(writer, index=False)
|
|
@@ -215,9 +206,13 @@ def run_search(query: str, top_k: int):
|
|
| 215 |
# UI НА STREAMLIT
|
| 216 |
# ==========================
|
| 217 |
|
| 218 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 219 |
|
| 220 |
-
#
|
| 221 |
top_k = st.slider(
|
| 222 |
"Сколько результатов показать",
|
| 223 |
min_value=1,
|
|
@@ -226,28 +221,28 @@ top_k = st.slider(
|
|
| 226 |
step=1,
|
| 227 |
)
|
| 228 |
|
| 229 |
-
#
|
| 230 |
query = st.text_area(
|
| 231 |
"Введите запрос",
|
| 232 |
-
height=120,
|
| 233 |
placeholder="Например: пластификаторы для самоуплотняющихся бетонов",
|
| 234 |
key="query",
|
| 235 |
)
|
| 236 |
|
| 237 |
-
# Кнопки
|
| 238 |
-
|
| 239 |
-
with
|
|
|
|
|
|
|
| 240 |
do_search = st.button("🔍 Поиск", type="primary")
|
| 241 |
-
with
|
| 242 |
clear = st.button("🧹 Очистить")
|
| 243 |
|
| 244 |
-
# 3) Корректная очистка: не трогаем конкретные ключи после создания виджетов,
|
| 245 |
-
# а чистим всё состояние и перезапускаем скрипт
|
| 246 |
if clear:
|
| 247 |
st.session_state.clear()
|
| 248 |
st.experimental_rerun()
|
| 249 |
|
| 250 |
-
#
|
| 251 |
if do_search:
|
| 252 |
with st.spinner("Идёт поиск по базе диссертаций..."):
|
| 253 |
df_res, excel_bytes = run_search(query, top_k)
|
|
@@ -257,7 +252,6 @@ if do_search:
|
|
| 257 |
else:
|
| 258 |
st.success(f"Найдено записей: {len(df_res)}")
|
| 259 |
|
| 260 |
-
# 6) Без вкладок — просто таблица + кнопка скачивания
|
| 261 |
st.dataframe(
|
| 262 |
df_res,
|
| 263 |
use_container_width=True,
|
|
@@ -275,3 +269,11 @@ if do_search:
|
|
| 275 |
)
|
| 276 |
else:
|
| 277 |
st.info("Введите запрос выше и нажмите кнопку «Поиск».")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
# ==========================
|
| 14 |
|
| 15 |
st.set_page_config(
|
| 16 |
+
page_title="Поиск постдока",
|
| 17 |
layout="wide",
|
| 18 |
)
|
| 19 |
|
|
|
|
| 61 |
)
|
| 62 |
st.stop()
|
| 63 |
|
|
|
|
| 64 |
# ==========================
|
| 65 |
# ЗАГРУЗКА ДАННЫХ И МОДЕЛИ
|
| 66 |
# ==========================
|
| 67 |
|
| 68 |
@st.cache_data(show_spinner="Скачиваю и загружаю данные из приватного датасета...")
|
| 69 |
def load_data():
|
|
|
|
| 70 |
all_data_path = hf_hub_download(
|
| 71 |
repo_id=HF_DATA_REPO,
|
| 72 |
filename=ALL_DATA_FILENAME,
|
|
|
|
| 81 |
token=HF_TOKEN,
|
| 82 |
)
|
| 83 |
|
|
|
|
| 84 |
df_all = pd.read_excel(all_data_path)
|
| 85 |
df_all["registration_number"] = df_all["registration_number"].astype(str)
|
| 86 |
df_all = df_all.set_index("registration_number", drop=False)
|
| 87 |
|
|
|
|
| 88 |
with open(embeddings_path, "r", encoding="utf-8") as f:
|
| 89 |
emb_data = json.load(f)
|
| 90 |
|
|
|
|
| 94 |
dtype="float32",
|
| 95 |
)
|
| 96 |
|
|
|
|
| 97 |
norms = np.linalg.norm(emb_matrix, axis=1, keepdims=True)
|
| 98 |
emb_matrix = emb_matrix / np.maximum(norms, 1e-8)
|
| 99 |
|
|
|
|
| 183 |
if not query:
|
| 184 |
empty_df = pd.DataFrame(columns=["№", "score"] + COLUMNS_ORDER)
|
| 185 |
empty_df_ru = empty_df.rename(columns=COLUMN_LABELS_RU)
|
| 186 |
+
df_display = (
|
| 187 |
+
empty_df_ru.set_index("№") if "№" in empty_df_ru.columns else empty_df_ru
|
| 188 |
+
)
|
| 189 |
return df_display, None
|
| 190 |
|
| 191 |
results = search_core(query, top_k)
|
| 192 |
df_res = build_result_df(results)
|
| 193 |
|
|
|
|
| 194 |
df_res_ru = df_res.rename(columns=COLUMN_LABELS_RU)
|
|
|
|
|
|
|
| 195 |
df_display = df_res_ru.set_index("№")
|
| 196 |
|
|
|
|
| 197 |
output = io.BytesIO()
|
| 198 |
with pd.ExcelWriter(output, engine="xlsxwriter") as writer:
|
| 199 |
df_res_ru.to_excel(writer, index=False)
|
|
|
|
| 206 |
# UI НА STREAMLIT
|
| 207 |
# ==========================
|
| 208 |
|
| 209 |
+
# Заголовок по центру
|
| 210 |
+
st.markdown(
|
| 211 |
+
"<h1 style='text-align: center; margin-bottom: 0.5rem;'>Поиск постдока</h1>",
|
| 212 |
+
unsafe_allow_html=True,
|
| 213 |
+
)
|
| 214 |
|
| 215 |
+
# Слайдер
|
| 216 |
top_k = st.slider(
|
| 217 |
"Сколько результатов показать",
|
| 218 |
min_value=1,
|
|
|
|
| 221 |
step=1,
|
| 222 |
)
|
| 223 |
|
| 224 |
+
# Поле ввода: ~4 строки
|
| 225 |
query = st.text_area(
|
| 226 |
"Введите запрос",
|
| 227 |
+
height=120,
|
| 228 |
placeholder="Например: пластификаторы для самоуплотняющихся бетонов",
|
| 229 |
key="query",
|
| 230 |
)
|
| 231 |
|
| 232 |
+
# Кнопки: Поиск по центру, Очистить справа
|
| 233 |
+
btn_col1, btn_col2, btn_col3 = st.columns([1, 1, 1])
|
| 234 |
+
with btn_col1:
|
| 235 |
+
pass
|
| 236 |
+
with btn_col2:
|
| 237 |
do_search = st.button("🔍 Поиск", type="primary")
|
| 238 |
+
with btn_col3:
|
| 239 |
clear = st.button("🧹 Очистить")
|
| 240 |
|
|
|
|
|
|
|
| 241 |
if clear:
|
| 242 |
st.session_state.clear()
|
| 243 |
st.experimental_rerun()
|
| 244 |
|
| 245 |
+
# Результаты
|
| 246 |
if do_search:
|
| 247 |
with st.spinner("Идёт поиск по базе диссертаций..."):
|
| 248 |
df_res, excel_bytes = run_search(query, top_k)
|
|
|
|
| 252 |
else:
|
| 253 |
st.success(f"Найдено записей: {len(df_res)}")
|
| 254 |
|
|
|
|
| 255 |
st.dataframe(
|
| 256 |
df_res,
|
| 257 |
use_container_width=True,
|
|
|
|
| 269 |
)
|
| 270 |
else:
|
| 271 |
st.info("Введите запрос выше и нажмите кнопку «Поиск».")
|
| 272 |
+
|
| 273 |
+
# Футер
|
| 274 |
+
st.markdown(
|
| 275 |
+
"<p style='font-size: 0.8rem; text-align: center; color: gray;'>"
|
| 276 |
+
"(с) Антон Лощилов, 2025"
|
| 277 |
+
"</p>",
|
| 278 |
+
unsafe_allow_html=True,
|
| 279 |
+
)
|