following-fix

This commit is contained in:
Untone 2024-04-18 12:34:04 +03:00
parent 47bc3adb69
commit 1a685e458d
4 changed files with 86 additions and 59 deletions

View File

@ -59,14 +59,17 @@ async def get_author(_, _info, slug="", author_id=0):
author_query = select(Author).filter( author_query = select(Author).filter(
or_(Author.slug == slug, Author.id == author_id) or_(Author.slug == slug, Author.id == author_id)
) )
[found_author] = local_session().execute(author_query).first() lookup_result = local_session().execute(author_query).first()
logger.debug(found_author) if lookup_result:
if found_author: [found_author] = lookup_result
logger.debug(f"found author id: {found_author.id}") logger.debug(found_author)
author_id = found_author.id if found_author.id else author_id if found_author:
if author_id: logger.debug(f"found author id: {found_author.id}")
cached_result = await redis.execute("GET", f"author:{author_id}") author_id = found_author.id if found_author.id else author_id
author_dict = json.loads(cached_result) if cached_result else None 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 # update stat from db
if not author_dict or not author_dict.get("stat"): 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) # logger.debug(author)
if author and isinstance(author, Author): if author and isinstance(author, Author):
# logger.debug(author.dict()) # 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" rkey = f"author:{author_id}:follows-authors"
logger.debug(f"getting {author_id} follows authors") logger.debug(f"getting {author_id} follows authors")
cached = await redis.execute("GET", rkey) cached = await redis.execute("GET", rkey)

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.stat import author_follows_authors, author_follows_topics, get_with_stat from resolvers.stat import author_follows_authors, author_follows_topics, get_with_stat
from services.auth import login_required 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.db import local_session
from services.logger import root_logger as logger from services.logger import root_logger as logger
from services.notify import notify_follower from services.notify import notify_follower
@ -24,21 +24,35 @@ from services.schema import mutation, query
@mutation.field("follow") @mutation.field("follow")
@login_required @login_required
async def follow(_, info, what, slug): async def follow(_, info, what, slug):
follows = []
error = None error = None
user_id = info.context.get("user_id") user_id = info.context.get("user_id")
if not user_id: if not user_id:
return {"error": "unauthorized"} return {"error": "unauthorized"}
follower = local_session().query(Author).filter(Author.user == user_id).first() 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: if not follower:
return {"error": "cant find follower"} return {"error": "cant find follower"}
if what == "AUTHOR": if what == "AUTHOR":
error = author_follow(follower.id, slug) error = author_follow(follower.id, slug)
if not error: if not error:
author = local_session().query(Author).where(Author.slug == slug).first() result = get_with_stat(select(Author).where(Author.slug == slug))
if author: if result:
await notify_follower(follower.dict(), author.id, "follow") [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": elif what == "TOPIC":
error = topic_follow(follower.id, slug) error = topic_follow(follower.id, slug)
@ -53,10 +67,6 @@ async def follow(_, info, what, slug):
if error: if error:
return {"error": 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} return {f"{entity}s": follows}
@ -68,17 +78,24 @@ async def unfollow(_, info, what, slug):
user_id = info.context.get("user_id") user_id = info.context.get("user_id")
if not user_id: if not user_id:
return {"error": "unauthorized"} return {"error": "unauthorized"}
follower = local_session().query(Author).filter(Author.user == user_id).first() follower = local_session().query(Author).filter(Author.user == user_id).first()
if not follower: if not follower:
return {"error": "follower profile is not found"} return {"error": "cant find follower account"}
if what == "AUTHOR": if what == "AUTHOR":
error = author_unfollow(follower.id, slug) error = author_unfollow(follower.id, slug)
# NOTE: after triggers should update cached stats # NOTE: after triggers should update cached stats
if not error: if not error:
logger.info(f"@{follower.slug} unfollowed @{slug}") logger.info(f"@{follower.slug} unfollowed @{slug}")
author = local_session().query(Author).where(Author.slug == slug).first() 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") 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": elif what == "TOPIC":
error = topic_unfollow(follower.id, slug) error = topic_unfollow(follower.id, slug)
@ -91,7 +108,7 @@ async def unfollow(_, info, what, slug):
entity = what.lower() entity = what.lower()
follows_str = await redis.execute("GET", f"author:{follower.id}:follows-{entity}s") 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) follows = json.loads(follows_str)
return {"error": error, f"{entity}s": follows} return {"error": error, f"{entity}s": follows}

View File

@ -123,7 +123,7 @@ async def _create_reaction(session, shout, author, reaction):
rdict = r.dict() rdict = r.dict()
# пересчет счетчика комментариев # пересчет счетчика комментариев
if r.kind == ReactionKind.COMMENT.value: if str(r.kind) == ReactionKind.COMMENT.value:
await update_author_stat(author) await update_author_stat(author)
# collaborative editing # collaborative editing
@ -151,7 +151,7 @@ async def _create_reaction(session, shout, author, reaction):
pass pass
# обновление счетчика комментариев в кеше # обновление счетчика комментариев в кеше
if r.kind == ReactionKind.COMMENT.value: if str(r.kind) == ReactionKind.COMMENT.value:
await update_author_stat(author) await update_author_stat(author)
rdict["shout"] = shout.dict() rdict["shout"] = shout.dict()
@ -215,7 +215,6 @@ async def create_reaction(_, info, reaction):
if shout and author: if shout and author:
reaction["created_by"] = author.id reaction["created_by"] = author.id
kind = reaction.get("kind") kind = reaction.get("kind")
shout_id = shout.id
if not kind and isinstance(reaction.get("body"), str): if not kind and isinstance(reaction.get("body"), str):
kind = ReactionKind.COMMENT.value kind = ReactionKind.COMMENT.value
@ -260,42 +259,50 @@ async def update_reaction(_, info, reaction):
reaction_query = reaction_query.group_by(Reaction.id) reaction_query = reaction_query.group_by(Reaction.id)
try: try:
[r, reacted_stat, commented_stat, likes_stat, dislikes_stat, _l] = ( result = session.execute(reaction_query).unique().first()
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: author = (
return {"error": "invalid reaction id"} 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() body = reaction.get("body")
if author: if body:
if r.created_by != author.id and "editor" not in roles: r.body = body
return {"error": "access denied"} r.updated_at = int(time.time())
body = reaction.get("body") if r.kind != reaction["kind"]:
if body: # Определение изменения мнения может быть реализовано здесь
r.body = body pass
r.updated_at = int(time.time())
if r.kind != reaction["kind"]: Reaction.update(r, reaction)
# Определение изменения мнения может быть реализовано здесь session.add(r)
pass session.commit()
Reaction.update(r, reaction) r.stat = {
session.add(r) "reacted": reacted_stat,
session.commit() "commented": commented_stat,
"rating": int(likes_stat or 0) - int(dislikes_stat or 0),
}
r.stat = { await notify_reaction(r.dict(), "update")
"reacted": reacted_stat,
"commented": commented_stat,
"rating": int(likes_stat or 0) - int(dislikes_stat or 0),
}
await notify_reaction(r.dict(), "update") return {"reaction": r}
else:
return {"reaction": r} return {"error": "not authorized"}
else:
return {"error": "not authorized"}
except Exception: except Exception:
import traceback import traceback
@ -323,7 +330,7 @@ async def delete_reaction(_, info, reaction_id: int):
session.commit() session.commit()
# обновление счетчика комментариев в кеше # обновление счетчика комментариев в кеше
if r.kind == ReactionKind.COMMENT.value: if str(r.kind) == ReactionKind.COMMENT.value:
await update_author_stat(author) await update_author_stat(author)
await notify_reaction(reaction_dict, "delete") await notify_reaction(reaction_dict, "delete")

View File

@ -29,7 +29,7 @@ async def cache_author(author: dict):
follower_follows_authors_str = await redis.execute( follower_follows_authors_str = await redis.execute(
"GET", f'author:{author.get("id")}:follows-authors' "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) follower_follows_authors = json.loads(follower_follows_authors_str)
c = 0 c = 0
for old_author in follower_follows_authors: 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' "GET", f'author:{author.get("id")}:follows-authors'
) )
follows_authors = [] follows_authors = []
if follows_str: if isinstance(follows_str, str):
follows_authors = json.loads(follows_str) follows_authors = json.loads(follows_str)
if isinstance(follows_authors, list): if isinstance(follows_authors, list):
for followed_author in follows_authors: for followed_author in follows_authors:
@ -54,7 +54,7 @@ async def cache_author(author: dict):
followed_author_followers_str = await redis.execute( followed_author_followers_str = await redis.execute(
"GET", f'author:{author.get("id")}:followers' "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) followed_author_followers = json.loads(followed_author_followers_str)
c = 0 c = 0
for old_follower in followed_author_followers: 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 # update follower's stats everywhere
author_str = await redis.execute("GET", f"author:{follower.id}") author_str = await redis.execute("GET", f"author:{follower.id}")
if author_str: if isinstance(author_str, str):
author = json.loads(author_str) author = json.loads(author_str)
author["stat"][f"{entity_type}s"] = len(updated_data) author["stat"][f"{entity_type}s"] = len(updated_data)
await cache_author(author) 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) payload = json.dumps(updated_followers, cls=CustomJSONEncoder)
await redis.execute("SET", redis_key, payload) await redis.execute("SET", redis_key, payload)
author_str = await redis.execute("GET", f"author:{follower.id}") author_str = await redis.execute("GET", f"author:{follower.id}")
if author_str: if isinstance(author_str, str):
author = json.loads(author_str) author = json.loads(author_str)
author["stat"]["followers"] = len(updated_followers) author["stat"]["followers"] = len(updated_followers)
await cache_author(author) await cache_author(author)