Update app.py
Browse files
app.py
CHANGED
|
@@ -12,6 +12,11 @@ import logging
|
|
| 12 |
from sklearn.preprocessing import normalize
|
| 13 |
from concurrent.futures import ThreadPoolExecutor
|
| 14 |
import requests
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
|
| 16 |
# Настройка логирования
|
| 17 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
|
@@ -70,6 +75,9 @@ batch_size = 32
|
|
| 70 |
# Количество потоков для параллельной обработки
|
| 71 |
num_threads = 5
|
| 72 |
|
|
|
|
|
|
|
|
|
|
| 73 |
def get_db_connection():
|
| 74 |
"""Устанавливает соединение с базой данных."""
|
| 75 |
try:
|
|
@@ -354,9 +362,9 @@ def rerank_with_api(query, results, top_k):
|
|
| 354 |
except requests.exceptions.RequestException as e:
|
| 355 |
logging.error(f"Ошибка при запросе к API реранжировщика: {e}")
|
| 356 |
return []
|
| 357 |
-
|
| 358 |
-
def
|
| 359 |
-
"""
|
| 360 |
global search_in_progress
|
| 361 |
search_in_progress = True
|
| 362 |
start_time = time.time()
|
|
@@ -364,7 +372,7 @@ def search_movies(query, top_k=25):
|
|
| 364 |
try:
|
| 365 |
conn = get_db_connection()
|
| 366 |
if conn is None:
|
| 367 |
-
|
| 368 |
|
| 369 |
query_crc32 = calculate_crc32(query)
|
| 370 |
query_embedding = get_embedding_from_db(conn, query_cache_table, "query_crc32", query_crc32, model_name)
|
|
@@ -416,34 +424,65 @@ def search_movies(query, top_k=25):
|
|
| 416 |
movie_data_dict = get_movie_data_from_db(conn, movie_ids)
|
| 417 |
conn.close()
|
| 418 |
|
| 419 |
-
|
| 420 |
for movie_id, score in reranked_results:
|
| 421 |
# Находим данные фильма
|
| 422 |
movie_data, _ = movie_data_dict.get(movie_id, (None, None))
|
| 423 |
if movie_data:
|
| 424 |
-
|
| 425 |
-
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
|
|
|
|
|
|
|
|
|
|
| 429 |
else:
|
| 430 |
logging.warning(f"Данные для фильма с ID {movie_id} не найдены в БД.")
|
| 431 |
|
| 432 |
search_time = time.time() - start_time
|
| 433 |
logging.info(f"Поиск выполнен за {search_time:.2f} секунд.")
|
| 434 |
|
| 435 |
-
return
|
| 436 |
|
| 437 |
except Exception as e:
|
| 438 |
logging.error(f"Ошибка при выполнении поиска: {e}")
|
| 439 |
-
|
| 440 |
|
| 441 |
finally:
|
| 442 |
search_in_progress = False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 443 |
|
| 444 |
-
|
| 445 |
-
|
| 446 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 447 |
|
| 448 |
# Создаем интерфейс Gradio
|
| 449 |
iface = gr.Interface(
|
|
@@ -454,5 +493,14 @@ iface = gr.Interface(
|
|
| 454 |
description="Введите описание фильма, который вы ищете, и система найдет наиболее похожие фильмы."
|
| 455 |
)
|
| 456 |
|
| 457 |
-
#
|
| 458 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
from sklearn.preprocessing import normalize
|
| 13 |
from concurrent.futures import ThreadPoolExecutor
|
| 14 |
import requests
|
| 15 |
+
from fastapi import FastAPI, HTTPException, Query
|
| 16 |
+
from typing import List
|
| 17 |
+
import uvicorn
|
| 18 |
+
from starlette.requests import Request
|
| 19 |
+
from starlette.responses import HTMLResponse
|
| 20 |
|
| 21 |
# Настройка логирования
|
| 22 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
|
|
|
| 75 |
# Количество потоков для параллельной обработки
|
| 76 |
num_threads = 5
|
| 77 |
|
| 78 |
+
# FastAPI приложение
|
| 79 |
+
app = FastAPI()
|
| 80 |
+
|
| 81 |
def get_db_connection():
|
| 82 |
"""Устанавливает соединение с базой данных."""
|
| 83 |
try:
|
|
|
|
| 362 |
except requests.exceptions.RequestException as e:
|
| 363 |
logging.error(f"Ошибка при запросе к API реранжировщика: {e}")
|
| 364 |
return []
|
| 365 |
+
|
| 366 |
+
def search_movies_internal(query: str, top_k: int = 25):
|
| 367 |
+
"""Внутренняя функция для поиска фильмов по запросу (используется и в Gradio, и в API)."""
|
| 368 |
global search_in_progress
|
| 369 |
search_in_progress = True
|
| 370 |
start_time = time.time()
|
|
|
|
| 372 |
try:
|
| 373 |
conn = get_db_connection()
|
| 374 |
if conn is None:
|
| 375 |
+
raise Exception("Ошибка подключения к базе данных")
|
| 376 |
|
| 377 |
query_crc32 = calculate_crc32(query)
|
| 378 |
query_embedding = get_embedding_from_db(conn, query_cache_table, "query_crc32", query_crc32, model_name)
|
|
|
|
| 424 |
movie_data_dict = get_movie_data_from_db(conn, movie_ids)
|
| 425 |
conn.close()
|
| 426 |
|
| 427 |
+
formatted_results = []
|
| 428 |
for movie_id, score in reranked_results:
|
| 429 |
# Находим данные фильма
|
| 430 |
movie_data, _ = movie_data_dict.get(movie_id, (None, None))
|
| 431 |
if movie_data:
|
| 432 |
+
formatted_results.append({
|
| 433 |
+
"movie_id": movie_id,
|
| 434 |
+
"name": movie_data['name'],
|
| 435 |
+
"year": movie_data['year'],
|
| 436 |
+
"genres": [genre['name'] for genre in movie_data['genres']],
|
| 437 |
+
"description": movie_data.get('description', ''),
|
| 438 |
+
"relevance_score": score
|
| 439 |
+
})
|
| 440 |
else:
|
| 441 |
logging.warning(f"Данные для фильма с ID {movie_id} не найдены в БД.")
|
| 442 |
|
| 443 |
search_time = time.time() - start_time
|
| 444 |
logging.info(f"Поиск выполнен за {search_time:.2f} секунд.")
|
| 445 |
|
| 446 |
+
return formatted_results, search_time
|
| 447 |
|
| 448 |
except Exception as e:
|
| 449 |
logging.error(f"Ошибка при выполнении поиска: {e}")
|
| 450 |
+
raise
|
| 451 |
|
| 452 |
finally:
|
| 453 |
search_in_progress = False
|
| 454 |
+
|
| 455 |
+
def search_movies(query, top_k=25):
|
| 456 |
+
"""Функция поиска фильмов для Gradio интерфейса."""
|
| 457 |
+
try:
|
| 458 |
+
results, search_time = search_movies_internal(query, top_k)
|
| 459 |
+
output = f"<p>Время поиска: {search_time:.2f} сек</p>"
|
| 460 |
+
for result in results:
|
| 461 |
+
output += f"<h3>{result['name']} ({result['year']})</h3>\n"
|
| 462 |
+
output += f"<p><strong>Жанры:</strong> {', '.join(result['genres'])}</p>\n"
|
| 463 |
+
output += f"<p><strong>Описание:</strong> {result['description']}</p>\n"
|
| 464 |
+
output += f"<p><strong>Релевантность (reranker score):</strong> {result['relevance_score']:.4f}</p>\n"
|
| 465 |
+
output += "<hr>\n"
|
| 466 |
+
return output
|
| 467 |
+
except Exception as e:
|
| 468 |
+
return f"<p>Произошла ошибка при выполнении поиска: {e}</p>"
|
| 469 |
|
| 470 |
+
@app.get("/search/", response_model=List[dict])
|
| 471 |
+
async def api_search_movies(query: str = Query(..., description="Поисковый ��апрос"), top_k: int = Query(25, description="Количество возвращаемых результатов")):
|
| 472 |
+
"""API endpoint для поиска фильмов."""
|
| 473 |
+
try:
|
| 474 |
+
results, _ = search_movies_internal(query, top_k)
|
| 475 |
+
return results
|
| 476 |
+
except Exception as e:
|
| 477 |
+
raise HTTPException(status_code=500, detail=str(e))
|
| 478 |
+
|
| 479 |
+
# Запускаем обработку фильмов в отдельном потоке (если ещё не запущена)
|
| 480 |
+
if not 'processing_thread' in globals():
|
| 481 |
+
processing_thread = threading.Thread(target=process_movies)
|
| 482 |
+
processing_thread.start()
|
| 483 |
+
elif not processing_thread.is_alive():
|
| 484 |
+
processing_thread = threading.Thread(target=process_movies)
|
| 485 |
+
processing_thread.start()
|
| 486 |
|
| 487 |
# Создаем интерфейс Gradio
|
| 488 |
iface = gr.Interface(
|
|
|
|
| 493 |
description="Введите описание фильма, который вы ищете, и система найдет наиболее похожие фильмы."
|
| 494 |
)
|
| 495 |
|
| 496 |
+
# Встраиваем Gradio в FastAPI
|
| 497 |
+
app = gr.mount_gradio_app(app, iface, path="/")
|
| 498 |
+
|
| 499 |
+
# Рут-эндпоинт для демонстрации, что FastAPI работает
|
| 500 |
+
@app.get("/api")
|
| 501 |
+
async def root():
|
| 502 |
+
return {"message": "FastAPI is running. Access the API documentation at /docs"}
|
| 503 |
+
|
| 504 |
+
# Запускаем FastAPI
|
| 505 |
+
if __name__ == "__main__":
|
| 506 |
+
uvicorn.run(app, host="0.0.0.0", port=7860)
|