diff --git a/resolvers/author.py b/resolvers/author.py index fd01c2cc..4a2efb5e 100644 --- a/resolvers/author.py +++ b/resolvers/author.py @@ -10,7 +10,7 @@ from orm.reaction import Reaction, ReactionKind from orm.shout import Shout, ShoutAuthor, ShoutTopic from orm.topic import Topic from resolvers.follower import query_follows -from resolvers.stat import add_stat_columns, get_with_stat, unpack_stat +from resolvers.stat import get_authors_with_stat from services.auth import login_required from services.db import local_session from services.rediscache import redis @@ -98,7 +98,7 @@ def count_author_shouts_rating(session, author_id) -> int: def load_author_with_stats(q): - result = get_with_stat(q, Author, AuthorFollower) + result = get_authors_with_stat(q) if result: [author] = result @@ -199,7 +199,7 @@ def load_authors_by(_, _info, by, limit, offset): q = q.limit(limit).offset(offset) q = q.group_by(Author.id) - authors = get_with_stat(q, Author, AuthorFollower) + authors = get_authors_with_stat(q) return authors @@ -269,14 +269,10 @@ def create_author(user_id: str, slug: str, name: str = ''): @query.field('get_author_followers') def get_author_followers(_, _info, slug) -> List[Author]: - aliased_author = aliased(Author) - q = select(aliased_author) - + q = select(Author) q = ( q.join(AuthorFollower, AuthorFollower.follower == Author.id) - .join(aliased_author, aliased_author.id == AuthorFollower.author) - .where(aliased_author.slug == slug) + .join(Author, Author.id == AuthorFollower.author) + .where(Author.slug == slug) ) - q = add_stat_columns(q, aliased_author, AuthorFollower) - q = q.group_by(aliased_author.id) - return unpack_stat(q) + return get_authors_with_stat(q) diff --git a/resolvers/follower.py b/resolvers/follower.py index 0ec30162..810c744b 100644 --- a/resolvers/follower.py +++ b/resolvers/follower.py @@ -14,7 +14,7 @@ from orm.shout import Shout, ShoutReactionsFollower from orm.topic import Topic, TopicFollower from resolvers.community import community_follow, community_unfollow from resolvers.topic import topic_follow, topic_unfollow -from resolvers.stat import add_stat_columns, unpack_stat +from resolvers.stat import get_topics_with_stat, get_authors_with_stat from services.auth import login_required from services.db import local_session from services.follows import DEFAULT_FOLLOWS @@ -110,10 +110,8 @@ def query_follows(user_id: str): .filter(TopicFollower.topic == Topic.id) ) - authors_query = add_stat_columns(authors_query, aliased_author, AuthorFollower) - authors = unpack_stat(authors_query) - topics_query = add_stat_columns(topics_query, aliased_author, TopicFollower) - authors = unpack_stat(topics_query) + authors = get_authors_with_stat(authors_query) + topics = get_topics_with_stat(topics_query) return { 'topics': topics, @@ -226,10 +224,7 @@ def get_topic_followers(_, _info, slug: str, topic_id: int) -> List[Author]: .join(Topic, Topic.id == TopicFollower.topic) .filter(or_(Topic.slug == slug, Topic.id == topic_id)) ) - q = add_stat_columns(q, Author, TopicFollower) - q = q.group_by(Author.id) - - return unpack_stat(q) + return get_authors_with_stat(q) @query.field('get_shout_followers') diff --git a/resolvers/stat.py b/resolvers/stat.py index 94dddd46..fe476981 100644 --- a/resolvers/stat.py +++ b/resolvers/stat.py @@ -1,19 +1,44 @@ from sqlalchemy import func, distinct +from sqlalchemy.orm import aliased +from orm.topic import TopicFollower, Topic from services.db import local_session -from orm.author import AuthorFollower +from orm.author import AuthorFollower, Author from orm.shout import ShoutTopic, ShoutAuthor -def add_stat_columns(q, author_alias, follower_model_alias): - shouts_stat_model = ShoutAuthor if isinstance(follower_model_alias, AuthorFollower) else ShoutTopic - q = q.outerjoin(shouts_stat_model).add_columns(func.count(distinct(shouts_stat_model.shout)).label('shouts_stat')) - q = q.outerjoin( - follower_model_alias, follower_model_alias.follower == author_alias.id - ).add_columns(func.count(distinct(follower_model_alias.author)).label('authors_stat')) - q = q.outerjoin(follower_model_alias, follower_model_alias.author == author_alias.id).add_columns( - func.count(distinct(follower_model_alias.follower)).label('followers_stat') +def add_topic_stat_columns(q): + aliased_shout_author = aliased(ShoutAuthor) + aliased_topic_follower = aliased(TopicFollower) + + q = ( + q.outerjoin(ShoutTopic, Topic.id == ShoutTopic.topic) + .add_columns(func.count(distinct(ShoutTopic.shout)).label('shouts_stat')) + .outerjoin(aliased_shout_author, ShoutTopic.shout == aliased_shout_author.shout) + .add_columns(func.count(distinct(aliased_shout_author.author)).label('authors_stat')) + .outerjoin(aliased_topic_follower) + .add_columns(func.count(distinct(aliased_topic_follower.follower)).label('followers_stat')) ) + + q = q.group_by(Topic.id) + + return q + + +def add_author_stat_columns(q): + aliased_author_followers = aliased(AuthorFollower) + aliased_author_authors = aliased(AuthorFollower) + q = ( + q.outerjoin(ShoutAuthor, Author.id == ShoutAuthor.author) + .add_columns(func.count(distinct(ShoutAuthor.shout)).label('shouts_stat')) + .outerjoin(aliased_author_authors, AuthorFollower.follower == Author.id) + .add_columns(func.count(distinct(aliased_author_authors.author)).label('authors_stat')) + .outerjoin(aliased_author_followers) + .add_columns(func.count(distinct(aliased_author_followers.follower)).label('followers_stat')) + ) + + q = q.group_by(Author.id) + return q @@ -31,6 +56,11 @@ def unpack_stat(q): return records -def get_with_stat(q, author_alias, follower_model_alias): - q = add_stat_columns(q, author_alias, follower_model_alias) +def get_authors_with_stat(q): + q = add_author_stat_columns(q) + return unpack_stat(q) + + +def get_topics_with_stat(q): + q = add_topic_stat_columns(q) return unpack_stat(q) diff --git a/resolvers/topic.py b/resolvers/topic.py index 3e1615de..0955614a 100644 --- a/resolvers/topic.py +++ b/resolvers/topic.py @@ -3,7 +3,7 @@ from sqlalchemy import and_, distinct, func, select from orm.author import Author from orm.shout import ShoutTopic from orm.topic import Topic, TopicFollower -from resolvers.stat import get_with_stat +from resolvers.stat import get_topics_with_stat from services.auth import login_required from services.db import local_session from services.schema import mutation, query @@ -12,22 +12,17 @@ from services.logger import root_logger as logger @query.field('get_topics_all') def get_topics_all(_, _info): - q = select(Topic) - q = q.group_by(Topic.id) - topics = get_with_stat(q, Author, TopicFollower) - return topics + return get_topics_with_stat(select(Topic)) @query.field('get_topics_by_community') -async def get_topics_by_community(_, _info, community_id: int): +def get_topics_by_community(_, _info, community_id: int): q = select(Topic).where(Topic.community == community_id) - q = q.group_by(Topic.id) - topics = await get_with_stat(q, Author, TopicFollower) - return topics + return get_topics_with_stat(q) @query.field('get_topics_by_author') -async def get_topics_by_author(_, _info, author_id=None, slug='', user=''): +def get_topics_by_author(_, _info, author_id=None, slug='', user=''): q = select(Topic) if author_id: q = q.join(Author).where(Author.id == author_id) @@ -36,23 +31,21 @@ async def get_topics_by_author(_, _info, author_id=None, slug='', user=''): elif user: q = q.join(Author).where(Author.user == user) - q = q.group_by(Topic.id) - topics = await get_with_stat(q, Author, TopicFollower) - return topics + return get_topics_with_stat(q) @query.field('get_topic') -async def get_topic(_, _info, slug): +def get_topic(_, _info, slug): q = select(Topic).filter(Topic.slug == slug) q = q.group_by(Topic.id) - topics = await get_with_stat(q, Author, TopicFollower) + topics = get_topics_with_stat(q) if topics: return topics[0] @mutation.field('create_topic') @login_required -async def create_topic(_, _info, inp): +def create_topic(_, _info, inp): with local_session() as session: # TODO: check user permissions to create topic for exact community # and actor is permitted to craete it @@ -65,7 +58,7 @@ async def create_topic(_, _info, inp): @mutation.field('update_topic') @login_required -async def update_topic(_, _info, inp): +def update_topic(_, _info, inp): slug = inp['slug'] with local_session() as session: topic = session.query(Topic).filter(Topic.slug == slug).first() @@ -81,7 +74,7 @@ async def update_topic(_, _info, inp): @mutation.field('delete_topic') @login_required -async def delete_topic(_, info, slug: str): +def delete_topic(_, info, slug: str): user_id = info.context['user_id'] with local_session() as session: t: Topic = session.query(Topic).filter(Topic.slug == slug).first()