fix checker

This commit is contained in:
poturaevpetr
2025-12-26 01:21:07 +05:00
parent 0f8e6be08c
commit cceaf65576
3 changed files with 438 additions and 44 deletions
+42 -43
View File
@@ -1,12 +1,14 @@
"""
Класс для проверки файлов без AI заключения и отправки на распознавание
Исправленная версия recognition_checker.py
Избегает detached instance error путём использования словарей вместо SQLAlchemy объектов
"""
import requests
from sqlalchemy import inspect
from typing import List, Optional
from typing import List, Optional, Dict, Any
import logging
from concurrent.futures import ThreadPoolExecutor, as_completed
import threading
from concurrent.futures import ThreadPoolExecutor, as_completed
from autoLoader.database import get_db_session, Audio, AiConclusion
from autoLoader.config import GIGAAM_API_URL
@@ -67,7 +69,7 @@ class RecognitionChecker:
return True
def get_files_without_conclusion(self, limit: Optional[int] = None) -> List[Audio]:
def get_files_without_conclusion(self, limit: Optional[int] = None) -> List[Dict[str, Any]]:
"""
Находит все файлы, у которых нет AI заключения
@@ -75,7 +77,7 @@ class RecognitionChecker:
limit: Ограничение количества файлов (None = все)
Returns:
Список объектов Audio без заключения
Список словарей с информацией о файлах (избегаем detached instance)
"""
if not self.check_database():
logger.error("❌ База данных не готова")
@@ -97,40 +99,52 @@ class RecognitionChecker:
# Находим все audio, у которых нет заключения
if conclusion_ids:
files_without_conclusion = db.query(Audio).filter(
audio_objects = db.query(Audio).filter(
~Audio.id.in_(conclusion_ids)
).all()
else:
# Если заключений нет вообще - все файлы без заключения
files_without_conclusion = db.query(Audio).all()
audio_objects = db.query(Audio).all()
logger.info(f"📊 Найдено файлов без заключения: {len(files_without_conclusion)}")
# Конвертируем в словари внутри сессии БД
files_data = []
for audio in audio_objects:
files_data.append({
'id': str(audio.id),
'filename': audio.filename,
'file_size': audio.file_size,
'index_date': audio.index_date.isoformat() if audio.index_date else None
})
logger.info(f"📊 Найдено файлов без заключения: {len(files_data)}")
if limit:
return files_without_conclusion[:limit]
return files_data[:limit]
return files_without_conclusion
return files_data
except Exception as e:
logger.error(f"❌ Ошибка при поиске файлов: {e}")
return []
def send_to_recognition(self, audio: Audio) -> bool:
def send_to_recognition(self, audio_data: Dict[str, Any]) -> bool:
"""
Отправляет файл на распознавание в GigaAM API
Args:
audio: Объект Audio для распознавания
audio_data: Словарь с данными об аудио файле
Returns:
True если успешно отправлен, иначе False
"""
filename = audio_data.get('filename')
payload = {
"filename": audio.filename
"filename": filename
}
try:
logger.info(f"📤 [Thread-{threading.current_thread().name}] Отправка файла {audio.filename} на распознавание...")
logger.info(f"📤 [Thread-{threading.current_thread().name}] Отправка файла {filename} на распознавание...")
response = requests.post(
self.api_url,
@@ -139,7 +153,7 @@ class RecognitionChecker:
)
if response.status_code == 200 or response.status_code == 202:
logger.info(f"✅ [Thread-{threading.current_thread().name}] Файл {audio.filename} успешно отправлен")
logger.info(f"✅ [Thread-{threading.current_thread().name}] Файл {filename} успешно отправлен")
# Thread-safe обновление счётчиков
with self._lock:
@@ -156,7 +170,7 @@ class RecognitionChecker:
return False
except requests.exceptions.Timeout:
logger.error(f"❌ [Thread-{threading.current_thread().name}] Таймаут при отправке файла {audio.filename}")
logger.error(f"❌ [Thread-{threading.current_thread().name}] Таймаут при отправке файла {filename}")
with self._lock:
self._failed_count += 1
@@ -170,19 +184,19 @@ class RecognitionChecker:
return False
except Exception as e:
logger.error(f"❌ [Thread-{threading.current_thread().name}] Ошибка при отправке {audio.filename}: {e}")
logger.error(f"❌ [Thread-{threading.current_thread().name}] Ошибка при отправке {filename}: {e}")
with self._lock:
self._failed_count += 1
return False
def send_to_recognition_parallel(self, audio_list: List[Audio]) -> dict:
def send_to_recognition_parallel(self, audio_list: List[Dict[str, Any]]) -> Dict[str, Any]:
"""
Отправляет несколько файлов на распознавание параллельно
Args:
audio_list: Список объектов Audio для распознавания
audio_list: Список словарей с данными об аудио файлах
Returns:
Словарь с результатами отправки
@@ -226,19 +240,19 @@ class RecognitionChecker:
success = future.result()
result = {
"filename": audio.filename,
"audio_id": str(audio.id),
"filename": audio.get('filename'),
"audio_id": audio.get('id'),
"success": success
}
results["files"].append(result)
except Exception as exc:
logger.error(f"❌ Файл {audio.filename} сгенерировал исключение: {exc}")
logger.error(f"❌ Файл {audio.get('filename')} сгенерировал исключение: {exc}")
result = {
"filename": audio.filename,
"audio_id": str(audio.id),
"filename": audio.get('filename'),
"audio_id": audio.get('id'),
"success": False,
"error": str(exc)
}
@@ -257,7 +271,7 @@ class RecognitionChecker:
return results
def process_all_pending(self, limit: Optional[int] = None, parallel: bool = True) -> dict:
def process_all_pending(self, limit: Optional[int] = None, parallel: bool = True) -> Dict[str, Any]:
"""
Находит и отправляет все файлы без заключения на распознавание
@@ -298,8 +312,8 @@ class RecognitionChecker:
success = self.send_to_recognition(audio)
result = {
"filename": audio.filename,
"audio_id": str(audio.id),
"filename": audio.get('filename'),
"audio_id": audio.get('id'),
"success": success
}
@@ -347,7 +361,7 @@ class RecognitionChecker:
# Удобная функция для запуска из командной строки
def process_pending_files(api_url: Optional[str] = None, limit: Optional[int] = None):
def process_pending_files(api_url: Optional[str] = None, limit: Optional[int] = None) -> Dict[str, Any]:
"""
Обрабатывает все файлы без заключения
@@ -372,18 +386,3 @@ def process_pending_files(api_url: Optional[str] = None, limit: Optional[int] =
# Обрабатываем файлы
return checker.process_all_pending(limit)
if __name__ == "__main__":
# Пример использования
import sys
api_url = sys.argv[1] if len(sys.argv) > 1 else "http://localhost:5001/api/call/process"
limit = int(sys.argv[2]) if len(sys.argv) > 2 else None
results = process_pending_files(api_url, limit)
print(f"\n📊 Результаты:")
print(f"Всего: {results['total']}")
print(f"Отправлено: {results['sent']}")
print(f"Ошибок: {results['failed']}")