mirror of
https://github.com/flibusta-apps/book_library_server.git
synced 2025-12-06 15:15:36 +01:00
Fix
This commit is contained in:
@@ -2,18 +2,17 @@ exclude: 'docs|node_modules|migrations|.git|.tox'
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/ambv/black
|
||||
rev: 22.12.0
|
||||
rev: 23.3.0
|
||||
hooks:
|
||||
- id: black
|
||||
language_version: python3.11
|
||||
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: 'v0.0.216'
|
||||
rev: 'v0.0.265'
|
||||
hooks:
|
||||
- id: ruff
|
||||
args: ["--force-exclude"]
|
||||
|
||||
- repo: https://github.com/crate-ci/typos
|
||||
rev: v1.13.6
|
||||
rev: typos-dict-v0.9.26
|
||||
hooks:
|
||||
- id: typos
|
||||
|
||||
@@ -3,6 +3,7 @@ from typing import TypedDict
|
||||
from app.models import Author
|
||||
from app.services.common import GetRandomService, MeiliSearchService, TRGMSearchService
|
||||
|
||||
|
||||
GET_OBJECT_IDS_QUERY = """
|
||||
SELECT ARRAY(
|
||||
WITH filtered_authors AS (
|
||||
|
||||
@@ -8,6 +8,7 @@ from app.services.common import (
|
||||
TRGMSearchService,
|
||||
)
|
||||
|
||||
|
||||
GET_OBJECT_IDS_QUERY = """
|
||||
SELECT ARRAY(
|
||||
WITH filtered_books AS (
|
||||
|
||||
@@ -15,9 +15,10 @@ from redis import asyncio as aioredis
|
||||
from sqlalchemy import Table
|
||||
|
||||
from app.utils.orjson_default import default as orjson_default
|
||||
from app.utils.pagination import CustomPage, Page
|
||||
from app.utils.pagination import Page
|
||||
from core.config import env_config
|
||||
|
||||
|
||||
MODEL = TypeVar("MODEL", bound=Model)
|
||||
QUERY = TypeVar("QUERY", bound=TypedDict)
|
||||
|
||||
@@ -170,7 +171,7 @@ class BaseSearchService(Generic[MODEL, QUERY], BaseService[MODEL, QUERY]):
|
||||
|
||||
total, objects = await cls.get_limited_objects(query, redis, no_cache)
|
||||
|
||||
return CustomPage.create(items=objects, total=total, params=params)
|
||||
return Page.create(items=objects, total=total, params=params)
|
||||
|
||||
|
||||
class SearchQuery(TypedDict):
|
||||
|
||||
@@ -3,6 +3,7 @@ from typing import TypedDict
|
||||
from app.models import Sequence
|
||||
from app.services.common import GetRandomService, MeiliSearchService, TRGMSearchService
|
||||
|
||||
|
||||
GET_OBJECT_IDS_QUERY = """
|
||||
SELECT ARRAY (
|
||||
WITH filtered_sequences AS (
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from app.models import Author
|
||||
from app.services.common import MeiliSearchService, TRGMSearchService
|
||||
|
||||
|
||||
GET_OBJECT_IDS_QUERY = """
|
||||
SELECT ARRAY(
|
||||
WITH filtered_authors AS (
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
from typing import Any
|
||||
|
||||
import orjson
|
||||
|
||||
|
||||
def default(value: Any):
|
||||
if isinstance(value, frozenset):
|
||||
@@ -7,3 +9,7 @@ def default(value: Any):
|
||||
return "-".join(sorted(list_value))
|
||||
|
||||
return value
|
||||
|
||||
|
||||
def orjson_dumps(v, *, default) -> str:
|
||||
return orjson.dumps(v, default=default).decode()
|
||||
|
||||
@@ -1,8 +1,18 @@
|
||||
from typing import Any, Generic, Protocol, Sequence, TypeVar, runtime_checkable
|
||||
from math import ceil
|
||||
from typing import (
|
||||
Any,
|
||||
Generic,
|
||||
Protocol,
|
||||
Sequence,
|
||||
TypeVar,
|
||||
runtime_checkable,
|
||||
)
|
||||
|
||||
from fastapi_pagination import Page, Params
|
||||
from fastapi_pagination.bases import AbstractParams
|
||||
from pydantic import conint
|
||||
from fastapi_pagination import Params
|
||||
from fastapi_pagination.bases import AbstractParams, BasePage
|
||||
from fastapi_pagination.types import GreaterEqualOne, GreaterEqualZero
|
||||
import orjson
|
||||
from utils.orjson_default import orjson_dumps
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
@@ -14,23 +24,36 @@ class ToDict(Protocol):
|
||||
T = TypeVar("T", ToDict, Any)
|
||||
|
||||
|
||||
class CustomPage(Page[T], Generic[T]):
|
||||
total_pages: conint(ge=0) # type: ignore
|
||||
class Page(BasePage[T], Generic[T]):
|
||||
page: GreaterEqualOne
|
||||
size: GreaterEqualOne
|
||||
total_pages: GreaterEqualZero
|
||||
|
||||
__params_type__ = Params
|
||||
|
||||
class Config:
|
||||
json_loads = orjson.loads
|
||||
json_dumps = orjson_dumps
|
||||
|
||||
@classmethod
|
||||
def create(
|
||||
cls,
|
||||
items: Sequence[T],
|
||||
total: int,
|
||||
params: AbstractParams,
|
||||
) -> Page[T]:
|
||||
*,
|
||||
total: int,
|
||||
**kwargs: Any,
|
||||
) -> "Page[T]":
|
||||
if not isinstance(params, Params):
|
||||
raise ValueError("Page should be used with Params")
|
||||
|
||||
pages = ceil(total / params.size)
|
||||
|
||||
return cls(
|
||||
total=total,
|
||||
items=[item.dict() for item in items],
|
||||
items=items,
|
||||
page=params.page,
|
||||
size=params.size,
|
||||
total_pages=(total + params.size - 1) // params.size,
|
||||
total_pages=pages,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
@@ -8,6 +8,7 @@ from app.views.sequence import sequence_router
|
||||
from app.views.source import source_router
|
||||
from app.views.translation import translation_router
|
||||
|
||||
|
||||
routers = [
|
||||
source_router,
|
||||
author_router,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||
|
||||
from fastapi_pagination import Params
|
||||
from fastapi_pagination.ext.ormar import paginate
|
||||
|
||||
@@ -10,7 +11,8 @@ from app.serializers.author import Author, AuthorBook, TranslatedBook
|
||||
from app.serializers.author_annotation import AuthorAnnotation
|
||||
from app.services.author import AuthorMeiliSearchService, GetRandomAuthorService
|
||||
from app.services.translator import TranslatorMeiliSearchService
|
||||
from app.utils.pagination import CustomPage
|
||||
from app.utils.pagination import Page
|
||||
|
||||
|
||||
author_router = APIRouter(
|
||||
prefix="/api/v1/authors",
|
||||
@@ -23,9 +25,7 @@ PREFETCH_RELATED_FIELDS = ["source"]
|
||||
SELECT_RELATED_FIELDS = ["annotations"]
|
||||
|
||||
|
||||
@author_router.get(
|
||||
"/", response_model=CustomPage[Author], dependencies=[Depends(Params)]
|
||||
)
|
||||
@author_router.get("/", response_model=Page[Author], dependencies=[Depends(Params)])
|
||||
async def get_authors():
|
||||
return await paginate(
|
||||
AuthorDB.objects.select_related(SELECT_RELATED_FIELDS).prefetch_related(
|
||||
@@ -75,7 +75,7 @@ async def get_author_annotation(id: int):
|
||||
|
||||
|
||||
@author_router.get(
|
||||
"/{id}/books", response_model=CustomPage[AuthorBook], dependencies=[Depends(Params)]
|
||||
"/{id}/books", response_model=Page[AuthorBook], dependencies=[Depends(Params)]
|
||||
)
|
||||
async def get_author_books(
|
||||
id: int, allowed_langs: list[str] = Depends(get_allowed_langs)
|
||||
@@ -89,7 +89,7 @@ async def get_author_books(
|
||||
|
||||
|
||||
@author_router.get(
|
||||
"/search/{query}", response_model=CustomPage[Author], dependencies=[Depends(Params)]
|
||||
"/search/{query}", response_model=Page[Author], dependencies=[Depends(Params)]
|
||||
)
|
||||
async def search_authors(
|
||||
query: str,
|
||||
@@ -109,7 +109,7 @@ translator_router = APIRouter(
|
||||
)
|
||||
|
||||
|
||||
@translator_router.get("/{id}/books", response_model=CustomPage[TranslatedBook])
|
||||
@translator_router.get("/{id}/books", response_model=Page[TranslatedBook])
|
||||
async def get_translated_books(
|
||||
id: int, allowed_langs: list[str] = Depends(get_allowed_langs)
|
||||
):
|
||||
@@ -125,7 +125,7 @@ async def get_translated_books(
|
||||
|
||||
|
||||
@translator_router.get(
|
||||
"/search/{query}", response_model=CustomPage[Author], dependencies=[Depends(Params)]
|
||||
"/search/{query}", response_model=Page[Author], dependencies=[Depends(Params)]
|
||||
)
|
||||
async def search_translators(
|
||||
query: str,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
|
||||
from fastapi_pagination import Page, Params
|
||||
from fastapi_pagination.ext.ormar import paginate
|
||||
|
||||
@@ -6,6 +7,7 @@ from app.depends import check_token
|
||||
from app.models import AuthorAnnotation as AuthorAnnotationDB
|
||||
from app.serializers.author_annotation import AuthorAnnotation
|
||||
|
||||
|
||||
author_annotation_router = APIRouter(
|
||||
prefix="/api/v1/author_annotations",
|
||||
tags=["author_annotation"],
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||
|
||||
from fastapi_pagination import Params
|
||||
|
||||
from app.depends import check_token, get_allowed_langs
|
||||
@@ -15,7 +16,8 @@ from app.services.book import (
|
||||
BookMeiliSearchService,
|
||||
GetRandomBookService,
|
||||
)
|
||||
from app.utils.pagination import CustomPage
|
||||
from app.utils.pagination import Page
|
||||
|
||||
|
||||
book_router = APIRouter(
|
||||
prefix="/api/v1/books",
|
||||
@@ -29,9 +31,7 @@ SELECT_RELATED_FIELDS = ["authors", "translators", "annotations"]
|
||||
DETAIL_SELECT_RELATED_FIELDS = ["sequences", "genres"]
|
||||
|
||||
|
||||
@book_router.get(
|
||||
"/", response_model=CustomPage[RemoteBook], dependencies=[Depends(Params)]
|
||||
)
|
||||
@book_router.get("/", response_model=Page[RemoteBook], dependencies=[Depends(Params)])
|
||||
async def get_books(
|
||||
request: Request,
|
||||
book_filter: dict = Depends(get_book_filter),
|
||||
@@ -40,7 +40,7 @@ async def get_books(
|
||||
|
||||
|
||||
@book_router.get(
|
||||
"/base/", response_model=CustomPage[BookBaseInfo], dependencies=[Depends(Params)]
|
||||
"/base/", response_model=Page[BookBaseInfo], dependencies=[Depends(Params)]
|
||||
)
|
||||
async def get_base_books_info(
|
||||
request: Request, book_filter: dict = Depends(get_book_filter)
|
||||
@@ -116,7 +116,7 @@ async def get_book_annotation(id: int):
|
||||
|
||||
|
||||
@book_router.get(
|
||||
"/search/{query}", response_model=CustomPage[Book], dependencies=[Depends(Params)]
|
||||
"/search/{query}", response_model=Page[Book], dependencies=[Depends(Params)]
|
||||
)
|
||||
async def search_books(
|
||||
query: str,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
|
||||
from fastapi_pagination import Page, Params
|
||||
from fastapi_pagination.ext.ormar import paginate
|
||||
|
||||
@@ -6,6 +7,7 @@ from app.depends import check_token
|
||||
from app.models import BookAnnotation as BookAnnotationDB
|
||||
from app.serializers.book_annotation import BookAnnotation
|
||||
|
||||
|
||||
book_annotation_router = APIRouter(
|
||||
prefix="/api/v1/book_annotations",
|
||||
tags=["book_annotation"],
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||
|
||||
from fastapi_pagination import Params
|
||||
from fastapi_pagination.ext.ormar import paginate
|
||||
|
||||
@@ -7,7 +8,8 @@ from app.filters.genre import get_genre_filter
|
||||
from app.models import Genre as GenreDB
|
||||
from app.serializers.genre import Genre
|
||||
from app.services.genre import GenreMeiliSearchService
|
||||
from app.utils.pagination import CustomPage
|
||||
from app.utils.pagination import Page
|
||||
|
||||
|
||||
genre_router = APIRouter(
|
||||
prefix="/api/v1/genres", tags=["genres"], dependencies=[Depends(check_token)]
|
||||
@@ -17,7 +19,7 @@ genre_router = APIRouter(
|
||||
PREFETCH_RELATED_FIELDS = ["source"]
|
||||
|
||||
|
||||
@genre_router.get("/", response_model=CustomPage[Genre], dependencies=[Depends(Params)])
|
||||
@genre_router.get("/", response_model=Page[Genre], dependencies=[Depends(Params)])
|
||||
async def get_genres(genre_filter: dict = Depends(get_genre_filter)):
|
||||
return await paginate(
|
||||
GenreDB.objects.prefetch_related(PREFETCH_RELATED_FIELDS)
|
||||
@@ -46,7 +48,7 @@ async def get_genre(id: int):
|
||||
|
||||
|
||||
@genre_router.get(
|
||||
"/search/{query}", response_model=CustomPage[Genre], dependencies=[Depends(Params)]
|
||||
"/search/{query}", response_model=Page[Genre], dependencies=[Depends(Params)]
|
||||
)
|
||||
async def search_genres(
|
||||
query: str,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
|
||||
healtcheck_router = APIRouter(tags=["healthcheck"])
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from fastapi import APIRouter, Depends, Request
|
||||
|
||||
from fastapi_pagination import Params
|
||||
from fastapi_pagination.ext.ormar import paginate
|
||||
|
||||
@@ -8,7 +9,8 @@ from app.models import Sequence as SequenceDB
|
||||
from app.serializers.sequence import Book as SequenceBook
|
||||
from app.serializers.sequence import Sequence
|
||||
from app.services.sequence import GetRandomSequenceService, SequenceMeiliSearchService
|
||||
from app.utils.pagination import CustomPage
|
||||
from app.utils.pagination import Page
|
||||
|
||||
|
||||
sequence_router = APIRouter(
|
||||
prefix="/api/v1/sequences",
|
||||
@@ -17,9 +19,7 @@ sequence_router = APIRouter(
|
||||
)
|
||||
|
||||
|
||||
@sequence_router.get(
|
||||
"/", response_model=CustomPage[Sequence], dependencies=[Depends(Params)]
|
||||
)
|
||||
@sequence_router.get("/", response_model=Page[Sequence], dependencies=[Depends(Params)])
|
||||
async def get_sequences():
|
||||
return await paginate(SequenceDB.objects)
|
||||
|
||||
@@ -44,7 +44,7 @@ async def get_sequence(id: int):
|
||||
|
||||
@sequence_router.get(
|
||||
"/{id}/books",
|
||||
response_model=CustomPage[SequenceBook],
|
||||
response_model=Page[SequenceBook],
|
||||
dependencies=[Depends(Params)],
|
||||
)
|
||||
async def get_sequence_books(
|
||||
@@ -60,7 +60,7 @@ async def get_sequence_books(
|
||||
|
||||
@sequence_router.get(
|
||||
"/search/{query}",
|
||||
response_model=CustomPage[Sequence],
|
||||
response_model=Page[Sequence],
|
||||
dependencies=[Depends(Params)],
|
||||
)
|
||||
async def search_sequences(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from fastapi import APIRouter, Depends
|
||||
|
||||
from fastapi_pagination import Page, Params
|
||||
from fastapi_pagination.ext.ormar import paginate
|
||||
|
||||
@@ -6,6 +7,7 @@ from app.depends import check_token
|
||||
from app.models import Source as SourceDB
|
||||
from app.serializers.source import Source
|
||||
|
||||
|
||||
source_router = APIRouter(
|
||||
prefix="/api/v1/sources",
|
||||
tags=["source"],
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
from fastapi import APIRouter, Depends
|
||||
|
||||
from fastapi_pagination import Params
|
||||
from fastapi_pagination.ext.ormar import paginate
|
||||
|
||||
from app.depends import check_token
|
||||
from app.models import Translation as TranslationDB
|
||||
from app.serializers.translation import Translation
|
||||
from app.utils.pagination import CustomPage
|
||||
from app.utils.pagination import Page
|
||||
|
||||
|
||||
translation_router = APIRouter(
|
||||
prefix="/api/v1/translation",
|
||||
@@ -15,7 +17,7 @@ translation_router = APIRouter(
|
||||
|
||||
|
||||
@translation_router.get(
|
||||
"/", response_model=CustomPage[Translation], dependencies=[Depends(Params)]
|
||||
"/", response_model=Page[Translation], dependencies=[Depends(Params)]
|
||||
)
|
||||
async def get_translations():
|
||||
return await paginate(TranslationDB.objects.select_related(["book", "author"]))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.responses import ORJSONResponse
|
||||
|
||||
from fastapi_pagination import add_pagination
|
||||
from prometheus_fastapi_instrumentator import Instrumentator
|
||||
from redis import asyncio as aioredis
|
||||
@@ -9,6 +10,7 @@ from app.views import routers
|
||||
from core.config import env_config
|
||||
from core.db import database
|
||||
|
||||
|
||||
sentry_sdk.init(
|
||||
env_config.SENTRY_SDN,
|
||||
)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from fastapi.security import APIKeyHeader
|
||||
|
||||
|
||||
default_security = APIKeyHeader(name="Authorization")
|
||||
|
||||
@@ -5,6 +5,7 @@ from sqlalchemy import MetaData
|
||||
|
||||
from core.config import env_config
|
||||
|
||||
|
||||
DATABASE_URL = (
|
||||
f"postgresql://{env_config.POSTGRES_USER}:{quote(env_config.POSTGRES_PASSWORD)}@"
|
||||
f"{env_config.POSTGRES_HOST}:{env_config.POSTGRES_PORT}/{env_config.POSTGRES_DB}"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from core.app import start_app
|
||||
|
||||
|
||||
app = start_app()
|
||||
|
||||
@@ -65,14 +65,13 @@ max-complexity = 15
|
||||
[tool.ruff.isort]
|
||||
known-first-party = ["core", "app"]
|
||||
force-sort-within-sections = true
|
||||
force-wrap-aliases = true
|
||||
section-order = ["future", "standard-library", "base_framework", "framework_ext", "third-party", "first-party", "local-folder"]
|
||||
lines-after-imports = 2
|
||||
|
||||
# only_sections = true
|
||||
# force_sort_within_sections = true
|
||||
# lines_after_imports = 2
|
||||
# lexicographical = true
|
||||
# sections = ["FUTURE", "STDLIB", "BASEFRAMEWORK", "FRAMEWORKEXT", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"]
|
||||
# known_baseframework = ["fastapi",]
|
||||
# known_frameworkext = ["starlette",]
|
||||
[tool.ruff.isort.sections]
|
||||
base_framework = ["fastapi",]
|
||||
framework_ext = ["starlette"]
|
||||
|
||||
[tool.ruff.pyupgrade]
|
||||
keep-runtime-typing = true
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import httpx
|
||||
|
||||
|
||||
response = httpx.get(
|
||||
"http://localhost:8080/healthcheck",
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user