diff --git a/migration/tables/comments.py b/migration/tables/comments.py index 5f71d677..b2dda926 100644 --- a/migration/tables/comments.py +++ b/migration/tables/comments.py @@ -74,11 +74,8 @@ async def migrate(entry, storage): reaction_dict["kind"] = ReactionKind.COMMENT # creating reaction from old comment - day = (reaction_dict.get("createdAt") or ts).replace( - hour=0, minute=0, second=0, microsecond=0 - ) reaction = Reaction.create(**reaction_dict) - await ReactedStorage.increment(reaction) + await ReactedStorage.react(reaction) reaction_dict["id"] = reaction.id for comment_rating_old in entry.get("ratings", []): @@ -106,7 +103,7 @@ async def migrate(entry, storage): try: # creating reaction from old rating rr = Reaction.create(**re_reaction_dict) - await ReactedStorage.increment(rr) + await ReactedStorage.react(rr) except Exception as e: print("[migration] comment rating error: %r" % re_reaction_dict) diff --git a/migration/tables/content_items.py b/migration/tables/content_items.py index 764d2fbf..ff27b211 100644 --- a/migration/tables/content_items.py +++ b/migration/tables/content_items.py @@ -269,11 +269,11 @@ async def migrate(entry, storage): ) reaction.update(reaction_dict) else: - reaction_dict['day'] = (reaction_dict.get("createdAt") or ts).replace( - hour=0, minute=0, second=0, microsecond=0 - ) + reaction_dict["day"] = ( + reaction_dict.get("createdAt") or ts + ).replace(hour=0, minute=0, second=0, microsecond=0) rea = Reaction.create(**reaction_dict) - await ReactedStorage.increment(rea) + await ReactedStorage.react(rea) # shout_dict['ratings'].append(reaction_dict) except Exception: raise Exception("[migration] content_item.ratings error: \n%r" % content_rating) diff --git a/orm/collection.py b/orm/collection.py index 8e16dd39..7d0a0b44 100644 --- a/orm/collection.py +++ b/orm/collection.py @@ -21,3 +21,4 @@ class Collection(Base): pic = Column(String, nullable=True, comment="Picture") createdAt = Column(DateTime, default=datetime.now, comment="Created At") createdBy = Column(ForeignKey("user.id"), comment="Created By") + publishedAt = Column(DateTime, default=datetime.now, comment="Published At") diff --git a/orm/reaction.py b/orm/reaction.py index ec5f2e28..30069d1d 100644 --- a/orm/reaction.py +++ b/orm/reaction.py @@ -30,7 +30,6 @@ class Reaction(Base): return { "viewed": await ViewedStorage.get_reaction(self.id), "reacted": len(await ReactedStorage.get_reaction(self.id)), - # TODO: "replied" "rating": await ReactedStorage.get_reaction_rating(self.id), "commented": len(await ReactedStorage.get_reaction_comments(self.id)), } diff --git a/resolvers/collection.py b/resolvers/collection.py index d521b051..3755da14 100644 --- a/resolvers/collection.py +++ b/resolvers/collection.py @@ -9,14 +9,14 @@ from sqlalchemy import and_ @mutation.field("createCollection") @login_required -async def create_collection(_, info, input): +async def create_collection(_, _info, inp): # auth = info.context["request"].auth # user_id = auth.user_id collection = Collection.create( - slug=input.get("slug", ""), - title=input.get("title", ""), - desc=input.get("desc", ""), - pic=input.get("pic", ""), + slug=inp.get("slug", ""), + title=inp.get("title", ""), + desc=inp.get("desc", ""), + pic=inp.get("pic", ""), ) return {"collection": collection} @@ -24,7 +24,7 @@ async def create_collection(_, info, input): @mutation.field("updateCollection") @login_required -async def update_collection(_, info, input): +async def update_collection(_, info, inp): auth = info.context["request"].auth user_id = auth.user_id collection_slug = input.get("slug", "") @@ -38,9 +38,9 @@ async def update_collection(_, info, input): return {"error": "invalid collection id"} if collection.createdBy not in (owner + editors): return {"error": "access denied"} - collection.title = input.get("title", "") - collection.desc = input.get("desc", "") - collection.pic = input.get("pic", "") + collection.title = inp.get("title", "") + collection.desc = inp.get("desc", "") + collection.pic = inp.get("pic", "") collection.updatedAt = datetime.now() session.commit() @@ -63,7 +63,7 @@ async def delete_collection(_, info, slug): @query.field("getUserCollections") -async def get_user_collections(_, info, userslug): +async def get_user_collections(_, _info, userslug): collections = [] with local_session() as session: user = session.query(User).filter(User.slug == userslug).first() diff --git a/resolvers/reactions.py b/resolvers/reactions.py index 25cf0636..ef33abc1 100644 --- a/resolvers/reactions.py +++ b/resolvers/reactions.py @@ -58,10 +58,10 @@ def reactions_unfollow(user, slug): async def create_reaction(_, info, inp): user = info.context["request"].user - # TODO: filter allowed reaction kinds + # TODO: filter allowed for post reaction kinds reaction = Reaction.create(**inp) - ReactedStorage.increment(reaction.shout, reaction.replyTo) + ReactedStorage.react(reaction) try: reactions_follow(user, inp["shout"], True) except Exception as e: diff --git a/resolvers/zine.py b/resolvers/zine.py index 85d69d9d..e5fbab80 100644 --- a/resolvers/zine.py +++ b/resolvers/zine.py @@ -18,42 +18,42 @@ from sqlalchemy.orm import selectinload @query.field("topViewed") async def top_viewed(_, info, page, size): async with ShoutsCache.lock: - return ShoutsCache.top_viewed[((page - 1) * size) : (page * size)] + return ShoutsCache.get_top_viewed[((page - 1) * size) : (page * size)] @query.field("topMonth") async def top_month(_, info, page, size): async with ShoutsCache.lock: - return ShoutsCache.top_month[((page - 1) * size) : (page * size)] + return ShoutsCache.get_top_month[((page - 1) * size) : (page * size)] @query.field("topOverall") async def top_overall(_, info, page, size): async with ShoutsCache.lock: - return ShoutsCache.top_overall[((page - 1) * size) : (page * size)] + return ShoutsCache.get_top_overall[((page - 1) * size) : (page * size)] @query.field("recentPublished") async def recent_published(_, info, page, size): async with ShoutsCache.lock: - return ShoutsCache.recent_published[((page - 1) * size) : (page * size)] + return ShoutsCache.get_recent_published[((page - 1) * size) : (page * size)] @query.field("recentAll") async def recent_all(_, info, page, size): async with ShoutsCache.lock: - return ShoutsCache.recent_all[((page - 1) * size) : (page * size)] + return ShoutsCache.get_recent_all[((page - 1) * size) : (page * size)] @query.field("recentReacted") async def recent_reacted(_, info, page, size): async with ShoutsCache.lock: - return ShoutsCache.recent_reacted[((page - 1) * size) : (page * size)] + return ShoutsCache.get_recent_reacted[((page - 1) * size) : (page * size)] @mutation.field("viewShout") async def view_shout(_, info, slug): - await ViewedStorage.inc_shout(slug) + await ViewedStorage.increment(slug) return {"error": ""} diff --git a/services/zine/shoutscache.py b/services/zine/shoutscache.py index fc632397..06062d35 100644 --- a/services/zine/shoutscache.py +++ b/services/zine/shoutscache.py @@ -4,7 +4,7 @@ from sqlalchemy import and_, desc, func, select from sqlalchemy.orm import selectinload from base.orm import local_session from orm.reaction import Reaction -from orm.shout import Shout +from orm.shout import Shout, ShoutAuthor, ShoutTopic from services.stat.reacted import ReactedStorage from services.stat.viewed import ViewedByDay @@ -14,6 +14,16 @@ class ShoutsCache: period = 60 * 60 # 1 hour lock = asyncio.Lock() + recent_published = [] + recent_all = [] + recent_reacted = [] + top_month = [] + top_overall = [] + top_viewed = [] + + by_author = {} + by_topic = {} + @staticmethod async def prepare_recent_published(): with local_session() as session: @@ -151,6 +161,82 @@ class ShoutsCache: print("[zine.cache] %d top viewed shouts " % len(shouts)) ShoutsCache.top_viewed = shouts + @staticmethod + async def prepare_by_author(): + shouts_by_author = {} + with local_session() as session: + + for a in session.query(ShoutAuthor).all(): + + shout = session.query(Shout).filter(Shout.slug == a.shout).first() + + if not shouts_by_author[a.author]: + shouts_by_author[a.author] = [] + + if shout not in shouts_by_author[a.author]: + shouts_by_author[a.author].push(shout) + async with ShoutsCache.lock: + print("[zine.cache indexed by %d authors " % len(shouts_by_author.keys())) + ShoutsCache.by_author = shouts_by_author + + @staticmethod + async def prepare_by_topic(): + shouts_by_topic = {} + with local_session() as session: + + for t in session.query(ShoutTopic).all(): + + shout = session.query(Shout).filter(Shout.slug == t.shout).first() + + if not shouts_by_topic[t.topic]: + shouts_by_topic[t.topic] = [] + + if shout not in shouts_by_topic[t.topic]: + shouts_by_topic[t.topic].push(shout) + async with ShoutsCache.lock: + print("[zine.cache] indexed by %d topics " % len(shouts_by_topic.keys())) + ShoutsCache.by_topic = shouts_by_topic + + @staticmethod + async def get_shouts_by_author(): + async with ShoutsCache.lock: + return ShoutsCache.by_author + + @staticmethod + async def get_shouts_by_topic(): + async with ShoutsCache.lock: + return ShoutsCache.by_topic + + @staticmethod + async def get_top_overall(): + async with ShoutsCache.lock: + return ShoutsCache.by_topic + + @staticmethod + async def get_top_month(): + async with ShoutsCache.lock: + return ShoutsCache.by_topic + + @staticmethod + async def get_top_viewed(): + async with ShoutsCache.lock: + return ShoutsCache.by_topic + + @staticmethod + async def get_recent_published(): + async with ShoutsCache.lock: + return ShoutsCache.recent_published + + @staticmethod + async def get_recent_all(): + async with ShoutsCache.lock: + return ShoutsCache.recent_all + + @staticmethod + async def get_recent_reacted(): + async with ShoutsCache.lock: + return ShoutsCache.recent_reacted + @staticmethod async def worker(): while True: @@ -158,9 +244,13 @@ class ShoutsCache: await ShoutsCache.prepare_top_month() await ShoutsCache.prepare_top_overall() await ShoutsCache.prepare_top_viewed() + await ShoutsCache.prepare_recent_published() await ShoutsCache.prepare_recent_all() await ShoutsCache.prepare_recent_reacted() + + await ShoutsCache.prepare_by_author() + await ShoutsCache.prepare_by_topic() print("[zine.cache] periodical update") except Exception as err: print("[zine.cache] error: %s" % (err)) diff --git a/services/zine/topics.py b/services/zine/topics.py index 3e2bcd74..97773414 100644 --- a/services/zine/topics.py +++ b/services/zine/topics.py @@ -11,8 +11,8 @@ class TopicStorage: self = TopicStorage topics = session.query(Topic) self.topics = dict([(topic.slug, topic) for topic in topics]) - for topic in self.topics.values(): - self.load_parents(topic) + for tpc in self.topics.values(): + self.load_parents(tpc) print("[zine.topics] %d precached" % len(self.topics.keys())) @@ -51,7 +51,16 @@ class TopicStorage: return list(topics) @staticmethod - async def add_topic(topic): + async def get_topics_by_author(author): + self = TopicStorage + async with self.lock: + topics = filter( + lambda topic: topic.community == author, self.topics.values() + ) + return list(topics) + + @staticmethod + async def update_topic(topic): self = TopicStorage async with self.lock: self.topics[topic.slug] = topic