get-author-followers-fix
All checks were successful
Deploy on push / deploy (push) Successful in 2m10s

This commit is contained in:
Untone 2024-02-25 11:27:08 +03:00
parent b2357e0afb
commit efa6ac7d60
6 changed files with 83 additions and 122 deletions

View File

@ -1,19 +1,13 @@
import json import json
import time import time
from sqlalchemy import desc, select, or_, distinct, func from sqlalchemy import desc, select, or_, and_
from sqlalchemy.orm import aliased from sqlalchemy.orm import aliased
from orm.author import Author, AuthorFollower from orm.author import Author, AuthorFollower
from orm.shout import ShoutAuthor, ShoutTopic from orm.shout import ShoutAuthor, ShoutTopic
from orm.topic import Topic from orm.topic import Topic
from resolvers.follower import query_follows from resolvers.stat import get_with_stat, author_follows_authors, author_follows_topics
from resolvers.stat import (
get_authors_with_stat,
execute_with_ministat,
author_follows_authors,
author_follows_topics,
)
from services.auth import login_required from services.auth import login_required
from services.db import local_session from services.db import local_session
from services.rediscache import redis from services.rediscache import redis
@ -52,13 +46,13 @@ def get_author(_, _info, slug='', author_id=None):
if author_id: if author_id:
q = select(Author).where(Author.id == author_id) q = select(Author).where(Author.id == author_id)
[author] = get_authors_with_stat(q, ratings=True) [author] = get_with_stat(q)
except Exception as exc: except Exception as exc:
logger.error(exc) logger.error(exc)
return author return author
async def get_author_by_user_id(user_id: str, ratings=False): async def get_author_by_user_id(user_id: str):
redis_key = f'user:{user_id}:author' redis_key = f'user:{user_id}:author'
author = None author = None
try: try:
@ -72,7 +66,7 @@ async def get_author_by_user_id(user_id: str, ratings=False):
logger.info(f'getting author id for {user_id}') logger.info(f'getting author id for {user_id}')
q = select(Author).filter(Author.user == user_id) q = select(Author).filter(Author.user == user_id)
[author] = get_authors_with_stat(q, ratings) [author] = get_with_stat(q)
except Exception as exc: except Exception as exc:
logger.error(exc) logger.error(exc)
return author return author
@ -80,7 +74,7 @@ async def get_author_by_user_id(user_id: str, ratings=False):
@query.field('get_author_id') @query.field('get_author_id')
async def get_author_id(_, _info, user: str): async def get_author_id(_, _info, user: str):
return await get_author_by_user_id(user, ratings=True) return await get_author_by_user_id(user)
@query.field('load_authors_by') @query.field('load_authors_by')
@ -111,7 +105,7 @@ def load_authors_by(_, _info, by, limit, offset):
q = q.limit(limit).offset(offset) q = q.limit(limit).offset(offset)
authors = get_authors_with_stat(q, ratings=False) authors = get_with_stat(q)
return authors return authors
@ -127,8 +121,13 @@ def get_author_follows(_, _info, slug='', user=None, author_id=None):
) )
author_id = author_id_result[0] if author_id_result else None author_id = author_id_result[0] if author_id_result else None
if author_id: if author_id:
follows = query_follows(author_id) topics = author_follows_topics(author_id)
return follows authors = author_follows_authors(author_id)
return {
'topics': topics,
'authors': authors,
'communities': [{'id': 1, 'name': 'Дискурс', 'slug': 'discours'}],
}
else: else:
raise ValueError('Author not found') raise ValueError('Author not found')
@ -144,7 +143,7 @@ def get_author_follows_topics(_, _info, slug='', user=None, author_id=None):
) )
author_id = author_id_result[0] if author_id_result else None author_id = author_id_result[0] if author_id_result else None
if author_id: if author_id:
follows = author_follows_authors(author_id) follows = author_follows_topics(author_id)
return follows return follows
else: else:
raise ValueError('Author not found') raise ValueError('Author not found')
@ -161,7 +160,7 @@ def get_author_follows_authors(_, _info, slug='', user=None, author_id=None):
) )
author_id = author_id_result[0] if author_id_result else None author_id = author_id_result[0] if author_id_result else None
if author_id: if author_id:
follows = author_follows_topics(author_id) follows = author_follows_authors(author_id)
return follows return follows
else: else:
raise ValueError('Author not found') raise ValueError('Author not found')
@ -177,26 +176,25 @@ def create_author(user_id: str, slug: str, name: str = ''):
@query.field('get_author_followers') @query.field('get_author_followers')
def get_author_followers(_, _info, slug: str): def get_author_followers(_, _info, slug: str):
author_alias = aliased(Author) try:
alias_author_followers = aliased(AuthorFollower) with local_session() as session:
alias_author_authors = aliased(AuthorFollower) author_id_result = (
alias_author_follower_followers = aliased(AuthorFollower) session.query(Author.id).filter(Author.slug == slug).first()
alias_shout_author = aliased(ShoutAuthor) )
author_id = author_id_result[0] if author_id_result else None
q = ( author_alias = aliased(Author)
select(author_alias) author_follower_alias = aliased(AuthorFollower, name='af')
.join(alias_author_authors, alias_author_authors.follower == author_alias.id)
.join(alias_author_followers, alias_author_followers.author == author_alias.id)
.filter(author_alias.slug == slug)
.add_columns(
func.count(distinct(alias_shout_author.shout)).label('shouts_stat'),
func.count(distinct(alias_author_authors.author)).label('authors_stat'),
func.count(distinct(alias_author_follower_followers.follower)).label(
'followers_stat'
),
)
.outerjoin(alias_shout_author, author_alias.id == alias_shout_author.author)
.group_by(author_alias.id)
)
return execute_with_ministat(q) q = select(author_alias).join(
author_follower_alias,
and_(
author_follower_alias.author == author_id,
author_follower_alias.follower == author_alias.id,
),
)
return get_with_stat(q)
except Exception as exc:
logger.error(exc)
return []

View File

@ -13,7 +13,7 @@ from orm.shout import Shout, ShoutReactionsFollower
from orm.topic import Topic, TopicFollower from orm.topic import Topic, TopicFollower
from resolvers.community import community_follow, community_unfollow from resolvers.community import community_follow, community_unfollow
from resolvers.topic import topic_follow, topic_unfollow from resolvers.topic import topic_follow, topic_unfollow
from resolvers.stat import get_authors_with_stat, query_follows from resolvers.stat import get_with_stat, query_follows
from services.auth import login_required from services.auth import login_required
from services.db import local_session from services.db import local_session
from services.event_listeners import DEFAULT_FOLLOWS from services.event_listeners import DEFAULT_FOLLOWS
@ -190,7 +190,7 @@ def get_topic_followers(_, _info, slug: str, topic_id: int) -> List[Author]:
.join(Topic, Topic.id == TopicFollower.topic) .join(Topic, Topic.id == TopicFollower.topic)
.filter(or_(Topic.slug == slug, Topic.id == topic_id)) .filter(or_(Topic.slug == slug, Topic.id == topic_id))
) )
return get_authors_with_stat(q) return get_with_stat(q)
@query.field('get_shout_followers') @query.field('get_shout_followers')

View File

@ -103,36 +103,31 @@ def count_author_shouts_rating(session, author_id) -> int:
return shouts_likes - shouts_dislikes return shouts_likes - shouts_dislikes
def load_author_ratings(author: Author): def load_author_ratings(session, author: Author):
with local_session() as session: comments_count = (
comments_count = ( session.query(Reaction)
session.query(Reaction) .filter(
.filter( and_(
and_( Reaction.created_by == author.id,
Reaction.created_by == author.id, Reaction.kind == ReactionKind.COMMENT.value,
Reaction.kind == ReactionKind.COMMENT.value, Reaction.deleted_at.is_(None),
Reaction.deleted_at.is_(None),
)
) )
.count()
) )
likes_count = ( .count()
session.query(AuthorRating) )
.filter(and_(AuthorRating.author == author.id, AuthorRating.plus.is_(True))) likes_count = (
.count() session.query(AuthorRating)
) .filter(and_(AuthorRating.author == author.id, AuthorRating.plus.is_(True)))
dislikes_count = ( .count()
session.query(AuthorRating) )
.filter( dislikes_count = (
and_(AuthorRating.author == author.id, AuthorRating.plus.is_not(True)) session.query(AuthorRating)
) .filter(and_(AuthorRating.author == author.id, AuthorRating.plus.is_not(True)))
.count() .count()
) )
author.stat = author.stat if isinstance(author.stat, dict) else {} author.stat = author.stat if isinstance(author.stat, dict) else {}
author.stat['rating'] = likes_count - dislikes_count author.stat['rating'] = likes_count - dislikes_count
author.stat['rating_shouts'] = count_author_shouts_rating(session, author.id) author.stat['rating_shouts'] = count_author_shouts_rating(session, author.id)
author.stat['rating_comments'] = count_author_comments_rating( author.stat['rating_comments'] = count_author_comments_rating(session, author.id)
session, author.id author.stat['commented'] = comments_count
) return author
author.stat['commented'] = comments_count
return author

View File

@ -68,9 +68,10 @@ def add_author_stat_columns(q):
return q return q
def execute_with_ministat(q): def get_with_stat(q):
q = add_author_stat_columns(q)
records = [] records = []
logger.debug(f'execute with ministat: {q}') logger.debug(q.replace('\n', ' '))
with local_session() as session: with local_session() as session:
for [entity, shouts_stat, authors_stat, followers_stat] in session.execute(q): for [entity, shouts_stat, authors_stat, followers_stat] in session.execute(q):
entity.stat = { entity.stat = {
@ -78,27 +79,13 @@ def execute_with_ministat(q):
'authors': authors_stat, 'authors': authors_stat,
'followers': followers_stat, 'followers': followers_stat,
} }
if q.startswith('SELECT author'):
load_author_ratings(session, entity)
records.append(entity) records.append(entity)
return records return records
def get_authors_with_stat(q, ratings=False):
q = add_author_stat_columns(q)
authors = execute_with_ministat(q)
if ratings:
authors_with_ratings = []
for author in authors:
authors_with_ratings.append(load_author_ratings(author))
return authors_with_ratings
return authors
def get_topics_with_stat(q):
q = add_topic_stat_columns(q)
return execute_with_ministat(q)
def author_follows_authors(author_id: int): def author_follows_authors(author_id: int):
af = aliased(AuthorFollower, name='af') af = aliased(AuthorFollower, name='af')
q = ( q = (
@ -106,8 +93,7 @@ def author_follows_authors(author_id: int):
.select_from(join(Author, af, Author.id == af.author)) .select_from(join(Author, af, Author.id == af.author))
.where(af.follower == author_id) .where(af.follower == author_id)
) )
q = add_author_stat_columns(q) return get_with_stat(q)
return execute_with_ministat(q)
def author_follows_topics(author_id: int): def author_follows_topics(author_id: int):
@ -116,22 +102,4 @@ def author_follows_topics(author_id: int):
.select_from(join(Topic, TopicFollower, Topic.id == TopicFollower.topic)) .select_from(join(Topic, TopicFollower, Topic.id == TopicFollower.topic))
.where(TopicFollower.follower == author_id) .where(TopicFollower.follower == author_id)
) )
return get_with_stat(q)
q = add_topic_stat_columns(q)
return execute_with_ministat(q)
def query_follows(author_id: int):
try:
topics = author_follows_topics(author_id)
authors = author_follows_authors(author_id)
return {
'topics': topics,
'authors': authors,
'communities': [{'id': 1, 'name': 'Дискурс', 'slug': 'discours'}],
}
except Exception as e:
import traceback
logger.debug(traceback.format_exc())
raise Exception(e)

View File

@ -3,7 +3,7 @@ from sqlalchemy import and_, distinct, func, select
from orm.author import Author from orm.author import Author
from orm.shout import ShoutTopic from orm.shout import ShoutTopic
from orm.topic import Topic, TopicFollower from orm.topic import Topic, TopicFollower
from resolvers.stat import get_topics_with_stat from resolvers.stat import get_with_stat
from services.auth import login_required from services.auth import login_required
from services.db import local_session from services.db import local_session
from services.schema import mutation, query from services.schema import mutation, query
@ -12,13 +12,13 @@ from services.logger import root_logger as logger
@query.field('get_topics_all') @query.field('get_topics_all')
def get_topics_all(_, _info): def get_topics_all(_, _info):
return get_topics_with_stat(select(Topic)) return get_with_stat(select(Topic))
@query.field('get_topics_by_community') @query.field('get_topics_by_community')
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 = select(Topic).where(Topic.community == community_id)
return get_topics_with_stat(q) return get_with_stat(q)
@query.field('get_topics_by_author') @query.field('get_topics_by_author')
@ -31,13 +31,13 @@ def get_topics_by_author(_, _info, author_id=0, slug='', user=''):
elif user: elif user:
q = q.join(Author).where(Author.user == user) q = q.join(Author).where(Author.user == user)
return get_topics_with_stat(q) return get_with_stat(q)
@query.field('get_topic') @query.field('get_topic')
def get_topic(_, _info, slug: str): def get_topic(_, _info, slug: str):
q = select(Topic).filter(Topic.slug == slug) q = select(Topic).filter(Topic.slug == slug)
topics = get_topics_with_stat(q) topics = get_with_stat(q)
if topics: if topics:
return topics[0] return topics[0]

View File

@ -7,7 +7,7 @@ from orm.author import Author, AuthorFollower
from orm.reaction import Reaction from orm.reaction import Reaction
from orm.shout import ShoutAuthor, Shout from orm.shout import ShoutAuthor, Shout
from orm.topic import Topic, TopicFollower from orm.topic import Topic, TopicFollower
from resolvers.stat import get_authors_with_stat, get_topics_with_stat from resolvers.stat import get_with_stat
from services.rediscache import redis from services.rediscache import redis
@ -46,7 +46,7 @@ def after_shouts_update(mapper, connection, shout: Shout):
.where(ShoutAuthor.shout == shout.id) .where(ShoutAuthor.shout == shout.id)
.union(select(Author).where(exists(subquery))) .union(select(Author).where(exists(subquery)))
) )
authors = get_authors_with_stat(authors_query, ratings=True) authors = get_with_stat(authors_query)
for author in authors: for author in authors:
asyncio.create_task(update_author_cache(author)) asyncio.create_task(update_author_cache(author))
@ -61,7 +61,7 @@ def after_reaction_insert(mapper, connection, reaction: Reaction):
) )
author_query = author_subquery.union(replied_author_subquery) author_query = author_subquery.union(replied_author_subquery)
authors = get_authors_with_stat(author_query, ratings=True) authors = get_with_stat(author_query)
for author in authors: for author in authors:
asyncio.create_task(update_author_cache(author)) asyncio.create_task(update_author_cache(author))
@ -128,9 +128,9 @@ async def handle_author_follower_change(
connection, author_id: int, follower_id: int, is_insert: bool connection, author_id: int, follower_id: int, is_insert: bool
): ):
author_query = select(Author).filter(Author.id == author_id) author_query = select(Author).filter(Author.id == author_id)
[author] = get_authors_with_stat(author_query, ratings=True) [author] = get_with_stat(author_query)
follower_query = select(Author).filter(Author.id == follower_id) follower_query = select(Author).filter(Author.id == follower_id)
follower = get_authors_with_stat(follower_query, ratings=True) follower = get_with_stat(follower_query)
if follower and author: if follower and author:
_ = asyncio.create_task(update_author_cache(author)) _ = asyncio.create_task(update_author_cache(author))
_ = asyncio.create_task(update_author_cache(follower)) _ = asyncio.create_task(update_author_cache(follower))
@ -154,10 +154,10 @@ async def handle_topic_follower_change(
connection, topic_id: int, follower_id: int, is_insert: bool connection, topic_id: int, follower_id: int, is_insert: bool
): ):
q = select(Topic).filter(Topic.id == topic_id) q = select(Topic).filter(Topic.id == topic_id)
topics = get_topics_with_stat(q) topics = get_with_stat(q)
topic = topics[0] topic = topics[0]
follower_query = select(Author).filter(Author.id == follower_id) follower_query = select(Author).filter(Author.id == follower_id)
follower = get_authors_with_stat(follower_query, ratings=True) follower = get_with_stat(follower_query)
if follower and topic: if follower and topic:
_ = asyncio.create_task(update_author_cache(follower)) _ = asyncio.create_task(update_author_cache(follower))
await update_follows_for_user( await update_follows_for_user(