From 228cdf21e91253d9d58b35d963cb9c0b3fba811b Mon Sep 17 00:00:00 2001 From: tonyrewin Date: Sat, 26 Nov 2022 03:55:45 +0300 Subject: [PATCH] morning-fixes --- resolvers/zine/load.py | 100 ++++++++++++++++++-------------------- resolvers/zine/profile.py | 24 +++++++-- resolvers/zine/topics.py | 7 +-- services/search.py | 8 +-- services/stat/reacted.py | 15 +----- 5 files changed, 74 insertions(+), 80 deletions(-) diff --git a/resolvers/zine/load.py b/resolvers/zine/load.py index fe13d431..b883036e 100644 --- a/resolvers/zine/load.py +++ b/resolvers/zine/load.py @@ -5,23 +5,32 @@ from sqlalchemy.sql.expression import desc, asc, select, case from base.orm import local_session from base.resolvers import query from orm import ViewedEntry -from orm.shout import Shout, ShoutAuthor +from orm.shout import Shout from orm.reaction import Reaction, ReactionKind -from services.stat.reacted import ReactedStorage +from services.zine.shoutauthor import ShoutAuthorStorage +from services.stat.viewed import ViewedStorage -def add_rating_column(q): - return q.join(Reaction).add_columns(sa.func.sum(case( - (Reaction.kind == ReactionKind.AGREE, 1), - (Reaction.kind == ReactionKind.DISAGREE, -1), - (Reaction.kind == ReactionKind.PROOF, 1), - (Reaction.kind == ReactionKind.DISPROOF, -1), - (Reaction.kind == ReactionKind.ACCEPT, 1), - (Reaction.kind == ReactionKind.REJECT, -1), - (Reaction.kind == ReactionKind.LIKE, 1), - (Reaction.kind == ReactionKind.DISLIKE, -1), - else_=0 - )).label('rating')) +def calc_reactions(q): + return q.join(Reaction).add_columns( + sa.func.sum(case( + (Reaction.kind == ReactionKind.AGREE, 1), + (Reaction.kind == ReactionKind.DISAGREE, -1), + (Reaction.kind == ReactionKind.PROOF, 1), + (Reaction.kind == ReactionKind.DISPROOF, -1), + (Reaction.kind == ReactionKind.ACCEPT, 1), + (Reaction.kind == ReactionKind.REJECT, -1), + (Reaction.kind == ReactionKind.LIKE, 1), + (Reaction.kind == ReactionKind.DISLIKE, -1), + else_=0) + ).label('rating'), + sa.func.sum( + case( + (Reaction.body.is_not(None), 1), + else_=0 + ) + ).label('commented') + ) def apply_filters(q, filters, user=None): @@ -50,30 +59,30 @@ def apply_filters(q, filters, user=None): async def load_shout(_, info, slug): with local_session() as session: q = select(Shout).options( - # TODO add cation joinedload(Shout.authors), joinedload(Shout.topics), ) - q = add_rating_column(q) + q = calc_reactions(q) q = q.filter( Shout.slug == slug ).filter( Shout.deletedAt.is_(None) ).group_by(Shout.id) - [shout, rating] = session.execute(q).unique().one() - - shout.stat = await ReactedStorage.get_shout_stat(shout.slug, rating) + [shout, rating, commented] = session.execute(q).unique().one() + for a in shout.authors: + a.caption = await ShoutAuthorStorage.get_author_caption(a.slug) + viewed = await ViewedStorage.get_shout(shout.slug) + shout.stat = { + "rating": rating, + "viewed": viewed, + "commented": commented, + # "reacted": reacted + } return shout -def map_result_item(result_item): - shout = result_item[0] - shout.rating = result_item[1] - return shout - - @query.field("loadShouts") async def load_shouts_by(_, info, options): """ @@ -104,7 +113,7 @@ async def load_shouts_by(_, info, options): ) user = info.context["request"].user q = apply_filters(q, options.get("filters"), user) - q = add_rating_column(q) + q = calc_reactions(q) o = options.get("order_by") if o: @@ -127,37 +136,22 @@ async def load_shouts_by(_, info, options): order_by_desc = True if options.get('order_by_desc') is None else options.get('order_by_desc') - with_author_captions = False if options.get('with_author_captions') is None else options.get('with_author_captions') - query_order_by = desc(order_by) if order_by_desc else asc(order_by) offset = options.get("offset", 0) limit = options.get("limit", 10) q = q.group_by(Shout.id).order_by(query_order_by).limit(limit).offset(offset) + shouts = [] with local_session() as session: - shouts = list(map(map_result_item, session.execute(q).unique())) - - for shout in shouts: - shout.stat = await ReactedStorage.get_shout_stat(shout.slug, shout.rating) - del shout.rating - - author_captions = {} - - if with_author_captions: - author_captions_result = session.query(ShoutAuthor).where( - ShoutAuthor.shout.in_(map(lambda s: s.slug, shouts))).all() - - for author_captions_result_item in author_captions_result: - if author_captions.get(author_captions_result_item.shout) is None: - author_captions[author_captions_result_item.shout] = {} - - author_captions[ - author_captions_result_item.shout - ][ - author_captions_result_item.user - ] = author_captions_result_item.caption - - for author in shout.authors: - author.caption = author_captions[shout.slug][author.slug] - + for [shout, rating, commented] in session.execute(q).unique(): + shout.stat = { + "rating": rating, + "viewed": await ViewedStorage.get_shout(shout.slug), + "commented": commented, + # "reacted": reacted + } + # NOTE: no need authors captions in arrays + # for author in shout.authors: + # author.caption = await ShoutAuthorStorage.get_author_caption(shout.slug, author.slug) + shouts.append(shout) return shouts diff --git a/resolvers/zine/profile.py b/resolvers/zine/profile.py index d0e9d3af..9768a443 100644 --- a/resolvers/zine/profile.py +++ b/resolvers/zine/profile.py @@ -10,7 +10,6 @@ from orm.reaction import Reaction from orm.shout import ShoutAuthor from orm.topic import Topic, TopicFollower from orm.user import AuthorFollower, Role, User, UserRating, UserRole -from services.stat.reacted import ReactedStorage from services.stat.topicstat import TopicStat # from .community import followed_communities @@ -23,13 +22,12 @@ async def user_subscriptions(slug: str): "unread": await get_total_unread_counter(slug), # unread inbox messages counter "topics": [t.slug for t in await followed_topics(slug)], # followed topics slugs "authors": [a.slug for a in await followed_authors(slug)], # followed authors slugs - "reactions": await ReactedStorage.get_shouts_by_author(slug), + "reactions": await followed_reactions(slug) # "communities": [c.slug for c in followed_communities(slug)], # communities } async def get_author_stat(slug): - # TODO: implement author stat with local_session() as session: return { "shouts": session.query(ShoutAuthor).where(ShoutAuthor.user == slug).count(), @@ -41,11 +39,29 @@ async def get_author_stat(slug): ).where( Reaction.createdBy == slug ).filter( - func.length(Reaction.body) > 0 + Reaction.body.is_not(None) ).count() } +# @query.field("userFollowedDiscussions") +@login_required +async def followed_discussions(_, info, slug) -> List[Topic]: + return await followed_reactions(slug) + + +async def followed_reactions(slug): + with local_session() as session: + user = session.query(User).where(User.slug == slug).first() + return session.query( + Reaction.shout + ).where( + Reaction.author == slug + ).filter( + Reaction.createdAt > user.lastSeen + ).all() + + @query.field("userFollowedTopics") @login_required async def get_followed_topics(_, info, slug) -> List[Topic]: diff --git a/resolvers/zine/topics.py b/resolvers/zine/topics.py index 6edb3c09..8a913627 100644 --- a/resolvers/zine/topics.py +++ b/resolvers/zine/topics.py @@ -6,7 +6,6 @@ from base.resolvers import mutation, query from orm import Shout from orm.topic import Topic, TopicFollower from services.zine.topics import TopicStorage -# from services.stat.reacted import ReactedStorage from services.stat.topicstat import TopicStat @@ -17,11 +16,7 @@ async def get_topic_stat(slug): return { "shouts": len(TopicStat.shouts_by_topic.get(slug, {}).keys()), "authors": len(TopicStat.authors_by_topic.get(slug, {}).keys()), - "followers": len(TopicStat.followers_by_topic.get(slug, {}).keys()), - # "viewed": await ViewedStorage.get_topic(slug), - # "reacted": len(await ReactedStorage.get_topic(slug)), - # "commented": len(await ReactedStorage.get_topic_comments(slug)), - # "rating": await ReactedStorage.get_topic_rating(slug) + "followers": len(TopicStat.followers_by_topic.get(slug, {}).keys()) } diff --git a/services/search.py b/services/search.py index 9c9481be..834e5bf7 100644 --- a/services/search.py +++ b/services/search.py @@ -20,11 +20,13 @@ class SearchService: cached = await redis.execute("GET", text) if not cached: async with SearchService.lock: - by = { + options = { "title": text, - "body": text + "body": text, + "limit": limit, + "offset": offset } - payload = await load_shouts_by(None, None, by, limit, offset) + payload = await load_shouts_by(None, None, options) await redis.execute("SET", text, json.dumps(payload)) return payload else: diff --git a/services/stat/reacted.py b/services/stat/reacted.py index ef5a2953..3ae5684e 100644 --- a/services/stat/reacted.py +++ b/services/stat/reacted.py @@ -3,7 +3,6 @@ import time from base.orm import local_session from orm.reaction import ReactionKind, Reaction from services.zine.topics import TopicStorage -from services.stat.viewed import ViewedStorage def kind_to_rate(kind) -> int: @@ -34,18 +33,6 @@ class ReactedStorage: lock = asyncio.Lock() modified_shouts = set([]) - @staticmethod - async def get_shout_stat(slug, rating): - viewed = int(await ViewedStorage.get_shout(slug)) - # print(viewed) - return { - "viewed": viewed, - "reacted": len(await ReactedStorage.get_shout(slug)), - "commented": len(await ReactedStorage.get_comments(slug)), - # "rating": await ReactedStorage.get_rating(slug), - "rating": rating - } - @staticmethod async def get_shout(shout_slug): self = ReactedStorage @@ -59,7 +46,7 @@ class ReactedStorage: return self.reacted["authors"].get(user_slug, []) @staticmethod - async def get_shouts_by_author(user_slug): + async def get_followed_reactions(user_slug): self = ReactedStorage async with self.lock: author_reactions = self.reacted["authors"].get(user_slug, [])