feat: initial microservices project structure
Services: - api: FastAPI gateway with JWT auth, async endpoints, WebSocket - worker-gpu: CUDA sentence-transformers, FAISS IVFFlat, Ollama LLM - worker-indexer: Winnowing+MinHash plagiarism detection, PDF/DOCX extraction - worker-notifier: SMTP email notifications - worker-gost: GOST 7.1-2003 and GOST R 7.0.5-2008 formatting Infrastructure: - docker-compose.yml (production) + docker-compose.dev.yml (hot reload) - Nginx reverse proxy + WebSocket support - PostgreSQL 16 with Alembic migrations - Elasticsearch 8 with Russian/English analyzers - MinIO, RabbitMQ, Redis, Ollama Frontend: - React 18 + Vite + TypeScript + TailwindCSS + Zustand + React Query v5 - 9 pages: Home, Search, Cabinet, Task, Check, Bibliography, Pricing, Login, Register Scripts: - Parser stubs: OpenAlex, КиберЛенинка, arXiv (Phase 0 - to be filled) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
0
services/worker-gost/app/tasks/__init__.py
Normal file
0
services/worker-gost/app/tasks/__init__.py
Normal file
142
services/worker-gost/app/tasks/gost.py
Normal file
142
services/worker-gost/app/tasks/gost.py
Normal file
@@ -0,0 +1,142 @@
|
||||
"""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)
|
||||
Reference in New Issue
Block a user