"""Celery задача форматирования библиографии по ГОСТ.""" import logging from typing import Any from celery.utils.log import get_task_logger from sqlalchemy import select from app.celery_app import celery_app from app.db import db_session from app.formatters.gost_7_0_5 import GOST705Formatter from app.formatters.gost_7_1 import GOST71Formatter, _get_sort_key logger = get_task_logger(__name__) @celery_app.task( name="gost.format_bibliography", bind=True, max_retries=3, default_retry_delay=30, ) def format_bibliography( self, task_id: str, doc_ids: list[int], style: str = "7.1", ) -> dict[str, Any]: """ Форматировать список литературы по ГОСТ для переданных doc_ids. Args: task_id: ID задачи в PostgreSQL doc_ids: Список ID документов из PostgreSQL style: Стиль форматирования — "7.1" (полная) или "7.0.5" (краткая) Returns: dict с отформатированной библиографией """ from app.models import Document, Task logger.info( f"Форматирование библиографии для задачи {task_id!r}: " f"{len(doc_ids)} документов, стиль ГОСТ {style}" ) try: with db_session() as session: # Обновить статус task = session.get(Task, task_id) if task: task.status = "processing" session.commit() # Получить документы docs = session.execute( select(Document).where(Document.id.in_(doc_ids)) ).scalars().all() if not docs: raise ValueError(f"Документы не найдены: {doc_ids}") # Выбрать форматтер formatter = GOST71Formatter() if style == "7.1" else GOST705Formatter() # Преобразовать ORM объекты в словари для форматирования docs_dicts = [] for doc in docs: docs_dicts.append({ "id": doc.id, "title": doc.title, "authors": doc.authors or [], "year": doc.year, "journal": doc.journal, "volume": doc.volume, "issue": doc.issue, "pages": doc.pages, "doi": doc.doi, "url": doc.url, "lang": doc.lang or "ru", "source": doc.source, }) # Сортировать: кириллица (рус. авторы) → латиница (иностр.) sorted_docs = sorted(docs_dicts, key=lambda d: _get_sort_key(d)) bibliography = [] for i, doc_dict in enumerate(sorted_docs, 1): if style == "7.1": citation = formatter.format_full(doc_dict) else: citation = formatter.format_short(doc_dict) bibliography.append({ "number": i, "citation": citation, "doc_id": doc_dict["id"], }) result = { "bibliography": bibliography, "style": style, "total": len(bibliography), } # Сохранить результат task = session.get(Task, task_id) if task: task.result = result task.status = "done" task.queue_position = None session.commit() logger.info( f"Библиография сформирована для задачи {task_id!r}: " f"{len(bibliography)} записей по ГОСТ {style}" ) # Уведомить пользователя celery_app.send_task( "notify.send_task_done", args=[task_id], queue="queue.notify", ) return result except Exception as exc: logger.error(f"Ошибка при форматировании библиографии для задачи {task_id!r}: {exc}", exc_info=True) try: from app.models import Task with db_session() as session: task = session.get(Task, task_id) if task: task.status = "failed" task.error = str(exc) session.commit() except Exception as db_exc: logger.error(f"Не удалось обновить статус задачи: {db_exc}") raise self.retry(exc=exc, countdown=30)