mirror of
https://github.com/flibusta-apps/book_library_server.git
synced 2025-12-06 15:15:36 +01:00
Add translator endpoints
This commit is contained in:
@@ -90,6 +90,20 @@ def upgrade():
|
|||||||
unique=False,
|
unique=False,
|
||||||
postgresql_using="btree",
|
postgresql_using="btree",
|
||||||
)
|
)
|
||||||
|
op.create_index(
|
||||||
|
op.f("translations_book"),
|
||||||
|
"translations",
|
||||||
|
["book"],
|
||||||
|
unique=False,
|
||||||
|
postgresql_using="btree",
|
||||||
|
)
|
||||||
|
op.create_index(
|
||||||
|
op.f("translations_author"),
|
||||||
|
"translations",
|
||||||
|
["author"],
|
||||||
|
unique=False,
|
||||||
|
postgresql_using="btree",
|
||||||
|
)
|
||||||
# ### end Alembic commands ###
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
@@ -108,4 +122,6 @@ def downgrade():
|
|||||||
op.drop_index(op.f("book_authors_author"), table_name="book_authors")
|
op.drop_index(op.f("book_authors_author"), table_name="book_authors")
|
||||||
op.drop_index(op.f("book_sequences_book"), table_name="book_sequences")
|
op.drop_index(op.f("book_sequences_book"), table_name="book_sequences")
|
||||||
op.drop_index(op.f("book_sequences_sequence"), table_name="book_sequences")
|
op.drop_index(op.f("book_sequences_sequence"), table_name="book_sequences")
|
||||||
|
op.drop_index(op.f("translations_book"), table_name="translations")
|
||||||
|
op.drop_index(op.f("translations_author"), table_name="translations")
|
||||||
# ### end Alembic commands ###
|
# ### end Alembic commands ###
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ class TRGMSearchService(Generic[T]):
|
|||||||
SELECT_RELATED: Optional[Union[list[str], str]] = None
|
SELECT_RELATED: Optional[Union[list[str], str]] = None
|
||||||
PREFETCH_RELATED: Optional[Union[list[str], str]] = None
|
PREFETCH_RELATED: Optional[Union[list[str], str]] = None
|
||||||
GET_OBJECT_IDS_QUERY: Optional[str] = None
|
GET_OBJECT_IDS_QUERY: Optional[str] = None
|
||||||
|
CUSTOM_CACHE_PREFIX: Optional[str] = None
|
||||||
CACHE_TTL = 60 * 60
|
CACHE_TTL = 60 * 60
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -66,9 +67,14 @@ class TRGMSearchService(Generic[T]):
|
|||||||
|
|
||||||
return row["array"]
|
return row["array"]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@property
|
||||||
|
def cache_prefix(cls) -> str:
|
||||||
|
return cls.CUSTOM_CACHE_PREFIX or cls.model.__class__.__name__
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_cache_key(cls, query_data: str, allowed_langs: list[str]) -> str:
|
def get_cache_key(cls, query_data: str, allowed_langs: list[str]) -> str:
|
||||||
model_class_name = cls.model.__class__.__name__
|
model_class_name = cls.cache_prefix
|
||||||
allowed_langs_part = ",".join(allowed_langs)
|
allowed_langs_part = ",".join(allowed_langs)
|
||||||
return f"{model_class_name}_{query_data}_{allowed_langs_part}"
|
return f"{model_class_name}_{query_data}_{allowed_langs_part}"
|
||||||
|
|
||||||
|
|||||||
52
fastapi_book_server/app/services/translator.py
Normal file
52
fastapi_book_server/app/services/translator.py
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
from app.models import Author
|
||||||
|
from app.services.common import TRGMSearchService
|
||||||
|
|
||||||
|
|
||||||
|
GET_OBJECT_IDS_QUERY = """
|
||||||
|
SELECT ARRAY(
|
||||||
|
WITH filtered_authors AS (
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
GREATEST(
|
||||||
|
similarity(
|
||||||
|
(last_name || ' ' || first_name || ' ' || middle_name),
|
||||||
|
:query
|
||||||
|
),
|
||||||
|
similarity((last_name || ' ' || first_name), :query),
|
||||||
|
similarity((last_name), :query)
|
||||||
|
) as sml,
|
||||||
|
(
|
||||||
|
SELECT count(*) FROM translations
|
||||||
|
LEFT JOIN books
|
||||||
|
ON (books.id = book AND
|
||||||
|
books.is_deleted = 'f' AND
|
||||||
|
books.lang = ANY(:langs ::text[]))
|
||||||
|
WHERE author = authors.id
|
||||||
|
) as books_count
|
||||||
|
FROM authors
|
||||||
|
WHERE (
|
||||||
|
(last_name || ' ' || first_name || ' ' || middle_name) % :query OR
|
||||||
|
(last_name || ' ' || first_name) % :query OR
|
||||||
|
(last_name) % :query
|
||||||
|
) AND
|
||||||
|
EXISTS (
|
||||||
|
SELECT * FROM translations
|
||||||
|
LEFT JOIN books
|
||||||
|
ON (books.id = book AND
|
||||||
|
books.is_deleted = 'f' AND
|
||||||
|
books.lang = ANY(:langs ::text[]))
|
||||||
|
WHERE author = authors.id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
SELECT fauthors.id FROM filtered_authors as fauthors
|
||||||
|
ORDER BY fauthors.sml DESC, fauthors.books_count DESC
|
||||||
|
LIMIT 210
|
||||||
|
);
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class TranslatorTGRMSearchService(TRGMSearchService):
|
||||||
|
MODEL_CLASS = Author
|
||||||
|
CUSTOM_CACHE_PREFIX = "translator"
|
||||||
|
PREFETCH_RELATED = ["source", "annotations"]
|
||||||
|
GET_OBJECT_IDS_QUERY = GET_OBJECT_IDS_QUERY
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
from app.views.author import author_router
|
from app.views.author import author_router, translator_router
|
||||||
from app.views.author_annotation import author_annotation_router
|
from app.views.author_annotation import author_annotation_router
|
||||||
from app.views.book import book_router
|
from app.views.book import book_router
|
||||||
from app.views.book_annotation import book_annotation_router
|
from app.views.book_annotation import book_annotation_router
|
||||||
@@ -10,6 +10,7 @@ from app.views.translation import translation_router
|
|||||||
routers = [
|
routers = [
|
||||||
source_router,
|
source_router,
|
||||||
author_router,
|
author_router,
|
||||||
|
translator_router,
|
||||||
author_annotation_router,
|
author_annotation_router,
|
||||||
book_router,
|
book_router,
|
||||||
book_annotation_router,
|
book_annotation_router,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ from app.serializers.author import (
|
|||||||
)
|
)
|
||||||
from app.serializers.author_annotation import AuthorAnnotation
|
from app.serializers.author_annotation import AuthorAnnotation
|
||||||
from app.services.author import AuthorTGRMSearchService, GetRandomAuthorService
|
from app.services.author import AuthorTGRMSearchService, GetRandomAuthorService
|
||||||
|
from app.services.translator import TranslatorTGRMSearchService
|
||||||
from app.utils.pagination import CustomPage
|
from app.utils.pagination import CustomPage
|
||||||
|
|
||||||
|
|
||||||
@@ -97,19 +98,6 @@ async def get_author_books(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@author_router.get("/{id}/translated_books", response_model=CustomPage[TranslatedBook])
|
|
||||||
async def get_translated_books(
|
|
||||||
id: int, allowed_langs: list[str] = Depends(get_allowed_langs)
|
|
||||||
):
|
|
||||||
return await paginate(
|
|
||||||
BookDB.objects.select_related(["source", "annotations", "translators"]).filter(
|
|
||||||
translations__translator__id=id,
|
|
||||||
lang__in=allowed_langs,
|
|
||||||
is_deleted=False,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@author_router.get(
|
@author_router.get(
|
||||||
"/search/{query}", response_model=CustomPage[Author], dependencies=[Depends(Params)]
|
"/search/{query}", response_model=CustomPage[Author], dependencies=[Depends(Params)]
|
||||||
)
|
)
|
||||||
@@ -119,3 +107,34 @@ async def search_authors(
|
|||||||
return await AuthorTGRMSearchService.get(
|
return await AuthorTGRMSearchService.get(
|
||||||
query, request.app.state.redis, allowed_langs
|
query, request.app.state.redis, allowed_langs
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
translator_router = APIRouter(
|
||||||
|
prefix="/api/v1/translators",
|
||||||
|
tags=["author"],
|
||||||
|
dependencies=[Depends(check_token)],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@translator_router.get("/{id}/books", response_model=CustomPage[TranslatedBook])
|
||||||
|
async def get_translated_books(
|
||||||
|
id: int, allowed_langs: list[str] = Depends(get_allowed_langs)
|
||||||
|
):
|
||||||
|
return await paginate(
|
||||||
|
BookDB.objects.select_related(["source", "annotations", "authors"]).filter(
|
||||||
|
translators__id=id,
|
||||||
|
lang__in=allowed_langs,
|
||||||
|
is_deleted=False,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@translator_router.get(
|
||||||
|
"/search/{query}", response_model=CustomPage[Author], dependencies=[Depends(Params)]
|
||||||
|
)
|
||||||
|
async def search_translators(
|
||||||
|
query: str, request: Request, allowed_langs: list[str] = Depends(get_allowed_langs)
|
||||||
|
):
|
||||||
|
return await TranslatorTGRMSearchService.get(
|
||||||
|
query, request.app.state.redis, allowed_langs
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user