2022-07-21 11:58:50 +00:00
|
|
|
import asyncio
|
|
|
|
from datetime import datetime, timedelta
|
2022-09-17 18:12:14 +00:00
|
|
|
|
2022-09-20 07:11:22 +00:00
|
|
|
from sqlalchemy import and_, desc, func, select
|
2022-08-13 09:48:07 +00:00
|
|
|
from sqlalchemy.orm import selectinload
|
2022-09-17 18:12:14 +00:00
|
|
|
|
2022-08-11 05:53:14 +00:00
|
|
|
from base.orm import local_session
|
2022-09-20 07:11:22 +00:00
|
|
|
from orm.reaction import Reaction, ReactionKind
|
2022-09-07 16:19:06 +00:00
|
|
|
from orm.shout import Shout, ShoutAuthor, ShoutTopic
|
2022-09-19 13:50:43 +00:00
|
|
|
from services.stat.reacted import ReactedStorage
|
|
|
|
|
|
|
|
|
|
|
|
async def get_shout_stat(slug):
|
|
|
|
return {
|
2022-09-20 07:11:22 +00:00
|
|
|
# TODO: use ackee as datasource
|
|
|
|
"viewed": 0, # await ViewedStorage.get_shout(slug),
|
2022-09-19 13:50:43 +00:00
|
|
|
"reacted": len(await ReactedStorage.get_shout(slug)),
|
|
|
|
"commented": len(await ReactedStorage.get_comments(slug)),
|
|
|
|
"rating": await ReactedStorage.get_rating(slug),
|
|
|
|
}
|
2022-07-21 11:58:50 +00:00
|
|
|
|
|
|
|
|
2022-09-14 09:45:31 +00:00
|
|
|
async def prepare_shouts(session, stmt):
|
2022-09-14 09:07:02 +00:00
|
|
|
shouts = []
|
2022-09-20 07:11:22 +00:00
|
|
|
print(stmt)
|
2022-09-14 09:07:02 +00:00
|
|
|
for s in list(map(lambda r: r.Shout, session.execute(stmt))):
|
2022-09-19 13:50:43 +00:00
|
|
|
s.stat = await get_shout_stat(s.slug)
|
2022-09-14 09:07:02 +00:00
|
|
|
shouts.append(s)
|
|
|
|
return shouts
|
|
|
|
|
|
|
|
|
2022-07-21 11:58:50 +00:00
|
|
|
class ShoutsCache:
|
2022-09-03 10:50:14 +00:00
|
|
|
limit = 200
|
|
|
|
period = 60 * 60 # 1 hour
|
|
|
|
lock = asyncio.Lock()
|
2022-07-21 11:58:50 +00:00
|
|
|
|
2022-09-07 16:19:06 +00:00
|
|
|
recent_published = []
|
|
|
|
recent_all = []
|
|
|
|
recent_reacted = []
|
2022-09-19 16:14:20 +00:00
|
|
|
recent_commented = []
|
2022-09-07 16:19:06 +00:00
|
|
|
top_month = []
|
|
|
|
top_overall = []
|
2022-09-17 18:12:14 +00:00
|
|
|
top_commented = []
|
2022-09-07 16:19:06 +00:00
|
|
|
|
|
|
|
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-17 18:12:14 +00:00
|
|
|
shouts = await prepare_shouts(
|
|
|
|
session,
|
|
|
|
(
|
|
|
|
select(Shout)
|
2022-09-19 13:50:43 +00:00
|
|
|
.options(
|
|
|
|
selectinload(Shout.authors),
|
|
|
|
selectinload(Shout.topics)
|
|
|
|
)
|
2022-09-20 07:11:22 +00:00
|
|
|
.where(Shout.deletedAt.is_(None))
|
|
|
|
.filter(Shout.publishedAt.is_not(None))
|
2022-09-19 18:42:38 +00:00
|
|
|
.order_by(desc("publishedAt"))
|
2022-09-17 18:12:14 +00:00
|
|
|
.limit(ShoutsCache.limit)
|
|
|
|
),
|
|
|
|
)
|
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-07-21 11:58:50 +00:00
|
|
|
|
2022-09-03 10:50:14 +00:00
|
|
|
@staticmethod
|
|
|
|
async def prepare_recent_all():
|
|
|
|
with local_session() as session:
|
2022-09-17 18:12:14 +00:00
|
|
|
shouts = await prepare_shouts(
|
|
|
|
session,
|
|
|
|
(
|
|
|
|
select(Shout)
|
2022-09-19 13:50:43 +00:00
|
|
|
.options(
|
|
|
|
selectinload(Shout.authors),
|
|
|
|
selectinload(Shout.topics)
|
|
|
|
)
|
2022-09-20 07:11:22 +00:00
|
|
|
.where(Shout.deletedAt.is_(None))
|
2022-09-19 18:42:38 +00:00
|
|
|
.order_by(desc("createdAt"))
|
2022-09-17 18:12:14 +00:00
|
|
|
.limit(ShoutsCache.limit)
|
2022-09-20 07:11:22 +00:00
|
|
|
)
|
2022-09-17 18:12:14 +00:00
|
|
|
)
|
2022-09-03 10:50:14 +00:00
|
|
|
async with ShoutsCache.lock:
|
2022-09-19 18:42:38 +00:00
|
|
|
ShoutsCache.recent_all = shouts[0:ShoutsCache.limit]
|
|
|
|
print("[zine.cache] %d recently created shouts " % len(ShoutsCache.recent_all))
|
2022-07-21 11:58:50 +00:00
|
|
|
|
2022-09-03 10:50:14 +00:00
|
|
|
@staticmethod
|
|
|
|
async def prepare_recent_reacted():
|
|
|
|
with local_session() as session:
|
2022-09-19 13:50:43 +00:00
|
|
|
reactions = session.query(Reaction).order_by(Reaction.createdAt).limit(ShoutsCache.limit)
|
|
|
|
reacted_slugs = set([])
|
|
|
|
for r in reactions:
|
|
|
|
reacted_slugs.add(r.shout)
|
2022-09-17 18:12:14 +00:00
|
|
|
shouts = await prepare_shouts(
|
|
|
|
session,
|
|
|
|
(
|
2022-09-20 07:11:22 +00:00
|
|
|
select(
|
|
|
|
Shout,
|
|
|
|
Reaction.createdAt.label('reactedAt')
|
|
|
|
)
|
2022-09-17 18:12:14 +00:00
|
|
|
.options(
|
|
|
|
selectinload(Shout.authors),
|
|
|
|
selectinload(Shout.topics),
|
2022-09-19 18:42:38 +00:00
|
|
|
selectinload(Shout.reactions),
|
2022-09-17 18:12:14 +00:00
|
|
|
)
|
2022-09-19 18:42:38 +00:00
|
|
|
.join(Reaction)
|
2022-09-20 07:11:22 +00:00
|
|
|
.where(and_(Shout.deletedAt.is_(None), Shout.slug.in_(reacted_slugs)))
|
|
|
|
.filter(Shout.publishedAt.is_not(None))
|
2022-09-19 18:42:38 +00:00
|
|
|
.group_by(Shout.slug, "reactedAt")
|
|
|
|
.order_by(desc("reactedAt"))
|
2022-09-17 18:12:14 +00:00
|
|
|
.limit(ShoutsCache.limit)
|
2022-09-19 13:50:43 +00:00
|
|
|
)
|
2022-09-17 18:12:14 +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-07-21 11:58:50 +00:00
|
|
|
|
2022-09-19 16:14:20 +00:00
|
|
|
@staticmethod
|
|
|
|
async def prepare_recent_commented():
|
|
|
|
with local_session() as session:
|
|
|
|
reactions = session.query(Reaction).order_by(Reaction.createdAt).limit(ShoutsCache.limit)
|
|
|
|
commented_slugs = set([])
|
|
|
|
for r in reactions:
|
|
|
|
if bool(r.body):
|
|
|
|
commented_slugs.add(r.shout)
|
|
|
|
shouts = await prepare_shouts(
|
|
|
|
session,
|
|
|
|
(
|
2022-09-20 07:11:22 +00:00
|
|
|
select(
|
|
|
|
Shout,
|
|
|
|
Reaction.createdAt.label('reactedAt')
|
|
|
|
)
|
2022-09-19 16:14:20 +00:00
|
|
|
.options(
|
|
|
|
selectinload(Shout.authors),
|
|
|
|
selectinload(Shout.topics),
|
2022-09-19 18:42:38 +00:00
|
|
|
selectinload(Shout.reactions),
|
2022-09-19 16:14:20 +00:00
|
|
|
)
|
2022-09-20 07:11:22 +00:00
|
|
|
.join(Reaction)
|
|
|
|
.where(and_(Shout.deletedAt.is_(None), Shout.slug.in_(commented_slugs)))
|
2022-09-19 18:42:38 +00:00
|
|
|
.group_by(Shout.slug, "reactedAt")
|
|
|
|
.order_by(desc("reactedAt"))
|
2022-09-19 16:14:20 +00:00
|
|
|
.limit(ShoutsCache.limit)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
async with ShoutsCache.lock:
|
|
|
|
ShoutsCache.recent_commented = shouts
|
|
|
|
print("[zine.cache] %d recently commented shouts " % len(shouts))
|
|
|
|
|
2022-09-03 10:50:14 +00:00
|
|
|
@staticmethod
|
|
|
|
async def prepare_top_overall():
|
|
|
|
with local_session() as session:
|
2022-09-17 18:12:14 +00:00
|
|
|
shouts = await prepare_shouts(
|
|
|
|
session,
|
|
|
|
(
|
2022-09-20 07:11:22 +00:00
|
|
|
select(
|
|
|
|
Shout,
|
|
|
|
func.sum(int(bool(Reaction.kind == ReactionKind.LIKE))).label('liked')
|
|
|
|
)
|
2022-09-17 18:12:14 +00:00
|
|
|
.options(
|
|
|
|
selectinload(Shout.authors),
|
|
|
|
selectinload(Shout.topics),
|
|
|
|
selectinload(Shout.reactions),
|
|
|
|
)
|
|
|
|
.join(Reaction)
|
2022-09-20 07:11:22 +00:00
|
|
|
.where(Shout.deletedAt.is_(None))
|
|
|
|
.filter(Shout.publishedAt.is_not(None))
|
2022-09-17 18:12:14 +00:00
|
|
|
.group_by(Shout.slug)
|
2022-09-20 07:11:22 +00:00
|
|
|
.order_by(desc("liked"))
|
2022-09-17 18:12:14 +00:00
|
|
|
.limit(ShoutsCache.limit)
|
|
|
|
),
|
|
|
|
)
|
2022-09-19 13:50:43 +00:00
|
|
|
shouts.sort(key=lambda s: s.stat["rating"], reverse=True)
|
2022-09-03 10:50:14 +00:00
|
|
|
async with ShoutsCache.lock:
|
2022-09-19 18:42:38 +00:00
|
|
|
print("[zine.cache] %d top rated published " % len(shouts))
|
2022-09-03 10:50:14 +00:00
|
|
|
ShoutsCache.top_overall = shouts
|
2022-07-21 11:58:50 +00:00
|
|
|
|
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-17 18:12:14 +00:00
|
|
|
shouts = await prepare_shouts(
|
|
|
|
session,
|
|
|
|
(
|
2022-09-20 07:11:22 +00:00
|
|
|
select(
|
|
|
|
Shout,
|
|
|
|
func.sum(int(bool(Reaction.kind == ReactionKind.LIKE))).label('liked')
|
|
|
|
)
|
2022-09-19 18:42:38 +00:00
|
|
|
.options(
|
|
|
|
selectinload(Shout.authors),
|
|
|
|
selectinload(Shout.topics),
|
|
|
|
selectinload(Shout.reactions),
|
|
|
|
)
|
2022-09-17 18:12:14 +00:00
|
|
|
.join(Reaction)
|
2022-09-20 07:11:22 +00:00
|
|
|
.where(Shout.deletedAt.is_(None))
|
|
|
|
.filter(Shout.publishedAt > month_ago)
|
2022-09-17 18:12:14 +00:00
|
|
|
.group_by(Shout.slug)
|
2022-09-20 07:11:22 +00:00
|
|
|
.order_by(desc("liked"))
|
2022-09-17 18:12:14 +00:00
|
|
|
.limit(ShoutsCache.limit)
|
|
|
|
),
|
|
|
|
)
|
2022-09-19 13:50:43 +00:00
|
|
|
shouts.sort(key=lambda s: s.stat["rating"], reverse=True)
|
2022-09-03 10:50:14 +00:00
|
|
|
async with ShoutsCache.lock:
|
|
|
|
ShoutsCache.top_month = shouts
|
2022-09-19 18:42:38 +00:00
|
|
|
print("[zine.cache] %d top month published " % len(ShoutsCache.top_month))
|
2022-07-21 11:58:50 +00:00
|
|
|
|
2022-09-17 18:12:14 +00:00
|
|
|
@staticmethod
|
|
|
|
async def prepare_top_commented():
|
|
|
|
month_ago = datetime.now() - timedelta(days=30)
|
|
|
|
with local_session() as session:
|
|
|
|
shouts = await prepare_shouts(
|
|
|
|
session,
|
|
|
|
(
|
2022-09-20 07:11:22 +00:00
|
|
|
select(
|
|
|
|
Shout,
|
|
|
|
func.sum(int(bool(Reaction.body))).label("commented")
|
|
|
|
)
|
2022-09-19 18:42:38 +00:00
|
|
|
.options(
|
|
|
|
selectinload(Shout.authors),
|
|
|
|
selectinload(Shout.topics),
|
|
|
|
selectinload(Shout.reactions)
|
|
|
|
)
|
2022-09-17 18:12:14 +00:00
|
|
|
.join(Reaction)
|
2022-09-20 07:11:22 +00:00
|
|
|
.where(Shout.deletedAt.is_(None))
|
|
|
|
.filter(Shout.publishedAt > month_ago)
|
2022-09-17 18:12:14 +00:00
|
|
|
.group_by(Shout.slug)
|
|
|
|
.order_by(desc("commented"))
|
|
|
|
.limit(ShoutsCache.limit)
|
|
|
|
),
|
|
|
|
)
|
2022-09-19 13:50:43 +00:00
|
|
|
shouts.sort(key=lambda s: s.stat["commented"], reverse=True)
|
2022-09-17 18:12:14 +00:00
|
|
|
async with ShoutsCache.lock:
|
2022-09-19 18:42:38 +00:00
|
|
|
ShoutsCache.top_commented = shouts
|
|
|
|
print("[zine.cache] %d last month top commented shouts " % len(ShoutsCache.top_commented))
|
2022-09-17 18:12:14 +00:00
|
|
|
|
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():
|
2022-09-20 07:11:22 +00:00
|
|
|
shout = session.query(Shout).where(Shout.slug == a.shout).first()
|
2022-09-19 13:50:43 +00:00
|
|
|
shout.stat = await get_shout_stat(shout.slug)
|
|
|
|
shouts_by_author[a.user] = shouts_by_author.get(a.user, [])
|
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
|
|
|
|
|
2022-09-20 13:32:04 +00:00
|
|
|
@staticmethod
|
|
|
|
async def get_top_published_before(daysago, offset, limit):
|
|
|
|
shouts_by_rating = []
|
|
|
|
before = datetime.now() - timedelta(days=daysago)
|
|
|
|
for s in ShoutsCache.recent_published:
|
|
|
|
if s.publishedAt >= before:
|
|
|
|
shouts_by_rating.append(s)
|
|
|
|
return shouts_by_rating
|
|
|
|
|
2022-09-07 16:19:06 +00:00
|
|
|
@staticmethod
|
|
|
|
async def prepare_by_topic():
|
|
|
|
shouts_by_topic = {}
|
|
|
|
with local_session() as session:
|
2022-09-19 13:50:43 +00:00
|
|
|
for a in session.query(ShoutTopic).all():
|
2022-09-20 07:11:22 +00:00
|
|
|
shout = session.query(Shout).where(Shout.slug == a.shout).first()
|
2022-09-19 13:50:43 +00:00
|
|
|
shout.stat = await get_shout_stat(shout.slug)
|
|
|
|
shouts_by_topic[a.topic] = shouts_by_topic.get(a.topic, [])
|
|
|
|
if shout not in shouts_by_topic[a.topic]:
|
|
|
|
shouts_by_topic[a.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()
|
2022-09-19 18:42:38 +00:00
|
|
|
await ShoutsCache.prepare_top_commented()
|
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-19 16:14:20 +00:00
|
|
|
await ShoutsCache.prepare_recent_commented()
|
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)
|