diff --git a/README.md b/README.md index d252d9a3..34143b94 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,15 @@ poetry env use .venv/bin/python3.12 poetry update poetry run server.py ``` + +### Полезные команды + +```shell +poetry run ruff check . --fix --select I # линтер и сортировка импортов +poetry run ruff format . --line-length=120 # форматирование кода +``` + + ## Подключенные сервисы Для межсерверной коммуникации используются отдельные логики, папка `services/*` содержит адаптеры для использования базы данных, `redis`, кеширование и клиенты для запросов GraphQL. diff --git a/cache/cache.py b/cache/cache.py index af1cceea..49895051 100644 --- a/cache/cache.py +++ b/cache/cache.py @@ -63,17 +63,17 @@ async def update_follower_stat(follower_id, entity_type, count): # Get author from cache -async def get_cached_author(author_id: int): +async def get_cached_author(author_id: int, get_with_stat): author_key = f"author:id:{author_id}" result = await redis.execute("get", author_key) if result: return json.loads(result) # Load from database if not found in cache - with local_session() as session: - author = session.execute(select(Author).where(Author.id == author_id)).scalar_one_or_none() - if author: - await cache_author(author.dict()) - return author.dict() + q = select(Author).where(Author.id == author_id) + author = get_with_stat(q) + if author: + await cache_author(author.dict()) + return author.dict() return None diff --git a/cache/revalidator.py b/cache/revalidator.py index 392e4dc1..46f5193b 100644 --- a/cache/revalidator.py +++ b/cache/revalidator.py @@ -1,5 +1,6 @@ import asyncio -from cache.cache import get_cached_author, cache_author, get_cached_topic, cache_topic + +from cache.cache import cache_author, cache_topic, get_cached_author, get_cached_topic from utils.logger import root_logger as logger diff --git a/cache/triggers.py b/cache/triggers.py index 7c34c648..82ab822f 100644 --- a/cache/triggers.py +++ b/cache/triggers.py @@ -1,9 +1,10 @@ from sqlalchemy import event + +from cache.revalidator import revalidation_manager from orm.author import Author, AuthorFollower from orm.reaction import Reaction from orm.shout import Shout, ShoutAuthor, ShoutReactionsFollower from orm.topic import Topic, TopicFollower -from cache.revalidator import revalidation_manager from utils.logger import root_logger as logger diff --git a/main.py b/main.py index 2ec14568..b4d61f20 100644 --- a/main.py +++ b/main.py @@ -7,15 +7,15 @@ from ariadne.asgi import GraphQL from starlette.applications import Starlette from starlette.routing import Route +from cache.precache import precache_data +from cache.revalidator import revalidation_manager from services.exception import ExceptionHandlerMiddleware +from services.redis import redis from services.schema import resolvers from services.search import search_service from services.sentry import start_sentry from services.viewed import ViewedStorage from services.webhook import WebhookEndpoint -from cache.precache import precache_data -from services.redis import redis -from cache.revalidator import revalidation_manager from settings import DEV_SERVER_PID_FILE_NAME, MODE import_module("resolvers") diff --git a/resolvers/__init__.py b/resolvers/__init__.py index 5abf3fed..a466dcef 100644 --- a/resolvers/__init__.py +++ b/resolvers/__init__.py @@ -1,3 +1,4 @@ +from cache.triggers import events_register from resolvers.author import ( # search_authors, get_author, get_author_followers, @@ -22,24 +23,24 @@ from resolvers.rating import rate_author from resolvers.reaction import ( create_reaction, delete_reaction, + load_comment_ratings, load_reactions_by, - update_reaction, load_shout_comments, load_shout_ratings, - load_comment_ratings, + update_reaction, ) from resolvers.reader import ( get_shout, load_shouts_by, + load_shouts_coauthored, + load_shouts_discussed, load_shouts_feed, + load_shouts_followed, + load_shouts_followed_by, load_shouts_random_top, load_shouts_random_topic, load_shouts_search, load_shouts_unrated, - load_shouts_coauthored, - load_shouts_discussed, - load_shouts_followed, - load_shouts_followed_by, ) from resolvers.topic import ( get_topic, @@ -49,7 +50,6 @@ from resolvers.topic import ( get_topics_by_author, get_topics_by_community, ) -from cache.triggers import events_register events_register() diff --git a/resolvers/editor.py b/resolvers/editor.py index f61ce441..ef4400a5 100644 --- a/resolvers/editor.py +++ b/resolvers/editor.py @@ -4,6 +4,7 @@ from sqlalchemy import and_, desc, select from sqlalchemy.orm import joinedload from sqlalchemy.sql.functions import coalesce +from cache.cache import cache_author, cache_topic from orm.author import Author from orm.rating import is_negative, is_positive from orm.reaction import Reaction, ReactionKind @@ -12,13 +13,12 @@ from orm.topic import Topic from resolvers.follower import follow, unfollow from resolvers.stat import get_with_stat from services.auth import login_required -from cache.cache import cache_author, cache_topic from services.db import local_session -from utils.diff import apply_diff, get_diff -from utils.logger import root_logger as logger from services.notify import notify_shout from services.schema import mutation, query from services.search import search_service +from utils.diff import apply_diff, get_diff +from utils.logger import root_logger as logger async def cache_by_id(entity, entity_id: int, cache_method): diff --git a/resolvers/follower.py b/resolvers/follower.py index 80e61e2e..be5e4999 100644 --- a/resolvers/follower.py +++ b/resolvers/follower.py @@ -3,6 +3,12 @@ from typing import List from sqlalchemy import select from sqlalchemy.sql import and_ +from cache.cache import ( + cache_author, + cache_topic, + get_cached_follower_authors, + get_cached_follower_topics, +) from orm.author import Author, AuthorFollower from orm.community import Community, CommunityFollower from orm.reaction import Reaction @@ -10,7 +16,6 @@ from orm.shout import Shout, ShoutReactionsFollower from orm.topic import Topic, TopicFollower from resolvers.stat import get_with_stat from services.auth import login_required -from cache.cache import cache_author, cache_topic, get_cached_follower_authors, get_cached_follower_topics from services.db import local_session from services.notify import notify_follower from services.schema import mutation, query diff --git a/resolvers/notifier.py b/resolvers/notifier.py index 0505db5f..5cd2fbcb 100644 --- a/resolvers/notifier.py +++ b/resolvers/notifier.py @@ -8,12 +8,17 @@ from sqlalchemy.orm import aliased from sqlalchemy.sql import not_ from orm.author import Author -from orm.notification import Notification, NotificationAction, NotificationEntity, NotificationSeen +from orm.notification import ( + Notification, + NotificationAction, + NotificationEntity, + NotificationSeen, +) from orm.shout import Shout from services.auth import login_required from services.db import local_session -from utils.logger import root_logger as logger from services.schema import mutation, query +from utils.logger import root_logger as logger def query_notifications(author_id: int, after: int = 0) -> Tuple[int, int, List[Tuple[Notification, bool]]]: diff --git a/resolvers/reader.py b/resolvers/reader.py index 3e7dfadb..bab76042 100644 --- a/resolvers/reader.py +++ b/resolvers/reader.py @@ -1,5 +1,6 @@ from typing import List -from sqlalchemy.orm import aliased, selectinload, joinedload + +from sqlalchemy.orm import aliased, joinedload, selectinload from sqlalchemy.sql import union from sqlalchemy.sql.expression import ( and_, @@ -12,17 +13,18 @@ from sqlalchemy.sql.expression import ( select, text, ) + from orm.author import Author, AuthorFollower from orm.reaction import Reaction, ReactionKind -from orm.shout import Shout, ShoutAuthor, ShoutTopic, ShoutReactionsFollower +from orm.shout import Shout, ShoutAuthor, ShoutReactionsFollower, ShoutTopic from orm.topic import Topic, TopicFollower from resolvers.topic import get_topics_random from services.auth import login_required from services.db import local_session -from utils.logger import root_logger as logger from services.schema import query from services.search import search_text from services.viewed import ViewedStorage +from utils.logger import root_logger as logger def query_shouts(slug=None): diff --git a/server.py b/server.py index d74fd22d..3140d441 100644 --- a/server.py +++ b/server.py @@ -3,8 +3,8 @@ import subprocess from granian.constants import Interfaces from granian.server import Granian -from utils.logger import root_logger as logger from settings import PORT +from utils.logger import root_logger as logger def is_docker_container_running(name): diff --git a/services/auth.py b/services/auth.py index 8d9b2982..bf48b76a 100644 --- a/services/auth.py +++ b/services/auth.py @@ -2,10 +2,10 @@ from functools import wraps import httpx -from resolvers.stat import get_with_stat from cache.cache import get_cached_author_by_user_id -from utils.logger import root_logger as logger +from resolvers.stat import get_with_stat from settings import ADMIN_SECRET, AUTH_URL +from utils.logger import root_logger as logger async def request_data(gql, headers=None): diff --git a/services/db.py b/services/db.py index 0458c567..9e32ac9b 100644 --- a/services/db.py +++ b/services/db.py @@ -10,8 +10,8 @@ from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import Session, configure_mappers from sqlalchemy.sql.schema import Table -from utils.logger import root_logger as logger from settings import DB_URL +from utils.logger import root_logger as logger # from sqlalchemy_searchable import make_searchable diff --git a/services/notify.py b/services/notify.py index e807d397..626afa7b 100644 --- a/services/notify.py +++ b/services/notify.py @@ -2,8 +2,8 @@ import json from orm.notification import Notification from services.db import local_session -from utils.logger import root_logger as logger from services.redis import redis +from utils.logger import root_logger as logger def save_notification(action: str, entity: str, payload): diff --git a/services/search.py b/services/search.py index 2d64274e..8e195d19 100644 --- a/services/search.py +++ b/services/search.py @@ -5,8 +5,8 @@ import os from opensearchpy import OpenSearch -from utils.encoders import CustomJSONEncoder from services.redis import redis +from utils.encoders import CustomJSONEncoder # Set redis logging level to suppress DEBUG messages logger = logging.getLogger("search") diff --git a/services/sentry.py b/services/sentry.py index 18d21ab6..04f4f377 100644 --- a/services/sentry.py +++ b/services/sentry.py @@ -2,6 +2,7 @@ import sentry_sdk from sentry_sdk.integrations.ariadne import AriadneIntegration from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration from sentry_sdk.integrations.starlette import StarletteIntegration + from settings import GLITCHTIP_DSN diff --git a/services/viewed.py b/services/viewed.py index b4eb9ebe..762e6253 100644 --- a/services/viewed.py +++ b/services/viewed.py @@ -7,7 +7,12 @@ from typing import Dict # ga from google.analytics.data_v1beta import BetaAnalyticsDataClient -from google.analytics.data_v1beta.types import DateRange, Dimension, Metric, RunReportRequest +from google.analytics.data_v1beta.types import ( + DateRange, + Dimension, + Metric, + RunReportRequest, +) from orm.author import Author from orm.shout import Shout, ShoutAuthor, ShoutTopic diff --git a/services/webhook.py b/services/webhook.py index fd98fdf6..08a8aefb 100644 --- a/services/webhook.py +++ b/services/webhook.py @@ -8,9 +8,9 @@ from starlette.exceptions import HTTPException from starlette.requests import Request from starlette.responses import JSONResponse +from cache.cache import cache_author from orm.author import Author from resolvers.stat import get_with_stat -from cache.cache import cache_author from services.db import local_session