Add linters configs

This commit is contained in:
2022-01-01 20:54:59 +03:00
parent 4a78d4f987
commit cbba30f2af
30 changed files with 580 additions and 298 deletions

35
.github/workflows/linters.yaml vendored Normal file
View File

@@ -0,0 +1,35 @@
name: Linters
on:
push:
branches:
- main
pull_request:
types: [opened, synchronize, reopened]
jobs:
Run-Pre-Commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 32
- uses: actions/setup-python@v2
with:
python-version: 3.9
- name: Install pre-commit
run: pip3 install pre-commit
- name: Pre-commit (Push)
env:
SETUPTOOLS_USE_DISTUTILS: stdlib
if: ${{ github.event_name == 'push' }}
run: pre-commit run --source ${{ github.event.before }} --origin ${{ github.event.after }} --show-diff-on-failure
- name: Pre-commit (Pull-Request)
env:
SETUPTOOLS_USE_DISTUTILS: stdlib
if: ${{ github.event_name == 'pull_request' }}
run: pre-commit run --source ${{ github.event.pull_request.base.sha }} --origin ${{ github.event.pull_request.head.sha }} --show-diff-on-failure

20
.pre-commit-config.yaml Normal file
View File

@@ -0,0 +1,20 @@
exclude: 'docs|node_modules|migrations|.git|.tox'
repos:
- repo: https://github.com/ambv/black
rev: 21.12b0
hooks:
- id: black
language_version: python3.9
- repo: https://github.com/pycqa/isort
rev: 5.10.1
hooks:
- id: isort
- repo: https://github.com/csachs/pyproject-flake8
rev: v0.0.1a2.post1
hooks:
- id: pyproject-flake8
additional_dependencies: [
'-e', 'git+https://github.com/pycqa/pyflakes@1911c20#egg=pyflakes',
'-e', 'git+https://github.com/pycqa/pycodestyle@d219c68#egg=pycodestyle',
]

View File

@@ -1 +1 @@
__version__ = '0.1.0'
__version__ = "0.1.0"

View File

@@ -1,20 +1,22 @@
from logging.config import fileConfig
import os
import sys
from alembic import context
import sys, os
from sqlalchemy.engine import create_engine
from core.db import DATABASE_URL
myPath = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, myPath + '/../../')
sys.path.insert(0, myPath + "/../../")
config = context.config
from app.models import BaseMeta
target_metadata = BaseMeta.metadata

View File

@@ -10,35 +10,77 @@ import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '08193b547a80'
down_revision = 'b44117a41998'
revision = "08193b547a80"
down_revision = "b44117a41998"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_index(op.f('ix_books_title'), 'books', ['title'], unique=False)
op.create_index(op.f('ix_sequences_name'), 'sequences', ['name'], unique=False)
op.create_index(op.f('tgrm_books_title'), 'books', ['title'], postgresql_using='gin', postgresql_ops={'description': 'gin_trgm_ops'})
op.create_index(op.f('tgrm_sequences_name'), 'sequences', ['name'], postgresql_using='gin', postgresql_ops={'description': 'gin_trgm_ops'})
op.create_index(op.f('tgrm_authors_lfm'), 'authors', [sa.text("(last_name || ' ' || first_name || ' ' || middle_name)")] ,postgresql_using='gin', postgresql_ops={'description': 'gin_trgm_ops'})
op.create_index(op.f('tgrm_authors_lf'), 'authors', [sa.text("(last_name || ' ' || first_name)")] ,postgresql_using='gin', postgresql_ops={'description': 'gin_trgm_ops'})
op.create_index(op.f('tgrm_authors_l'), 'authors', ['last_name'] ,postgresql_using='gin', postgresql_ops={'description': 'gin_trgm_ops'})
op.create_index(op.f('book_authors_book'), 'book_authors', ['book'], unique=False, postgresql_using='btree')
op.create_index(op.f('book_authors_author'), 'book_authors', ['author'], unique=False, postgresql_using='btree')
op.create_index(op.f("ix_books_title"), "books", ["title"], unique=False)
op.create_index(op.f("ix_sequences_name"), "sequences", ["name"], unique=False)
op.create_index(
op.f("tgrm_books_title"),
"books",
["title"],
postgresql_using="gin",
postgresql_ops={"description": "gin_trgm_ops"},
)
op.create_index(
op.f("tgrm_sequences_name"),
"sequences",
["name"],
postgresql_using="gin",
postgresql_ops={"description": "gin_trgm_ops"},
)
op.create_index(
op.f("tgrm_authors_lfm"),
"authors",
[sa.text("(last_name || ' ' || first_name || ' ' || middle_name)")],
postgresql_using="gin",
postgresql_ops={"description": "gin_trgm_ops"},
)
op.create_index(
op.f("tgrm_authors_lf"),
"authors",
[sa.text("(last_name || ' ' || first_name)")],
postgresql_using="gin",
postgresql_ops={"description": "gin_trgm_ops"},
)
op.create_index(
op.f("tgrm_authors_l"),
"authors",
["last_name"],
postgresql_using="gin",
postgresql_ops={"description": "gin_trgm_ops"},
)
op.create_index(
op.f("book_authors_book"),
"book_authors",
["book"],
unique=False,
postgresql_using="btree",
)
op.create_index(
op.f("book_authors_author"),
"book_authors",
["author"],
unique=False,
postgresql_using="btree",
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f('ix_sequences_name'), table_name='sequences')
op.drop_index(op.f('ix_books_title'), table_name='books')
op.drop_index(op.f('tgrm_books_title'), table_name='books')
op.drop_index(op.f('tgrm_sequences_name'), table_name='books')
op.drop_index(op.f('tgrm_authors_lfm'), table_name='books')
op.drop_index(op.f('tgrm_authors_lf'), table_name='books')
op.drop_index(op.f('tgrm_authors_l'), table_name='books')
op.drop_index(op.f('book_authors_book'), table_name='book_authors')
op.drop_index(op.f('book_authors_author'), table_name='book_authors')
op.drop_index(op.f("ix_sequences_name"), table_name="sequences")
op.drop_index(op.f("ix_books_title"), table_name="books")
op.drop_index(op.f("tgrm_books_title"), table_name="books")
op.drop_index(op.f("tgrm_sequences_name"), table_name="books")
op.drop_index(op.f("tgrm_authors_lfm"), table_name="books")
op.drop_index(op.f("tgrm_authors_lf"), table_name="books")
op.drop_index(op.f("tgrm_authors_l"), table_name="books")
op.drop_index(op.f("book_authors_book"), table_name="book_authors")
op.drop_index(op.f("book_authors_author"), table_name="book_authors")
# ### end Alembic commands ###

View File

@@ -11,7 +11,7 @@ from sqlalchemy.sql.schema import UniqueConstraint
# revision identifiers, used by Alembic.
revision = 'b44117a41998'
revision = "b44117a41998"
down_revision = None
branch_labels = None
depends_on = None
@@ -19,128 +19,203 @@ depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('sources',
sa.Column('id', sa.SmallInteger(), nullable=False),
sa.Column('name', sa.String(length=32), nullable=False),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('name')
op.create_table(
"sources",
sa.Column("id", sa.SmallInteger(), nullable=False),
sa.Column("name", sa.String(length=32), nullable=False),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("name"),
)
op.create_table('authors',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('source', sa.SmallInteger(), nullable=False),
sa.Column('remote_id', sa.Integer(), nullable=False),
sa.Column('first_name', sa.String(length=256), nullable=False),
sa.Column('last_name', sa.String(length=256), nullable=False),
sa.Column('middle_name', sa.String(length=256), nullable=True),
sa.ForeignKeyConstraint(['source'], ['sources.id'], name='fk_authors_sources_id_source'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('source', 'remote_id', name='uc_authors_source_remote_id')
op.create_table(
"authors",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("source", sa.SmallInteger(), nullable=False),
sa.Column("remote_id", sa.Integer(), nullable=False),
sa.Column("first_name", sa.String(length=256), nullable=False),
sa.Column("last_name", sa.String(length=256), nullable=False),
sa.Column("middle_name", sa.String(length=256), nullable=True),
sa.ForeignKeyConstraint(
["source"], ["sources.id"], name="fk_authors_sources_id_source"
),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("source", "remote_id", name="uc_authors_source_remote_id"),
)
op.create_table('books',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('source', sa.SmallInteger(), nullable=False),
sa.Column('remote_id', sa.Integer(), nullable=False),
sa.Column('title', sa.String(length=256), nullable=False),
sa.Column('lang', sa.String(length=3), nullable=False),
sa.Column('file_type', sa.String(length=4), nullable=False),
sa.Column('uploaded', sa.Date(), nullable=False),
sa.Column('is_deleted', sa.Boolean(), server_default=sa.text('false'), nullable=False),
sa.ForeignKeyConstraint(['source'], ['sources.id'], name='fk_books_sources_id_source'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('source', 'remote_id', name='uc_books_source_remote_id')
op.create_table(
"books",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("source", sa.SmallInteger(), nullable=False),
sa.Column("remote_id", sa.Integer(), nullable=False),
sa.Column("title", sa.String(length=256), nullable=False),
sa.Column("lang", sa.String(length=3), nullable=False),
sa.Column("file_type", sa.String(length=4), nullable=False),
sa.Column("uploaded", sa.Date(), nullable=False),
sa.Column(
"is_deleted", sa.Boolean(), server_default=sa.text("false"), nullable=False
),
sa.ForeignKeyConstraint(
["source"], ["sources.id"], name="fk_books_sources_id_source"
),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("source", "remote_id", name="uc_books_source_remote_id"),
)
op.create_table('genres',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('source', sa.SmallInteger(), nullable=False),
sa.Column('remote_id', sa.Integer(), nullable=False),
sa.Column('code', sa.String(length=45), nullable=False),
sa.Column('description', sa.String(length=99), nullable=False),
sa.Column('meta', sa.String(length=45), nullable=False),
sa.ForeignKeyConstraint(['source'], ['sources.id'], name='fk_genres_sources_id_source'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('source', 'remote_id', name='uc_genres_source_remote_id')
op.create_table(
"genres",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("source", sa.SmallInteger(), nullable=False),
sa.Column("remote_id", sa.Integer(), nullable=False),
sa.Column("code", sa.String(length=45), nullable=False),
sa.Column("description", sa.String(length=99), nullable=False),
sa.Column("meta", sa.String(length=45), nullable=False),
sa.ForeignKeyConstraint(
["source"], ["sources.id"], name="fk_genres_sources_id_source"
),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("source", "remote_id", name="uc_genres_source_remote_id"),
)
op.create_table('sequences',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('source', sa.SmallInteger(), nullable=False),
sa.Column('remote_id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(length=256), nullable=False),
sa.ForeignKeyConstraint(['source'], ['sources.id'], name='fk_sequences_sources_id_source'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('source', 'remote_id', name='uc_sequences_source_remote_id')
op.create_table(
"sequences",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("source", sa.SmallInteger(), nullable=False),
sa.Column("remote_id", sa.Integer(), nullable=False),
sa.Column("name", sa.String(length=256), nullable=False),
sa.ForeignKeyConstraint(
["source"], ["sources.id"], name="fk_sequences_sources_id_source"
),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint(
"source", "remote_id", name="uc_sequences_source_remote_id"
),
)
op.create_table('author_annotations',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('author', sa.Integer(), nullable=False),
sa.Column('title', sa.String(length=256), nullable=False),
sa.Column('text', sa.Text(), nullable=False),
sa.Column('file', sa.String(length=256), nullable=True),
sa.ForeignKeyConstraint(['author'], ['authors.id'], name='fk_author_annotations_authors_id_author'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('author')
op.create_table(
"author_annotations",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("author", sa.Integer(), nullable=False),
sa.Column("title", sa.String(length=256), nullable=False),
sa.Column("text", sa.Text(), nullable=False),
sa.Column("file", sa.String(length=256), nullable=True),
sa.ForeignKeyConstraint(
["author"], ["authors.id"], name="fk_author_annotations_authors_id_author"
),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("author"),
)
op.create_table('book_annotations',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('book', sa.Integer(), nullable=False),
sa.Column('title', sa.String(length=256), nullable=False),
sa.Column('text', sa.Text(), nullable=False),
sa.Column('file', sa.String(length=256), nullable=True),
sa.ForeignKeyConstraint(['book'], ['books.id'], name='fk_book_annotations_books_id_book'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('book')
op.create_table(
"book_annotations",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("book", sa.Integer(), nullable=False),
sa.Column("title", sa.String(length=256), nullable=False),
sa.Column("text", sa.Text(), nullable=False),
sa.Column("file", sa.String(length=256), nullable=True),
sa.ForeignKeyConstraint(
["book"], ["books.id"], name="fk_book_annotations_books_id_book"
),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("book"),
)
op.create_table('book_authors',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('author', sa.Integer(), nullable=True),
sa.Column('book', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['author'], ['authors.id'], name='fk_book_authors_authors_author_id', onupdate='CASCADE', ondelete='CASCADE'),
sa.ForeignKeyConstraint(['book'], ['books.id'], name='fk_book_authors_books_book_id', onupdate='CASCADE', ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('book', 'author', name='uc_book_authors_book_author'),
op.create_table(
"book_authors",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("author", sa.Integer(), nullable=True),
sa.Column("book", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(
["author"],
["authors.id"],
name="fk_book_authors_authors_author_id",
onupdate="CASCADE",
ondelete="CASCADE",
),
sa.ForeignKeyConstraint(
["book"],
["books.id"],
name="fk_book_authors_books_book_id",
onupdate="CASCADE",
ondelete="CASCADE",
),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("book", "author", name="uc_book_authors_book_author"),
)
op.create_table('book_genres',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('genre', sa.Integer(), nullable=True),
sa.Column('book', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['book'], ['books.id'], name='fk_book_genres_books_book_id', onupdate='CASCADE', ondelete='CASCADE'),
sa.ForeignKeyConstraint(['genre'], ['genres.id'], name='fk_book_genres_genres_genre_id', onupdate='CASCADE', ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('book', 'genre', name='uc_book_genres_book_genre'),
op.create_table(
"book_genres",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("genre", sa.Integer(), nullable=True),
sa.Column("book", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(
["book"],
["books.id"],
name="fk_book_genres_books_book_id",
onupdate="CASCADE",
ondelete="CASCADE",
),
sa.ForeignKeyConstraint(
["genre"],
["genres.id"],
name="fk_book_genres_genres_genre_id",
onupdate="CASCADE",
ondelete="CASCADE",
),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("book", "genre", name="uc_book_genres_book_genre"),
)
op.create_table('book_sequences',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('position', sa.SmallInteger(), nullable=False),
sa.Column('sequence', sa.Integer(), nullable=True),
sa.Column('book', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['book'], ['books.id'], name='fk_book_sequences_books_book_id', onupdate='CASCADE', ondelete='CASCADE'),
sa.ForeignKeyConstraint(['sequence'], ['sequences.id'], name='fk_book_sequences_sequences_sequence_id', onupdate='CASCADE', ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('book', 'sequence', name='uc_book_sequences_book_sequence'),
op.create_table(
"book_sequences",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("position", sa.SmallInteger(), nullable=False),
sa.Column("sequence", sa.Integer(), nullable=True),
sa.Column("book", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(
["book"],
["books.id"],
name="fk_book_sequences_books_book_id",
onupdate="CASCADE",
ondelete="CASCADE",
),
sa.ForeignKeyConstraint(
["sequence"],
["sequences.id"],
name="fk_book_sequences_sequences_sequence_id",
onupdate="CASCADE",
ondelete="CASCADE",
),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("book", "sequence", name="uc_book_sequences_book_sequence"),
)
op.create_table('translations',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('position', sa.SmallInteger(), nullable=False),
sa.Column('author', sa.Integer(), nullable=True),
sa.Column('book', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['author'], ['authors.id'], name='fk_translations_authors_author_id', onupdate='CASCADE', ondelete='CASCADE'),
sa.ForeignKeyConstraint(['book'], ['books.id'], name='fk_translations_books_book_id', onupdate='CASCADE', ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('book', 'author', name='uc_translations_book_author'),
op.create_table(
"translations",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("position", sa.SmallInteger(), nullable=False),
sa.Column("author", sa.Integer(), nullable=True),
sa.Column("book", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(
["author"],
["authors.id"],
name="fk_translations_authors_author_id",
onupdate="CASCADE",
ondelete="CASCADE",
),
sa.ForeignKeyConstraint(
["book"],
["books.id"],
name="fk_translations_books_book_id",
onupdate="CASCADE",
ondelete="CASCADE",
),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("book", "author", name="uc_translations_book_author"),
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('translations')
op.drop_table('book_sequences')
op.drop_table('book_genres')
op.drop_table('book_authors')
op.drop_table('book_annotations')
op.drop_table('author_annotations')
op.drop_table('sequences')
op.drop_table('genres')
op.drop_table('books')
op.drop_table('authors')
op.drop_table('sources')
op.drop_table("translations")
op.drop_table("book_sequences")
op.drop_table("book_genres")
op.drop_table("book_authors")
op.drop_table("book_annotations")
op.drop_table("author_annotations")
op.drop_table("sequences")
op.drop_table("genres")
op.drop_table("books")
op.drop_table("authors")
op.drop_table("sources")
# ### end Alembic commands ###

View File

@@ -6,4 +6,6 @@ from core.config import env_config
async def check_token(api_key: str = Security(default_security)):
if api_key != env_config.API_KEY:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Wrong api key!")
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN, detail="Wrong api key!"
)

View File

@@ -5,6 +5,6 @@ def get_book_filter(is_deleted: Optional[bool] = None) -> dict:
result = {}
if is_deleted is not None:
result['is_deleted'] = is_deleted
result["is_deleted"] = is_deleted
return result

View File

@@ -64,9 +64,13 @@ class AuthorAnnotation(ormar.Model):
id = ormar.Integer(primary_key=True, nullable=False)
author: Author = ormar.ForeignKey(Author, nullable=False, unique=True, related_name="annotations")
author: Author = ormar.ForeignKey(
Author, nullable=False, unique=True, related_name="annotations"
)
title: str = ormar.String(max_length=256, nullable=False, default="") # type: ignore
title: str = ormar.String(
max_length=256, nullable=False, default=""
) # type: ignore
text: str = ormar.Text(nullable=False, default="") # type: ignore
file: str = ormar.String(max_length=256, nullable=True) # type: ignore
@@ -89,7 +93,7 @@ class Sequence(ormar.Model):
class BookAuthors(ormar.Model):
class Meta(BaseMeta):
tablename = "book_authors"
id: int = ormar.Integer(primary_key=True, nullable=False) # type: ignore
@@ -103,7 +107,9 @@ class BookGenres(ormar.Model):
class BookSequences(ormar.Model):
class Meta(BaseMeta):
tablename = "book_sequences"
orders_by = ["position", ]
orders_by = [
"position",
]
id: int = ormar.Integer(primary_key=True, nullable=False) # type: ignore
@@ -113,7 +119,9 @@ class BookSequences(ormar.Model):
class Translation(ormar.Model):
class Meta(BaseMeta):
tablename = "translations"
orders_by = ["position", ]
orders_by = [
"position",
]
id: int = ormar.Integer(primary_key=True, nullable=False) # type: ignore
@@ -132,21 +140,27 @@ class Book(ormar.Model):
source: Source = ormar.ForeignKey(Source, nullable=False)
remote_id: int = ormar.Integer(minimum=0, nullable=False) # type: ignore
title: str = ormar.String(max_length=256, nullable=False, index=True) # type: ignore
title: str = ormar.String(
max_length=256, nullable=False, index=True
) # type: ignore
lang: str = ormar.String(max_length=3, nullable=False) # type: ignore
file_type: str = ormar.String(max_length=4, nullable=False) # type: ignore
uploaded: date = ormar.Date() # type: ignore
is_deleted: bool = ormar.Boolean(default=False, server_default=text("false"), nullable=False)
is_deleted: bool = ormar.Boolean(
default=False, server_default=text("false"), nullable=False
)
authors = ormar.ManyToMany(Author, through=BookAuthors)
translators = ormar.ManyToMany(Author, through=Translation, related_name="translated_books")
translators = ormar.ManyToMany(
Author, through=Translation, related_name="translated_books"
)
genres = ormar.ManyToMany(Genre, through=BookGenres)
sequences = ormar.ManyToMany(Sequence, through=BookSequences)
@ormar.property_field
def available_types(self) -> list[str]:
if self.file_type == 'fb2' and self.source.name == 'flibusta':
return ['fb2', 'fb2zip', 'epub', 'mobi']
if self.file_type == "fb2" and self.source.name == "flibusta":
return ["fb2", "fb2zip", "epub", "mobi"]
return [self.file_type]
@@ -161,8 +175,12 @@ class BookAnnotation(ormar.Model):
id = ormar.Integer(primary_key=True, nullable=False)
book: Book = ormar.ForeignKey(Book, nullable=False, unique=True, related_name="annotations")
book: Book = ormar.ForeignKey(
Book, nullable=False, unique=True, related_name="annotations"
)
title: str = ormar.String(max_length=256, nullable=False, default="") # type: ignore
title: str = ormar.String(
max_length=256, nullable=False, default=""
) # type: ignore
text: str = ormar.Text(nullable=False, default="") # type: ignore
file: str = ormar.String(max_length=256, nullable=True) # type: ignore

View File

@@ -1,5 +1,5 @@
from typing import Optional
from datetime import date
from typing import Optional
from pydantic import BaseModel

View File

@@ -1,5 +1,6 @@
from typing import Optional
from datetime import date
from typing import Optional
from pydantic import BaseModel
from app.serializers.orjson_config import ORJSONConfig

View File

@@ -1,15 +1,17 @@
from app.models import Author
from app.services.common import TRGMSearchService, GetRandomService
GET_OBJECT_IDS_QUERY = """
SELECT ARRAY(
WITH filtered_authors AS (
SELECT
WITH filtered_authors AS (
SELECT
id,
GREATEST(
similarity((last_name || ' ' || first_name || ' ' || middle_name), :query),
similarity(
(last_name || ' ' || first_name || ' ' || middle_name),
:query
),
similarity((last_name || ' ' || first_name), :query),
similarity((last_name), :query)
) as sml,
@@ -27,7 +29,7 @@ SELECT ARRAY(
EXISTS (
SELECT * FROM book_authors
LEFT JOIN books ON (books.id = book AND books.is_deleted = 'f')
WHERE author = authors.id
WHERE author = authors.id
)
)
SELECT fauthors.id FROM filtered_authors as fauthors

View File

@@ -2,15 +2,15 @@ from typing import Union
from fastapi import HTTPException, status
from app.models import Book as BookDB, Author as AuthorDB
from app.services.common import TRGMSearchService, GetRandomService
from app.models import Author as AuthorDB
from app.models import Book as BookDB
from app.serializers.book import CreateBook, CreateRemoteBook
from app.services.common import TRGMSearchService, GetRandomService
GET_OBJECT_IDS_QUERY = """
SELECT ARRAY(
WITH filtered_books AS (
WITH filtered_books AS (
SELECT id, similarity(title, :query) as sml FROM books
WHERE books.title % :query AND books.is_deleted = 'f'
)
@@ -42,9 +42,7 @@ class BookCreator:
if len(author_ids) != len(authors):
cls._raise_bad_request()
book = await BookDB.objects.create(
**data_dict
)
book = await BookDB.objects.create(**data_dict)
for author in authors:
await book.authors.add(author)
@@ -56,14 +54,14 @@ class BookCreator:
data_dict = data.dict()
author_ids = data_dict.pop("remote_authors", [])
authors = await AuthorDB.objects.filter(source__id=data.source, remote_id__in=author_ids).all()
authors = await AuthorDB.objects.filter(
source__id=data.source, remote_id__in=author_ids
).all()
if len(author_ids) != len(authors):
cls._raise_bad_request()
book = await BookDB.objects.create(
**data_dict
)
book = await BookDB.objects.create(**data_dict)
for author in authors:
await book.authors.add(author)

View File

@@ -1,17 +1,17 @@
from typing import Optional, Generic, TypeVar, Union
from databases import Database
import aioredis
from databases import Database
from fastapi_pagination.api import resolve_params
from fastapi_pagination.bases import AbstractParams, RawParams
from app.utils.pagination import Page, CustomPage
import aioredis
import orjson
from ormar import Model, QuerySet
from sqlalchemy import Table
from app.utils.pagination import Page, CustomPage
T = TypeVar('T', bound=Model)
T = TypeVar("T", bound=Model)
class TRGMSearchService(Generic[T]):
@@ -48,7 +48,9 @@ class TRGMSearchService(Generic[T]):
@classmethod
@property
def object_ids_query(cls) -> str:
assert cls.GET_OBJECT_IDS_QUERY is not None, f"GET_OBJECT_IDS_QUERY in {cls.__name__} don't set!"
assert (
cls.GET_OBJECT_IDS_QUERY is not None
), f"GET_OBJECT_IDS_QUERY in {cls.__name__} don't set!"
return cls.GET_OBJECT_IDS_QUERY
@classmethod
@@ -56,9 +58,9 @@ class TRGMSearchService(Generic[T]):
row = await cls.database.fetch_one(cls.object_ids_query, {"query": query_data})
if row is None:
raise ValueError('Something is wrong!')
raise ValueError("Something is wrong!")
return row['array']
return row["array"]
@classmethod
def get_cache_key(cls, query_data: str) -> str:
@@ -66,7 +68,9 @@ class TRGMSearchService(Generic[T]):
return f"{model_class_name}_{query_data}"
@classmethod
async def get_cached_ids(cls, query_data: str, redis: aioredis.Redis) -> Optional[list[int]]:
async def get_cached_ids(
cls, query_data: str, redis: aioredis.Redis
) -> Optional[list[int]]:
try:
key = cls.get_cache_key(query_data)
data = await redis.get(key)
@@ -80,7 +84,9 @@ class TRGMSearchService(Generic[T]):
return None
@classmethod
async def cache_object_ids(cls, query_data: str, object_ids: list[int], redis: aioredis.Redis):
async def cache_object_ids(
cls, query_data: str, object_ids: list[int], redis: aioredis.Redis
):
try:
key = cls.get_cache_key(query_data)
await redis.set(key, orjson.dumps(object_ids), ex=cls.CACHE_TTL)
@@ -88,7 +94,9 @@ class TRGMSearchService(Generic[T]):
print(e)
@classmethod
async def get_objects(cls, query_data: str, redis: aioredis.Redis) -> tuple[int, list[T]]:
async def get_objects(
cls, query_data: str, redis: aioredis.Redis
) -> tuple[int, list[T]]:
params = cls.get_raw_params()
cached_object_ids = await cls.get_cached_ids(query_data, redis)
@@ -99,7 +107,7 @@ class TRGMSearchService(Generic[T]):
else:
object_ids = cached_object_ids
limited_object_ids = object_ids[params.offset:params.offset + params.limit]
limited_object_ids = object_ids[params.offset : params.offset + params.limit]
queryset: QuerySet[T] = cls.model.objects
@@ -117,11 +125,7 @@ class TRGMSearchService(Generic[T]):
total, objects = await cls.get_objects(query, redis)
return CustomPage.create(
items=objects,
total=total,
params=params
)
return CustomPage.create(items=objects, total=total, params=params)
GET_RANDOM_OBJECT_ID_QUERY = """

View File

@@ -1,18 +1,17 @@
from app.models import Sequence
from app.services.common import TRGMSearchService, GetRandomService
GET_OBJECT_IDS_QUERY = """
SELECT ARRAY (
WITH filtered_sequences AS (
SELECT
SELECT
id,
similarity(name, :query) as sml,
(
SELECT count(*) FROM book_sequences
LEFT JOIN books ON (books.id = book AND books.is_deleted = 'f')
WHERE sequence = sequences.id
WHERE sequence = sequences.id
) as books_count
FROM sequences
WHERE name % :query AND

View File

@@ -2,10 +2,12 @@ from typing import Union
from fastapi import HTTPException, status
from app.models import Author as AuthorDB
from app.models import Book as BookDB
from app.models import Source as SourceDB
from app.models import Translation as TranslationDB
from app.serializers.translation import CreateTranslation, CreateRemoteTranslation
from app.models import Translation as TranslationDB, Source as SourceDB, Book as BookDB, Author as AuthorDB
class TranslationCreator:
@classmethod
@@ -14,23 +16,27 @@ class TranslationCreator:
@classmethod
async def _create_translation(cls, data: CreateTranslation) -> TranslationDB:
return await TranslationDB.objects.create(
**data.dict()
)
return await TranslationDB.objects.create(**data.dict())
@classmethod
async def _create_remote_translation(cls, data: CreateRemoteTranslation) -> TranslationDB:
async def _create_remote_translation(
cls, data: CreateRemoteTranslation
) -> TranslationDB:
source = await SourceDB.objects.get_or_none(id=data.source)
if source is None:
cls._raise_bad_request()
book = await BookDB.objects.get_or_none(source__id=source.id, remote_id=data.remote_book)
book = await BookDB.objects.get_or_none(
source__id=source.id, remote_id=data.remote_book
)
if book is None:
cls._raise_bad_request()
translator = await AuthorDB.objects.get_or_none(source__id=source.id, remote_id=data.remote_translator)
translator = await AuthorDB.objects.get_or_none(
source__id=source.id, remote_id=data.remote_translator
)
if translator is None:
cls._raise_bad_request()
@@ -42,7 +48,9 @@ class TranslationCreator:
)
@classmethod
async def create(cls, data: Union[CreateTranslation, CreateRemoteTranslation]) -> TranslationDB:
async def create(
cls, data: Union[CreateTranslation, CreateRemoteTranslation]
) -> TranslationDB:
if isinstance(data, CreateTranslation):
return await cls._create_translation(data)
if isinstance(data, CreateRemoteTranslation):

View File

@@ -1,9 +1,8 @@
from typing import Protocol, TypeVar, Any, Generic, Sequence, runtime_checkable
from pydantic import conint
from fastapi_pagination import Page, Params
from fastapi_pagination.bases import AbstractParams
from pydantic import conint
@runtime_checkable
@@ -12,7 +11,7 @@ class ToDict(Protocol):
...
T = TypeVar('T', ToDict, Any)
T = TypeVar("T", ToDict, Any)
class CustomPage(Page[T], Generic[T]):

View File

@@ -1,14 +1,10 @@
from app.views.source import source_router
from app.views.author import author_router
from app.views.author_annotation import author_annotation_router
from app.views.book import book_router
from app.views.book_annotation import book_annotation_router
from app.views.translation import translation_router
from app.views.sequence import sequence_router
from app.views.source import source_router
from app.views.translation import translation_router
routers = [

View File

@@ -1,16 +1,22 @@
from random import choice as random_choice
from fastapi import APIRouter, Depends, Request, HTTPException, status
from fastapi_pagination import Params
from fastapi_pagination.ext.ormar import paginate
from app.utils.pagination import CustomPage
from app.models import Author as AuthorDB, AuthorAnnotation as AuthorAnnotationDB, Book as BookDB
from app.serializers.author import Author, CreateAuthor, UpdateAuthor, AuthorBook, TranslatedBook
from app.depends import check_token
from app.models import Author as AuthorDB
from app.models import AuthorAnnotation as AuthorAnnotationDB
from app.models import Book as BookDB
from app.serializers.author import (
Author,
CreateAuthor,
UpdateAuthor,
AuthorBook,
TranslatedBook,
)
from app.serializers.author_annotation import AuthorAnnotation
from app.services.author import AuthorTGRMSearchService, GetRandomAuthorService
from app.depends import check_token
from app.utils.pagination import CustomPage
author_router = APIRouter(
@@ -20,22 +26,19 @@ author_router = APIRouter(
)
PREFETCH_RELATED = ["source", "annotations"]
@author_router.get("/", response_model=CustomPage[Author], dependencies=[Depends(Params)])
@author_router.get(
"/", response_model=CustomPage[Author], dependencies=[Depends(Params)]
)
async def get_authors():
return await paginate(
AuthorDB.objects.prefetch_related(PREFETCH_RELATED)
)
return await paginate(AuthorDB.objects.prefetch_related(PREFETCH_RELATED))
@author_router.post("/", response_model=Author, dependencies=[Depends(Params)])
async def create_author(data: CreateAuthor):
author = await AuthorDB.objects.create(
**data.dict()
)
author = await AuthorDB.objects.create(**data.dict())
return await AuthorDB.objects.prefetch_related(PREFETCH_RELATED).get(id=author.id)
@@ -49,7 +52,9 @@ async def get_random_author():
@author_router.get("/{id}", response_model=Author)
async def get_author(id: int):
author = await AuthorDB.objects.prefetch_related(PREFETCH_RELATED).get_or_none(id=id)
author = await AuthorDB.objects.prefetch_related(PREFETCH_RELATED).get_or_none(
id=id
)
if author is None:
raise HTTPException(status.HTTP_404_NOT_FOUND)
@@ -75,24 +80,32 @@ async def get_author_annotation(id: int):
if annotation is None:
raise HTTPException(status.HTTP_404_NOT_FOUND)
return annotation
@author_router.get("/{id}/books", response_model=CustomPage[AuthorBook], dependencies=[Depends(Params)])
@author_router.get(
"/{id}/books", response_model=CustomPage[AuthorBook], dependencies=[Depends(Params)]
)
async def get_author_books(id: int):
return await paginate(
BookDB.objects.select_related(["source", "annotations", "translators"]).filter(authors__id=id).order_by('title')
BookDB.objects.select_related(["source", "annotations", "translators"])
.filter(authors__id=id)
.order_by("title")
)
@author_router.get("/{id}/translated_books", response_model=CustomPage[TranslatedBook])
async def get_translated_books(id: int):
return await paginate(
BookDB.objects.select_related(["source", "annotations", "translators"]).filter(translations__translator__id=id)
BookDB.objects.select_related(["source", "annotations", "translators"]).filter(
translations__translator__id=id
)
)
@author_router.get("/search/{query}", response_model=CustomPage[Author], dependencies=[Depends(Params)])
@author_router.get(
"/search/{query}", response_model=CustomPage[Author], dependencies=[Depends(Params)]
)
async def search_authors(query: str, request: Request):
return await AuthorTGRMSearchService.get(query, request.app.state.redis)

View File

@@ -3,9 +3,13 @@ from fastapi import APIRouter, Depends, HTTPException, status
from fastapi_pagination import Params, Page
from fastapi_pagination.ext.ormar import paginate
from app.models import AuthorAnnotation as AuthorAnnotationDB
from app.serializers.author_annotation import AuthorAnnotation, CreateAuthorAnnotation, UpdateAuthorAnnotation
from app.depends import check_token
from app.models import AuthorAnnotation as AuthorAnnotationDB
from app.serializers.author_annotation import (
AuthorAnnotation,
CreateAuthorAnnotation,
UpdateAuthorAnnotation,
)
author_annotation_router = APIRouter(
@@ -15,18 +19,16 @@ author_annotation_router = APIRouter(
)
@author_annotation_router.get("/", response_model=Page[AuthorAnnotation], dependencies=[Depends(Params)])
@author_annotation_router.get(
"/", response_model=Page[AuthorAnnotation], dependencies=[Depends(Params)]
)
async def get_author_annotations():
return await paginate(
AuthorAnnotationDB.objects
)
return await paginate(AuthorAnnotationDB.objects)
@author_annotation_router.post("/", response_model=AuthorAnnotation)
async def create_author_annotation(data: CreateAuthorAnnotation):
return await AuthorAnnotationDB.objects.create(
**data.dict()
)
return await AuthorAnnotationDB.objects.create(**data.dict())
@author_annotation_router.get("/{id}", response_model=AuthorAnnotation)

View File

@@ -1,18 +1,26 @@
from typing import Union
from random import choice as random_choice
from fastapi import APIRouter, Depends, Request, HTTPException, status
from fastapi_pagination import Params
from fastapi_pagination.ext.ormar import paginate
from app.utils.pagination import CustomPage
from app.models import Book as BookDB, Author as AuthorDB, BookAnnotation as BookAnnotationDB
from app.serializers.book import Book, RemoteBook, BookDetail, CreateBook, UpdateBook, CreateRemoteBook
from app.depends import check_token
from app.filters.book import get_book_filter
from app.models import Author as AuthorDB
from app.models import Book as BookDB
from app.models import BookAnnotation as BookAnnotationDB
from app.serializers.book import (
Book,
RemoteBook,
BookDetail,
CreateBook,
UpdateBook,
CreateRemoteBook,
)
from app.serializers.book_annotation import BookAnnotation
from app.services.book import BookTGRMSearchService, GetRandomBookService, BookCreator
from app.filters.book import get_book_filter
from app.depends import check_token
from app.utils.pagination import CustomPage
book_router = APIRouter(
@@ -22,10 +30,12 @@ book_router = APIRouter(
)
SELECT_RELATED_FIELDS = ["source", "authors", "translators", "annotations"]
SELECT_RELATED_FIELDS = ["source", "authors", "translators", "annotations"]
@book_router.get("/", response_model=CustomPage[RemoteBook], dependencies=[Depends(Params)])
@book_router.get(
"/", response_model=CustomPage[RemoteBook], dependencies=[Depends(Params)]
)
async def get_books(book_filter: dict = Depends(get_book_filter)):
return await paginate(
BookDB.objects.select_related(SELECT_RELATED_FIELDS).filter(**book_filter)
@@ -49,7 +59,7 @@ async def get_random_book():
@book_router.get("/{id}", response_model=BookDetail)
async def get_book(id: int):
book = await BookDB.objects.select_related(SELECT_RELATED_FIELDS).get_or_none(id=id)
if book is None:
raise HTTPException(status.HTTP_404_NOT_FOUND)
@@ -59,8 +69,7 @@ async def get_book(id: int):
@book_router.get("/remote/{source_id}/{remote_id}", response_model=Book)
async def get_remote_book(source_id: int, remote_id: int):
book = await BookDB.objects.select_related(SELECT_RELATED_FIELDS).get_or_none(
source=source_id,
remote_id=remote_id
source=source_id, remote_id=remote_id
)
if book is None:
@@ -84,9 +93,7 @@ async def update_book(id: int, data: UpdateBook):
author_ids = data_dict.pop("authors", [])
authors = await AuthorDB.objects.filter(id__in=author_ids).all()
book = await BookDB.objects.create(
**data_dict
)
book = await BookDB.objects.create(**data_dict)
for author in authors:
await book.authors.add(author)
@@ -104,6 +111,8 @@ async def get_book_annotation(id: int):
return annotation
@book_router.get("/search/{query}", response_model=CustomPage[Book], dependencies=[Depends(Params)])
@book_router.get(
"/search/{query}", response_model=CustomPage[Book], dependencies=[Depends(Params)]
)
async def search_books(query: str, request: Request):
return await BookTGRMSearchService.get(query, request.app.state.redis)

View File

@@ -3,30 +3,32 @@ from fastapi import APIRouter, Depends, HTTPException, status
from fastapi_pagination import Params, Page
from fastapi_pagination.ext.ormar import paginate
from app.models import BookAnnotation as BookAnnotationDB
from app.serializers.book_annotation import BookAnnotation, CreateBookAnnotation, UpdateBookAnnotation
from app.depends import check_token
from app.models import BookAnnotation as BookAnnotationDB
from app.serializers.book_annotation import (
BookAnnotation,
CreateBookAnnotation,
UpdateBookAnnotation,
)
book_annotation_router = APIRouter(
prefix="/api/v1/book_annotations",
tags=["book_annotation"],
dependencies=[Depends(check_token)]
dependencies=[Depends(check_token)],
)
@book_annotation_router.get("/", response_model=Page[BookAnnotation], dependencies=[Depends(Params)])
@book_annotation_router.get(
"/", response_model=Page[BookAnnotation], dependencies=[Depends(Params)]
)
async def get_book_annotations():
return await paginate(
BookAnnotationDB.objects
)
return await paginate(BookAnnotationDB.objects)
@book_annotation_router.post("/", response_model=BookAnnotation)
async def create_book_annotation(data: CreateBookAnnotation):
return await BookAnnotationDB.objects.create(
**data.dict()
)
return await BookAnnotationDB.objects.create(**data.dict())
@book_annotation_router.get("/{id}", response_model=BookAnnotation)

View File

@@ -1,15 +1,15 @@
from random import choice as random_choice
from fastapi import APIRouter, Depends, Request
from fastapi_pagination import Params
from fastapi_pagination.ext.ormar import paginate
from app.utils.pagination import CustomPage
from app.models import Sequence as SequenceDB, Book as BookDB, BookSequences as BookSequencesDB
from app.serializers.sequence import Sequence, CreateSequence, Book as SequenceBook
from app.services.sequence import SequenceTGRMSearchService, GetRandomSequenceService
from app.depends import check_token
from app.models import Book as BookDB
from app.models import Sequence as SequenceDB
from app.serializers.sequence import Book as SequenceBook
from app.serializers.sequence import Sequence, CreateSequence
from app.services.sequence import SequenceTGRMSearchService, GetRandomSequenceService
from app.utils.pagination import CustomPage
sequence_router = APIRouter(
@@ -19,11 +19,11 @@ sequence_router = APIRouter(
)
@sequence_router.get("/", response_model=CustomPage[Sequence], dependencies=[Depends(Params)])
@sequence_router.get(
"/", response_model=CustomPage[Sequence], dependencies=[Depends(Params)]
)
async def get_sequences():
return await paginate(
SequenceDB.objects
)
return await paginate(SequenceDB.objects)
@sequence_router.get("/random", response_model=Sequence)
@@ -38,21 +38,30 @@ async def get_sequence(id: int):
return await SequenceDB.objects.get(id=id)
@sequence_router.get("/{id}/books", response_model=CustomPage[SequenceBook], dependencies=[Depends(Params)])
@sequence_router.get(
"/{id}/books",
response_model=CustomPage[SequenceBook],
dependencies=[Depends(Params)],
)
async def get_sequence_books(id: int):
return await paginate(
BookDB.objects.select_related(["source", "annotations", "authors", "translators"])
.filter(sequences__id=id).order_by("sequences__booksequences__position")
BookDB.objects.select_related(
["source", "annotations", "authors", "translators"]
)
.filter(sequences__id=id)
.order_by("sequences__booksequences__position")
)
@sequence_router.post("/", response_model=Sequence)
async def create_sequence(data: CreateSequence):
return await SequenceDB.objects.create(
**data.dict()
)
return await SequenceDB.objects.create(**data.dict())
@sequence_router.get("/search/{query}", response_model=CustomPage[Sequence], dependencies=[Depends(Params)])
@sequence_router.get(
"/search/{query}",
response_model=CustomPage[Sequence],
dependencies=[Depends(Params)],
)
async def search_sequences(query: str, request: Request):
return await SequenceTGRMSearchService.get(query, request.app.state.redis)

View File

@@ -3,9 +3,9 @@ from fastapi import APIRouter, Depends
from fastapi_pagination import Params, Page
from fastapi_pagination.ext.ormar import paginate
from app.depends import check_token
from app.models import Source as SourceDB
from app.serializers.source import Source, CreateSource
from app.depends import check_token
source_router = APIRouter(
@@ -22,6 +22,4 @@ async def get_sources():
@source_router.post("", response_model=Source)
async def create_source(data: CreateSource):
return await SourceDB.objects.create(
**data.dict()
)
return await SourceDB.objects.create(**data.dict())

View File

@@ -4,12 +4,16 @@ from fastapi import APIRouter, Depends, HTTPException, status
from fastapi_pagination import Params
from fastapi_pagination.ext.ormar import paginate
from app.utils.pagination import CustomPage
from app.models import Translation as TranslationDB
from app.serializers.translation import Translation, CreateTranslation, CreateRemoteTranslation
from app.services.translation import TranslationCreator
from app.depends import check_token
from app.models import Translation as TranslationDB
from app.serializers.translation import (
Translation,
CreateTranslation,
CreateRemoteTranslation,
)
from app.services.translation import TranslationCreator
from app.utils.pagination import CustomPage
translation_router = APIRouter(
@@ -19,23 +23,27 @@ translation_router = APIRouter(
)
@translation_router.get("/", response_model=CustomPage[Translation], dependencies=[Depends(Params)])
@translation_router.get(
"/", response_model=CustomPage[Translation], dependencies=[Depends(Params)]
)
async def get_translations():
return await paginate(
TranslationDB.objects.prefetch_related(["book", "author"])
)
return await paginate(TranslationDB.objects.prefetch_related(["book", "author"]))
@translation_router.post("/", response_model=Translation)
async def create_translation(data: Union[CreateTranslation, CreateRemoteTranslation]):
translation = await TranslationCreator.create(data)
return await TranslationDB.objects.prefetch_related(["book", "author"]).get(id=translation.id)
return await TranslationDB.objects.prefetch_related(["book", "author"]).get(
id=translation.id
)
@translation_router.delete("/{id}", response_model=Translation)
async def delete_translation(id: int):
translation = await TranslationDB.objects.prefetch_related(["book", "author"]).get_or_none(id=id)
translation = await TranslationDB.objects.prefetch_related(
["book", "author"]
).get_or_none(id=id)
if translation is None:
raise HTTPException(status.HTTP_404_NOT_FOUND)

View File

@@ -1,11 +1,11 @@
from fastapi import FastAPI
from fastapi_pagination import add_pagination
import aioredis
from core.db import database
from core.config import env_config
import aioredis
from fastapi_pagination import add_pagination
from app.views import routers
from core.config import env_config
from core.db import database
def start_app() -> FastAPI:
@@ -25,13 +25,13 @@ def start_app() -> FastAPI:
add_pagination(app)
@app.on_event('startup')
@app.on_event("startup")
async def startup() -> None:
database_ = app.state.database
if not database_.is_connected:
await database_.connect()
@app.on_event('shutdown')
@app.on_event("shutdown")
async def shutdown() -> None:
database_ = app.state.database
if database_.is_connected:

View File

@@ -18,8 +18,8 @@ class EnvConfig(BaseSettings):
REDIS_PASSWORD: Optional[str]
class Config:
env_file = '.env'
env_file_encoding = 'utf-8'
env_file = ".env"
env_file_encoding = "utf-8"
env_config = EnvConfig()

View File

@@ -1,6 +1,6 @@
from urllib.parse import quote
from databases import Database
from databases import Database
from sqlalchemy import MetaData
from core.config import env_config

View File

@@ -24,3 +24,43 @@ pytest = "^5.2"
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
[tool.black]
include = '\.pyi?$'
exclude = '''
/(
\.git
| \.vscode
| \venv
| alembic
)/
'''
[tool.flake8]
ignore = [
# Whitespace before ':' ( https://www.flake8rules.com/rules/E203.html )
"E203"
]
max-line-length=88
max-complexity = 15
select = "B,C,E,F,W,T4,B9"
exclude = [
# No need to traverse our git directory
".git",
# There's no value in checking cache directories
"__pycache__",
# The conf file is mostly autogenerated, ignore it
"fastapi_book_server/app/alembic/*",
# The old directory contains Flake8 2.0
]
[tool.isort]
profile = "black"
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",]
src_paths = ["fastapi_book_server"]

View File

@@ -2,4 +2,4 @@ from fastapi_book_server import __version__
def test_version():
assert __version__ == '0.1.0'
assert __version__ == "0.1.0"