From 1a685e458dd2c60c565c70bfd0090406ae1fb0a5 Mon Sep 17 00:00:00 2001 From: Untone Date: Thu, 18 Apr 2024 12:34:04 +0300 Subject: [PATCH] following-fix --- resolvers/author.py | 21 +++++++------ resolvers/follower.py | 41 +++++++++++++++++------- resolvers/reaction.py | 73 ++++++++++++++++++++++++------------------- services/cache.py | 10 +++--- 4 files changed, 86 insertions(+), 59 deletions(-) diff --git a/resolvers/author.py b/resolvers/author.py index 018a44c4..04993b62 100644 --- a/resolvers/author.py +++ b/resolvers/author.py @@ -59,14 +59,17 @@ async def get_author(_, _info, slug="", author_id=0): author_query = select(Author).filter( or_(Author.slug == slug, Author.id == author_id) ) - [found_author] = local_session().execute(author_query).first() - logger.debug(found_author) - if found_author: - logger.debug(f"found author id: {found_author.id}") - author_id = found_author.id if found_author.id else author_id - if author_id: - cached_result = await redis.execute("GET", f"author:{author_id}") - author_dict = json.loads(cached_result) if cached_result else None + lookup_result = local_session().execute(author_query).first() + if lookup_result: + [found_author] = lookup_result + logger.debug(found_author) + if found_author: + logger.debug(f"found author id: {found_author.id}") + author_id = found_author.id if found_author.id else author_id + if author_id: + cached_result = await redis.execute("GET", f"author:{author_id}") + if isinstance(cached_result, str): + author_dict = json.loads(cached_result) # update stat from db if not author_dict or not author_dict.get("stat"): @@ -180,7 +183,7 @@ async def get_author_follows(_, _info, slug="", user=None, author_id=0): # logger.debug(author) if author and isinstance(author, Author): # logger.debug(author.dict()) - author_id = author.id + author_id = author.id if not author_id else author_id rkey = f"author:{author_id}:follows-authors" logger.debug(f"getting {author_id} follows authors") cached = await redis.execute("GET", rkey) diff --git a/resolvers/follower.py b/resolvers/follower.py index 75d96826..19532d89 100644 --- a/resolvers/follower.py +++ b/resolvers/follower.py @@ -13,7 +13,7 @@ from orm.shout import Shout, ShoutReactionsFollower from orm.topic import Topic, TopicFollower from resolvers.stat import author_follows_authors, author_follows_topics, get_with_stat from services.auth import login_required -from services.cache import DEFAULT_FOLLOWS +from services.cache import DEFAULT_FOLLOWS, cache_follower from services.db import local_session from services.logger import root_logger as logger from services.notify import notify_follower @@ -24,21 +24,35 @@ from services.schema import mutation, query @mutation.field("follow") @login_required async def follow(_, info, what, slug): - follows = [] error = None user_id = info.context.get("user_id") if not user_id: return {"error": "unauthorized"} follower = local_session().query(Author).filter(Author.user == user_id).first() + if not follower: + return {"error": "cant find follower account"} + + entity = what.lower() + follows = [] + follows_str = await redis.execute("GET", f"author:{follower.id}:follows-{entity}s") + if isinstance(follows_str, str): + follows = json.loads(follows_str) + if not follower: return {"error": "cant find follower"} + if what == "AUTHOR": error = author_follow(follower.id, slug) if not error: - author = local_session().query(Author).where(Author.slug == slug).first() - if author: - await notify_follower(follower.dict(), author.id, "follow") + result = get_with_stat(select(Author).where(Author.slug == slug)) + if result: + [author] = result + if author: + await cache_follower(follower, author) + await notify_follower(follower.dict(), author.id, "follow") + if not any(a["id"] == author.id for a in follows): + follows.append(author.dict()) elif what == "TOPIC": error = topic_follow(follower.id, slug) @@ -53,10 +67,6 @@ async def follow(_, info, what, slug): if error: return {"error": error} - entity = what.lower() - follows_str = await redis.execute("GET", f"author:{follower.id}:follows-{entity}s") - if follows_str: - follows = json.loads(follows_str) return {f"{entity}s": follows} @@ -68,17 +78,24 @@ async def unfollow(_, info, what, slug): user_id = info.context.get("user_id") if not user_id: return {"error": "unauthorized"} + follower = local_session().query(Author).filter(Author.user == user_id).first() if not follower: - return {"error": "follower profile is not found"} + return {"error": "cant find follower account"} + if what == "AUTHOR": error = author_unfollow(follower.id, slug) # NOTE: after triggers should update cached stats if not error: logger.info(f"@{follower.slug} unfollowed @{slug}") author = local_session().query(Author).where(Author.slug == slug).first() - if author: + if isinstance(author, Author): + await cache_follower(follower, author, False) await notify_follower(follower.dict(), author.id, "unfollow") + for idx, item in enumerate(follows): + if item["id"] == author.id: + follows.pop(idx) # Remove the author_dict from the follows list + break elif what == "TOPIC": error = topic_unfollow(follower.id, slug) @@ -91,7 +108,7 @@ async def unfollow(_, info, what, slug): entity = what.lower() follows_str = await redis.execute("GET", f"author:{follower.id}:follows-{entity}s") - if follows_str: + if isinstance(follows_str, str): follows = json.loads(follows_str) return {"error": error, f"{entity}s": follows} diff --git a/resolvers/reaction.py b/resolvers/reaction.py index bb16f820..d4ae75d3 100644 --- a/resolvers/reaction.py +++ b/resolvers/reaction.py @@ -123,7 +123,7 @@ async def _create_reaction(session, shout, author, reaction): rdict = r.dict() # пересчет счетчика комментариев - if r.kind == ReactionKind.COMMENT.value: + if str(r.kind) == ReactionKind.COMMENT.value: await update_author_stat(author) # collaborative editing @@ -151,7 +151,7 @@ async def _create_reaction(session, shout, author, reaction): pass # обновление счетчика комментариев в кеше - if r.kind == ReactionKind.COMMENT.value: + if str(r.kind) == ReactionKind.COMMENT.value: await update_author_stat(author) rdict["shout"] = shout.dict() @@ -215,7 +215,6 @@ async def create_reaction(_, info, reaction): if shout and author: reaction["created_by"] = author.id kind = reaction.get("kind") - shout_id = shout.id if not kind and isinstance(reaction.get("body"), str): kind = ReactionKind.COMMENT.value @@ -260,42 +259,50 @@ async def update_reaction(_, info, reaction): reaction_query = reaction_query.group_by(Reaction.id) try: - [r, reacted_stat, commented_stat, likes_stat, dislikes_stat, _l] = ( - session.execute(reaction_query).unique().first() - ) + result = session.execute(reaction_query).unique().first() + if result: + [ + r, + reacted_stat, + commented_stat, + likes_stat, + dislikes_stat, + last_comment, + ] = result + if not r: + return {"error": "invalid reaction id"} - if not r: - return {"error": "invalid reaction id"} + author = ( + session.query(Author).filter(Author.user == user_id).first() + ) + if author: + if r.created_by != author.id and "editor" not in roles: + return {"error": "access denied"} - author = session.query(Author).filter(Author.user == user_id).first() - if author: - if r.created_by != author.id and "editor" not in roles: - return {"error": "access denied"} + body = reaction.get("body") + if body: + r.body = body + r.updated_at = int(time.time()) - body = reaction.get("body") - if body: - r.body = body - r.updated_at = int(time.time()) + if r.kind != reaction["kind"]: + # Определение изменения мнения может быть реализовано здесь + pass - if r.kind != reaction["kind"]: - # Определение изменения мнения может быть реализовано здесь - pass + Reaction.update(r, reaction) + session.add(r) + session.commit() - Reaction.update(r, reaction) - session.add(r) - session.commit() + r.stat = { + "reacted": reacted_stat, + "commented": commented_stat, + "rating": int(likes_stat or 0) - int(dislikes_stat or 0), + } - r.stat = { - "reacted": reacted_stat, - "commented": commented_stat, - "rating": int(likes_stat or 0) - int(dislikes_stat or 0), - } + await notify_reaction(r.dict(), "update") - await notify_reaction(r.dict(), "update") - - return {"reaction": r} - else: - return {"error": "not authorized"} + return {"reaction": r} + else: + return {"error": "not authorized"} except Exception: import traceback @@ -323,7 +330,7 @@ async def delete_reaction(_, info, reaction_id: int): session.commit() # обновление счетчика комментариев в кеше - if r.kind == ReactionKind.COMMENT.value: + if str(r.kind) == ReactionKind.COMMENT.value: await update_author_stat(author) await notify_reaction(reaction_dict, "delete") diff --git a/services/cache.py b/services/cache.py index 95cf8157..354c2c89 100644 --- a/services/cache.py +++ b/services/cache.py @@ -29,7 +29,7 @@ async def cache_author(author: dict): follower_follows_authors_str = await redis.execute( "GET", f'author:{author.get("id")}:follows-authors' ) - if follower_follows_authors_str: + if isinstance(follower_follows_authors_str, str): follower_follows_authors = json.loads(follower_follows_authors_str) c = 0 for old_author in follower_follows_authors: @@ -46,7 +46,7 @@ async def cache_author(author: dict): "GET", f'author:{author.get("id")}:follows-authors' ) follows_authors = [] - if follows_str: + if isinstance(follows_str, str): follows_authors = json.loads(follows_str) if isinstance(follows_authors, list): for followed_author in follows_authors: @@ -54,7 +54,7 @@ async def cache_author(author: dict): followed_author_followers_str = await redis.execute( "GET", f'author:{author.get("id")}:followers' ) - if followed_author_followers_str: + if isinstance(followed_author_followers_str, str): followed_author_followers = json.loads(followed_author_followers_str) c = 0 for old_follower in followed_author_followers: @@ -90,7 +90,7 @@ async def cache_follows(follower: Author, entity_type: str, entity, is_insert=Tr # update follower's stats everywhere author_str = await redis.execute("GET", f"author:{follower.id}") - if author_str: + if isinstance(author_str, str): author = json.loads(author_str) author["stat"][f"{entity_type}s"] = len(updated_data) await cache_author(author) @@ -114,7 +114,7 @@ async def cache_follower(follower: Author, author: Author, is_insert=True): payload = json.dumps(updated_followers, cls=CustomJSONEncoder) await redis.execute("SET", redis_key, payload) author_str = await redis.execute("GET", f"author:{follower.id}") - if author_str: + if isinstance(author_str, str): author = json.loads(author_str) author["stat"]["followers"] = len(updated_followers) await cache_author(author)