Files
hotels/upload_to_graphiti.py
Фёдор 0cf3297290 Проект аудита отелей: основные скрипты и документация
- Краулеры: smart_crawler.py, regional_crawler.py
- Аудит: audit_orel_to_excel.py, audit_chukotka_to_excel.py
- РКН проверка: check_rkn_registry.py, recheck_unclear_rkn.py
- Отчёты: create_orel_horizontal_report.py
- Обработка: process_all_hotels_embeddings.py
- Документация: README.md, DB_SCHEMA_REFERENCE.md
2025-10-16 10:52:09 +03:00

195 lines
7.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
Загрузка данных отелей в Graphiti для векторизации
"""
import asyncio
import httpx
import psycopg2
from psycopg2.extras import RealDictCursor
from urllib.parse import unquote
from datetime import datetime
import logging
import os
from bs4 import BeautifulSoup
# Настройка логирования
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler(f'graphiti_upload_{datetime.now().strftime("%Y%m%d_%H%M%S")}.log'),
logging.StreamHandler()
]
)
logger = logging.getLogger(__name__)
# Конфигурация
DB_CONFIG = {
'host': "147.45.189.234",
'port': 5432,
'database': "default_db",
'user': "gen_user",
'password': unquote("2~~9_%5EkVsU%3F2%5CS")
}
GRAPHITI_API = "http://185.197.75.249:9200/upload"
PROXY = os.getenv('HTTP_PROXY', 'http://185.197.75.249:3128')
RATE_LIMIT_DELAY = 1 # Задержка между загрузками
async def upload_to_graphiti(hotel_data: dict, pages_data: list, group_id: str) -> dict:
"""Загрузка данных отеля в Graphiti"""
try:
# Формируем текст для загрузки
text_parts = []
# Заголовок с информацией об отеле
header = f"""
ОТЕЛЬ: {hotel_data['full_name']}
РЕГИОН: {hotel_data['region_name']}
САЙТ: {hotel_data['website_address']}
ИНН: {hotel_data['owner_inn'] or 'не указан'}
ТЕЛЕФОН: {hotel_data['phone'] or 'не указан'}
EMAIL: {hotel_data['email'] or 'не указан'}
"""
if hotel_data.get('rkn_registry_status') == 'found':
header += f"РЕЕСТР РКН: ✅ Зарегистрирован ({hotel_data['rkn_registry_number']}, {hotel_data['rkn_registry_date']})\n"
else:
header += f"РЕЕСТР РКН: ❌ Не найден или неясен\n"
text_parts.append(header)
# Добавляем контент страниц (ограничиваем размер)
total_chars = 0
max_total_chars = 50000 # Максимум 50К символов на отель
for page in pages_data:
if total_chars >= max_total_chars:
break
# Очищаем HTML
soup = BeautifulSoup(page['html'], 'html.parser')
for tag in soup.find_all(['script', 'style']):
tag.decompose()
clean_text = soup.get_text()
clean_text = ' '.join(clean_text.split()) # Убираем лишние пробелы
if len(clean_text) > 100: # Только если есть содержимое
# Ограничиваем размер каждой страницы
page_text = clean_text[:3000]
text_parts.append(f"\n--- СТРАНИЦА: {page['url']} ---\n{page_text}")
total_chars += len(page_text)
full_text = '\n\n'.join(text_parts)
# Финальное ограничение
if len(full_text) > max_total_chars:
full_text = full_text[:max_total_chars]
# Формируем запрос
payload = {
"group_id": group_id,
"title": f"Отель: {hotel_data['full_name']} ({hotel_data['region_name']})",
"content": full_text
}
# Отправляем в Graphiti (без прокси, т.к. локальный API)
async with httpx.AsyncClient(timeout=120.0) as client:
response = await client.post(GRAPHITI_API, json=payload)
if response.status_code == 200:
result = response.json()
logger.info(f" ✅ Загружено в Graphiti: {len(pages_data)} страниц")
return {'success': True, 'result': result}
else:
logger.error(f" ✗ Ошибка Graphiti: {response.status_code}")
return {'success': False, 'error': response.text}
except Exception as e:
logger.error(f" ✗ Ошибка загрузки: {e}")
return {'success': False, 'error': str(e)}
async def main():
"""Основная функция"""
import sys
region = sys.argv[1] if len(sys.argv) > 1 else 'Камчатский край'
group_id = f"hotel_{region.lower().replace(' ', '_').replace('ский', '').replace('край', '').strip()}"
conn = psycopg2.connect(**DB_CONFIG, cursor_factory=RealDictCursor)
try:
cur = conn.cursor()
# Получаем отели с данными
cur.execute('''
SELECT DISTINCT h.id, h.full_name, h.region_name, h.website_address,
h.owner_inn, h.phone, h.email, h.rkn_registry_status,
h.rkn_registry_number, h.rkn_registry_date
FROM hotel_main h
JOIN hotel_website_raw w ON h.id = w.hotel_id
WHERE h.region_name ILIKE %s
ORDER BY h.full_name
''', (f'%{region}%',))
hotels = cur.fetchall()
logger.info(f"\n{'='*70}")
logger.info(f"🚀 ЗАГРУЗКА В GRAPHITI: {region}")
logger.info(f"📊 Отелей: {len(hotels)}")
logger.info(f"🏷️ Group ID: {group_id}")
logger.info(f"⏱️ Примерное время: {len(hotels) * RATE_LIMIT_DELAY / 60:.1f} минут")
logger.info(f"{'='*70}\n")
successful = 0
failed = 0
for i, hotel in enumerate(hotels, 1):
logger.info(f"\n[{i}/{len(hotels)}] {'='*40}")
logger.info(f"🏨 {hotel['full_name']}")
logger.info(f"🌐 {hotel['website_address']}")
# Получаем страницы отеля
cur.execute('''
SELECT url, html, page_title
FROM hotel_website_raw
WHERE hotel_id = %s
ORDER BY depth, crawled_at
''', (hotel['id'],))
pages = cur.fetchall()
logger.info(f" 📄 Страниц: {len(pages)}")
# Загружаем в Graphiti
result = await upload_to_graphiti(hotel, pages, group_id)
if result['success']:
successful += 1
else:
failed += 1
# Задержка
await asyncio.sleep(RATE_LIMIT_DELAY)
# Итоги
logger.info(f"\n{'='*70}")
logger.info("📊 ИТОГИ ЗАГРУЗКИ:")
logger.info(f" ✅ Успешно: {successful}/{len(hotels)}")
logger.info(f" ✗ Ошибки: {failed}/{len(hotels)}")
logger.info(f"{'='*70}")
finally:
cur.close()
conn.close()
if __name__ == "__main__":
asyncio.run(main())