From 86245ce1fb611055074ba1a2bde730f93f465c4e Mon Sep 17 00:00:00 2001 From: poturaevpetr Date: Tue, 30 Dec 2025 00:31:11 +0500 Subject: [PATCH] add autorestart rec --- apiApp/routers/audio_management_router.py | 185 ++++++++++------------ 1 file changed, 88 insertions(+), 97 deletions(-) diff --git a/apiApp/routers/audio_management_router.py b/apiApp/routers/audio_management_router.py index 903ba78..9eece54 100644 --- a/apiApp/routers/audio_management_router.py +++ b/apiApp/routers/audio_management_router.py @@ -1,3 +1,84 @@ +""" +API endpoints для управления аудиофайлами (регистрация и пакетная обработка) +Используется Calls_WEB_Client_main для оркестрации процесса распознавания +""" +from fastapi import APIRouter, HTTPException, Depends, BackgroundTasks +from sqlalchemy.orm import Session +from pydantic import BaseModel +from typing import Optional, List +import os +import logging +from datetime import datetime + +from apiApp.database import get_db +from apiApp.database.Audio import Audio +from apiApp.database.AiConclusion import AiConclusion +from apiApp.config import AUDIOFILES_PATH + +logger = logging.getLogger(__name__) +audio_management_router = APIRouter() + + +def query_audio_without_conclusion(db, limit=None): + """ + Возвращает запрос для поиска Audio без AiConclusion + + Использует exists() подзапрос, так как AiConclusion - это relationship + """ + from sqlalchemy import exists + + subquery = db.query(AiConclusion.audio_id).filter( + AiConclusion.audio_id == Audio.id + ) + + query = db.query(Audio).filter( + ~exists().where(subquery.exists()) + ).order_by(Audio.index_date.asc()) + + if limit: + query = query.limit(limit) + + return query + +class AudioRegisterRequest(BaseModel): + """Запрос на регистрацию аудиофайла""" + filename: str + file_path: str # Полный путь к файлу в общей папке audiofiles + + +class AudioProcessAllRequest(BaseModel): + """Запрос на пакетное распознавание""" + limit: int = 100 + + +class AudioRegisterResponse(BaseModel): + """Ответ на регистрацию аудиофайла""" + id: str + filename: str + file_size: int + created_at: datetime + + +@audio_management_router.post("/audio/register", response_model=AudioRegisterResponse, status_code=201) +async def register_audio_file( + request: AudioRegisterRequest, + db: Session = Depends(get_db) +): + """ + Регистрация аудиофайла в БД (без копирования файла) + + Создаёт запись в таблице Audio для файла, который уже находится + в общей папке audiofiles. НЕ копирует файл, только создаёт запись в БД. + + Args: + request: {filename: "in-xxx.wav", file_path: "/app/audiofiles/in-xxx.wav"} + + Returns: + 201 Created + информация о созданной записи + 400 Bad Request если файл уже зарегистрирован + 404 Not Found если файл не существует на диске + """ + try: filename = request.filename file_path = request.file_path @@ -111,24 +192,13 @@ async def process_all_pending_audio( } """ try: - from sqlalchemy import exists - limit = request.limit logger.info(f"🚀 Поиск Audio без AiConclusion (limit={limit})") - # Находим все Audio без AiConclusion через подзапрос - subquery = db.query(AiConclusion.audio_id).filter( - AiConclusion.audio_id == Audio.id - ) - - pending_audio = db.query(Audio).filter( - ~exists().where(subquery.exists()) - ).order_by(Audio.index_date.asc()).limit(limit).all() - - total_pending = db.query(Audio).filter( - ~exists().where(subquery.exists()) - ).count() + # Находим все Audio без AiConclusion используя вспомогательную функцию + pending_audio = query_audio_without_conclusion(db, limit).all() + total_pending = query_audio_without_conclusion(db).count() if not pending_audio: logger.info("ℹ️ Нет файлов для распознавания") @@ -211,15 +281,7 @@ async def get_pending_audio( Список файлов, ожидающих распознавания """ try: - from sqlalchemy import exists - - subquery = db.query(AiConclusion.audio_id).filter( - AiConclusion.audio_id == Audio.id - ) - - pending_audio = db.query(Audio).filter( - ~exists().where(subquery.exists()) - ).order_by(Audio.index_date.asc()).limit(limit).all() + pending_audio = query_audio_without_conclusion(db, limit).all() files_info = [] for audio in pending_audio: @@ -234,9 +296,7 @@ async def get_pending_audio( "exists_on_disk": exists }) - total_pending = db.query(Audio).filter( - ~exists().where(subquery.exists()) - ).count() + total_pending = query_audio_without_conclusion(db).count() return { "total_pending": total_pending, @@ -261,19 +321,9 @@ async def get_audio_stats(db: Session = Depends(get_db)): Статистика по Audio записям """ try: - from sqlalchemy import exists - total_audio = db.query(Audio).count() - - subquery = db.query(AiConclusion.audio_id).filter( - AiConclusion.audio_id == Audio.id - ) - - with_conclusion = db.query(Audio).filter( - exists().where(subquery.exists()) - ).count() - - without_conclusion = total_audio - with_conclusion + with_conclusion = total_audio - query_audio_without_conclusion(db).count() + without_conclusion = query_audio_without_conclusion(db).count() # Проверяем существование файлов на диске all_audio = db.query(Audio).all() @@ -299,62 +349,3 @@ async def get_audio_stats(db: Session = Depends(get_db)): ) -def auto_restore_on_startup(db: Session, limit: int = 100): - """ - Автоматическое восстановление распознавания при старте FileAudioAPI - - Проверяет, есть ли файлы без AiConclusion, и запускает их распознавание - - Args: - db: Сессия БД - limit: Максимум файлов для восстановления - """ - try: - from sqlalchemy import or_ - - # Проверяем, есть ли файлы без AiConclusion - pending_audio = db.query(Audio).filter( - or_( - Audio.AiConclusion == None, - Audio.AiConclusion == '' - ) - ).limit(limit).all() - - if not pending_audio: - logger.info("ℹ️ Auto-restore: нет файлов для распознавания") - return - - logger.info(f"🔄 Auto-restore: найдено {len(pending_audio)} файлов без AiConclusion") - - # Запускаем распознавание - started_count = 0 - for audio in pending_audio: - file_path = os.path.join(AUDIOFILES_PATH, audio.filename) - - if not os.path.exists(file_path): - logger.warning(f"⚠️ Файл не найден: {audio.filename}") - continue - - # Отправляем в GigaAM API - from apiApp.config import GIGAAM_API_URL - api_url = f"{GIGAAM_API_URL}/api/call/process" - - payload = {"filename": audio.filename} - - try: - import requests - response = requests.post(api_url, json=payload, timeout=5) - - if response.status_code in [200, 202]: - logger.info(f"✅ Запущено распознавание: {audio.filename}") - started_count += 1 - else: - logger.warning(f"⚠️ Ошибка запуска {audio.filename}: {response.status_code}") - - except Exception as e: - logger.error(f"❌ Ошибка при запуске {audio.filename}: {e}") - - logger.info(f"🎉 Auto-restore завершено: запущено {started_count} файлов") - - except Exception as e: - logger.error(f"❌ Ошибка при auto-restore: {e}")