core/services/zine/shoutscache.py

195 lines
7.3 KiB
Python
Raw Normal View History

import asyncio
from datetime import datetime, timedelta
from sqlalchemy import and_, desc, func, select
from sqlalchemy.orm import selectinload
2022-08-11 05:53:14 +00:00
from base.orm import local_session
from orm.reaction import Reaction
2022-09-07 16:19:06 +00:00
from orm.shout import Shout, ShoutAuthor, ShoutTopic
2022-08-11 09:09:57 +00:00
from services.stat.viewed import ViewedByDay
2022-09-14 09:45:31 +00:00
async def prepare_shouts(session, stmt):
2022-09-14 09:07:02 +00:00
shouts = []
for s in list(map(lambda r: r.Shout, session.execute(stmt))):
2022-09-14 09:45:31 +00:00
s.stats = await s.stat
2022-09-14 09:07:02 +00:00
shouts.append(s)
return shouts
class ShoutsCache:
2022-09-03 10:50:14 +00:00
limit = 200
period = 60 * 60 # 1 hour
lock = asyncio.Lock()
2022-09-07 16:19:06 +00:00
recent_published = []
recent_all = []
recent_reacted = []
top_month = []
top_overall = []
top_viewed = []
by_author = {}
by_topic = {}
2022-09-03 10:50:14 +00:00
@staticmethod
async def prepare_recent_published():
with local_session() as session:
2022-09-14 09:45:31 +00:00
shouts = await prepare_shouts(session, (
2022-09-03 10:50:14 +00:00
select(Shout)
.options(selectinload(Shout.authors), selectinload(Shout.topics))
2022-09-03 11:10:28 +00:00
.where(bool(Shout.publishedAt))
2022-09-03 10:50:14 +00:00
.order_by(desc("publishedAt"))
2022-09-14 11:09:28 +00:00
.order_by(desc("createdAt"))
2022-09-03 10:50:14 +00:00
.limit(ShoutsCache.limit)
2022-09-14 09:07:02 +00:00
))
2022-09-03 10:50:14 +00:00
async with ShoutsCache.lock:
ShoutsCache.recent_published = shouts
print("[zine.cache] %d recently published shouts " % len(shouts))
2022-09-03 10:50:14 +00:00
@staticmethod
async def prepare_recent_all():
with local_session() as session:
2022-09-14 09:45:31 +00:00
shouts = await prepare_shouts(session, (
2022-09-03 10:50:14 +00:00
select(Shout)
.options(selectinload(Shout.authors), selectinload(Shout.topics))
.order_by(desc("createdAt"))
.limit(ShoutsCache.limit)
2022-09-14 09:07:02 +00:00
))
2022-09-03 10:50:14 +00:00
async with ShoutsCache.lock:
ShoutsCache.recent_all = shouts
print("[zine.cache] %d recently created shouts " % len(shouts))
2022-09-03 10:50:14 +00:00
@staticmethod
async def prepare_recent_reacted():
with local_session() as session:
2022-09-14 09:45:31 +00:00
shouts = await prepare_shouts(session, (
2022-09-03 10:50:14 +00:00
select(Shout, func.max(Reaction.createdAt).label("reactionCreatedAt"))
.options(
selectinload(Shout.authors),
selectinload(Shout.topics),
)
.join(Reaction, Reaction.shout == Shout.slug)
2022-09-03 11:10:28 +00:00
.where(and_(bool(Shout.publishedAt), bool(Reaction.deletedAt)))
2022-09-03 10:50:14 +00:00
.group_by(Shout.slug)
.order_by(desc("reactionCreatedAt"))
.limit(ShoutsCache.limit)
2022-09-14 09:07:02 +00:00
))
2022-09-03 10:50:14 +00:00
async with ShoutsCache.lock:
ShoutsCache.recent_reacted = shouts
print("[zine.cache] %d recently reacted shouts " % len(shouts))
2022-09-03 10:50:14 +00:00
@staticmethod
async def prepare_top_overall():
with local_session() as session:
# with reacted times counter
2022-09-14 09:45:31 +00:00
shouts = await prepare_shouts(session, (
2022-09-03 10:50:14 +00:00
select(Shout, func.count(Reaction.id).label("reacted"))
.options(
selectinload(Shout.authors),
selectinload(Shout.topics),
selectinload(Shout.reactions),
)
.join(Reaction)
2022-09-03 11:10:28 +00:00
.where(and_(bool(Shout.publishedAt), bool(Reaction.deletedAt)))
2022-09-03 10:50:14 +00:00
.group_by(Shout.slug)
.order_by(desc("reacted"))
.limit(ShoutsCache.limit)
2022-09-14 09:07:02 +00:00
))
2022-09-14 09:45:31 +00:00
shouts.sort(key=lambda s: s.stats['rating'], reverse=True)
2022-09-03 10:50:14 +00:00
async with ShoutsCache.lock:
print("[zine.cache] %d top shouts " % len(shouts))
ShoutsCache.top_overall = shouts
2022-09-03 10:50:14 +00:00
@staticmethod
async def prepare_top_month():
month_ago = datetime.now() - timedelta(days=30)
with local_session() as session:
2022-09-14 09:45:31 +00:00
shouts = await prepare_shouts(session, (
2022-09-03 10:50:14 +00:00
select(Shout, func.count(Reaction.id).label("reacted"))
.options(selectinload(Shout.authors), selectinload(Shout.topics))
.join(Reaction)
2022-09-03 11:10:28 +00:00
.where(and_(Shout.createdAt > month_ago, bool(Reaction.deletedAt)))
2022-09-03 10:50:14 +00:00
.group_by(Shout.slug)
.order_by(desc("reacted"))
.limit(ShoutsCache.limit)
2022-09-14 09:07:02 +00:00
))
2022-09-14 09:45:31 +00:00
shouts.sort(key=lambda s: s.stats['rating'], reverse=True)
2022-09-03 10:50:14 +00:00
async with ShoutsCache.lock:
print("[zine.cache] %d top month shouts " % len(shouts))
ShoutsCache.top_month = shouts
2022-09-03 10:50:14 +00:00
@staticmethod
async def prepare_top_viewed():
month_ago = datetime.now() - timedelta(days=30)
with local_session() as session:
2022-09-14 09:45:31 +00:00
shouts = await prepare_shouts(session, (
2022-09-03 10:50:14 +00:00
select(Shout, func.sum(ViewedByDay.value).label("viewed"))
.options(selectinload(Shout.authors), selectinload(Shout.topics))
.join(ViewedByDay)
2022-09-03 11:10:28 +00:00
.where(and_(Shout.createdAt > month_ago, bool(Reaction.deletedAt)))
2022-09-03 10:50:14 +00:00
.group_by(Shout.slug)
.order_by(desc("viewed"))
.limit(ShoutsCache.limit)
2022-09-14 09:07:02 +00:00
))
2022-09-14 09:45:31 +00:00
shouts.sort(key=lambda s: s.stats['viewed'], reverse=True)
2022-09-03 10:50:14 +00:00
async with ShoutsCache.lock:
print("[zine.cache] %d top viewed shouts " % len(shouts))
ShoutsCache.top_viewed = shouts
2022-09-07 16:19:06 +00:00
@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()
2022-09-07 16:30:06 +00:00
if not shouts_by_author.get(a.user):
shouts_by_author[a.user] = []
2022-09-07 16:19:06 +00:00
2022-09-07 16:30:06 +00:00
if shout not in shouts_by_author[a.user]:
shouts_by_author[a.user].append(shout)
2022-09-07 16:19:06 +00:00
async with ShoutsCache.lock:
2022-09-07 16:30:06 +00:00
print("[zine.cache] indexed by %d authors " % len(shouts_by_author.keys()))
2022-09-07 16:19:06 +00:00
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()
2022-09-07 16:30:06 +00:00
if not shouts_by_topic.get(t.topic):
2022-09-07 16:19:06 +00:00
shouts_by_topic[t.topic] = []
if shout not in shouts_by_topic[t.topic]:
2022-09-07 16:30:06 +00:00
shouts_by_topic[t.topic].append(shout)
2022-09-07 16:19:06 +00:00
async with ShoutsCache.lock:
print("[zine.cache] indexed by %d topics " % len(shouts_by_topic.keys()))
ShoutsCache.by_topic = shouts_by_topic
2022-09-03 10:50:14 +00:00
@staticmethod
async def worker():
while True:
try:
await ShoutsCache.prepare_top_month()
await ShoutsCache.prepare_top_overall()
await ShoutsCache.prepare_top_viewed()
2022-09-07 16:19:06 +00:00
2022-09-03 10:50:14 +00:00
await ShoutsCache.prepare_recent_published()
await ShoutsCache.prepare_recent_all()
await ShoutsCache.prepare_recent_reacted()
2022-09-07 16:19:06 +00:00
await ShoutsCache.prepare_by_author()
await ShoutsCache.prepare_by_topic()
2022-09-03 10:50:14 +00:00
print("[zine.cache] periodical update")
except Exception as err:
print("[zine.cache] error: %s" % (err))
raise err
await asyncio.sleep(ShoutsCache.period)