From ab9990a61669aab35810c0f76e272558955295b9 Mon Sep 17 00:00:00 2001 From: knst-kotov Date: Mon, 13 Dec 2021 19:51:01 +0300 Subject: [PATCH] add topic stat --- main.py | 3 ++- orm/shout.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ resolvers/topics.py | 8 +++++++- schema.graphql | 6 ++++++ 4 files changed, 60 insertions(+), 2 deletions(-) diff --git a/main.py b/main.py index d2720618..438d838e 100644 --- a/main.py +++ b/main.py @@ -15,7 +15,7 @@ from redis import redis from resolvers.base import resolvers from resolvers.zine import GitTask, ShoutsCache -from orm.shout import ShoutViewStorage +from orm.shout import ShoutViewStorage, TopicStat import asyncio @@ -32,6 +32,7 @@ async def start_up(): git_task = asyncio.create_task(GitTask.git_task_worker()) shouts_cache_task = asyncio.create_task(ShoutsCache.worker()) view_storage_task = asyncio.create_task(ShoutViewStorage.worker()) + topic_stat_task = asyncio.create_task(TopicStat.worker()) async def shutdown(): await redis.disconnect() diff --git a/orm/shout.py b/orm/shout.py index 82d1b27e..2f7c46b3 100644 --- a/orm/shout.py +++ b/orm/shout.py @@ -140,6 +140,51 @@ class ShoutViewStorage: print("ShoutViewStorage worker: error = %s" % (err)) await asyncio.sleep(ShoutViewStorage.period) +class TopicStat: + shouts_by_topic = {} + lock = asyncio.Lock() + + period = 30*60 #sec + + @staticmethod + async def load_stat(session): + self = TopicStat + shout_topics = session.query(ShoutTopic) + for shout_topic in shout_topics: + topic = shout_topic.topic + shout = shout_topic.shout + if topic in self.shouts_by_topic: + self.shouts_by_topic[topic].append(shout) + else: + self.shouts_by_topic[topic] = [shout] + + @staticmethod + async def get_stat(topic): + self = TopicStat + async with self.lock: + shouts = self.shouts_by_topic.get(topic, []) + stat = { "shouts" : len(shouts) } + + views = 0 + for shout in shouts: + views += await ShoutViewStorage.get_view(shout) + stat["views"] = views + + return stat + + @staticmethod + async def worker(): + self = TopicStat + print("TopicStat worker start") + while True: + try: + print("TopicStat worker: load stat") + with local_session() as session: + async with self.lock: + await self.load_stat(session) + except Exception as err: + print("TopicStat worker: error = %s" % (err)) + await asyncio.sleep(self.period) class Shout(Base): __tablename__ = 'shout' diff --git a/resolvers/topics.py b/resolvers/topics.py index e6532689..2b5b3f85 100644 --- a/resolvers/topics.py +++ b/resolvers/topics.py @@ -1,4 +1,5 @@ from orm import Topic, TopicSubscription, TopicStorage, Shout, User +from orm.shout import TopicStat from orm.base import local_session from resolvers.base import mutation, query, subscription from resolvers.zine import ShoutSubscriptions @@ -8,7 +9,12 @@ import asyncio @query.field("topicsBySlugs") async def topics_by_slugs(_, info, slugs = None): with local_session() as session: - return await TopicStorage.get_topics(slugs) + topics = await TopicStorage.get_topics(slugs) + all_fields = [node.name.value for node in info.field_nodes[0].selection_set.selections] + if "topicStat" in all_fields: + for topic in topics: + topic.topicStat = await TopicStat.get_stat(topic.slug) + return topics @query.field("topicsByCommunity") async def topics_by_community(_, info, community): diff --git a/schema.graphql b/schema.graphql index e1a1a149..33ce98c8 100644 --- a/schema.graphql +++ b/schema.graphql @@ -331,6 +331,11 @@ type Community { pic: String! } +type TopicStat { + shouts: Int + views: Int +} + type Topic { slug: String! # ID title: String @@ -339,6 +344,7 @@ type Topic { parents: [String] # NOTE: topic can have parent topics children: [String] # and children community: String! + topicStat: TopicStat } # TODO: resolvers to add/remove topics from publication