store views on the shouts table, remove the viewed table

This commit is contained in:
Alexey Kulikov 2023-07-30 23:57:31 +01:00
parent 18e5cd4747
commit 41055d8501
6 changed files with 14 additions and 101 deletions

View File

@ -193,7 +193,7 @@ async def migrate(entry, storage):
await content_ratings_to_reactions(entry, shout_dict["slug"]) await content_ratings_to_reactions(entry, shout_dict["slug"])
# shout views # shout views
await ViewedStorage.increment(shout_dict["slug"], amount=entry.get("views", 1), viewer='old-discours') await ViewedStorage.increment(shout_dict["slug"], amount=entry.get("views", 1))
# del shout_dict['ratings'] # del shout_dict['ratings']
storage["shouts"]["by_oid"][entry["_id"]] = shout_dict storage["shouts"]["by_oid"][entry["_id"]] = shout_dict

View File

@ -6,7 +6,6 @@ from orm.reaction import Reaction
from orm.shout import Shout from orm.shout import Shout
from orm.topic import Topic, TopicFollower from orm.topic import Topic, TopicFollower
from orm.user import User, UserRating from orm.user import User, UserRating
from orm.viewed import ViewedEntry
# NOTE: keep orm module isolated # NOTE: keep orm module isolated
@ -22,7 +21,6 @@ __all__ = [
"Notification", "Notification",
"Reaction", "Reaction",
"UserRating", "UserRating",
"ViewedEntry"
] ]
@ -35,5 +33,4 @@ def init_tables():
Role.init_table() Role.init_table()
UserRating.init_table() UserRating.init_table()
Shout.init_table() Shout.init_table()
ViewedEntry.init_table()
print("[orm] tables initialized") print("[orm] tables initialized")

View File

@ -1,6 +1,6 @@
from datetime import datetime from datetime import datetime
from sqlalchemy import Boolean, Column, DateTime, ForeignKey, String, JSON from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String, JSON
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from base.orm import Base, local_session from base.orm import Base, local_session
@ -61,6 +61,7 @@ class Shout(Base):
authors = relationship(lambda: User, secondary=ShoutAuthor.__tablename__) authors = relationship(lambda: User, secondary=ShoutAuthor.__tablename__)
topics = relationship(lambda: Topic, secondary=ShoutTopic.__tablename__) topics = relationship(lambda: Topic, secondary=ShoutTopic.__tablename__)
views = Column(Integer, default=0)
reactions = relationship(lambda: Reaction) reactions = relationship(lambda: Reaction)
# TODO: these field should be used or modified # TODO: these field should be used or modified

View File

@ -1,24 +0,0 @@
from datetime import datetime
from sqlalchemy import Column, DateTime, ForeignKey, Integer
from base.orm import Base, local_session
class ViewedEntry(Base):
__tablename__ = "viewed"
viewer = Column(ForeignKey("user.id"), index=True, default=1)
shout = Column(ForeignKey("shout.id"), index=True, default=1)
amount = Column(Integer, default=1)
createdAt = Column(
DateTime, nullable=False, default=datetime.now, comment="Created at"
)
@staticmethod
def init_table():
with local_session() as session:
entry = {
"amount": 0
}
viewed = ViewedEntry.create(**entry)
session.add(viewed)
session.commit()

View File

@ -8,7 +8,7 @@ from auth.credentials import AuthCredentials
from base.exceptions import ObjectNotExist, OperationNotAllowed from base.exceptions import ObjectNotExist, OperationNotAllowed
from base.orm import local_session from base.orm import local_session
from base.resolvers import query from base.resolvers import query
from orm import ViewedEntry, TopicFollower from orm import TopicFollower
from orm.reaction import Reaction, ReactionKind from orm.reaction import Reaction, ReactionKind
from orm.shout import Shout, ShoutAuthor, ShoutTopic from orm.shout import Shout, ShoutAuthor, ShoutTopic
from orm.user import AuthorFollower from orm.user import AuthorFollower
@ -101,24 +101,8 @@ async def load_shout(_, info, slug=None, shout_id=None):
try: try:
[shout, reacted_stat, commented_stat, rating_stat, last_comment] = session.execute(q).first() [shout, reacted_stat, commented_stat, rating_stat, last_comment] = session.execute(q).first()
viewed_stat_query = select().select_from(
Shout
).where(
Shout.id == shout.id
).join(
ViewedEntry
).group_by(
Shout.id
).add_columns(
func.sum(ViewedEntry.amount).label('viewed_stat')
)
# Debug tip:
# print(viewed_stat_query.compile(compile_kwargs={"literal_binds": True}))
viewed_stat = session.execute(viewed_stat_query).scalar()
shout.stat = { shout.stat = {
"viewed": viewed_stat, "viewed": shout.views,
"reacted": reacted_stat, "reacted": reacted_stat,
"commented": commented_stat, "commented": commented_stat,
"rating": rating_stat "rating": rating_stat
@ -133,23 +117,6 @@ async def load_shout(_, info, slug=None, shout_id=None):
raise ObjectNotExist("Slug was not found: %s" % slug) raise ObjectNotExist("Slug was not found: %s" % slug)
def add_viewed_stat(session, shouts_map):
viewed_stat_query = select(
Shout.id
).where(
Shout.id.in_(shouts_map.keys())
).join(
ViewedEntry
).group_by(
Shout.id
).add_columns(
func.sum(ViewedEntry.amount).label('viewed_stat')
)
for [shout_id, viewed_stat] in session.execute(viewed_stat_query).unique():
shouts_map[shout_id].stat['viewed'] = viewed_stat
@query.field("loadShouts") @query.field("loadShouts")
async def load_shouts_by(_, info, options): async def load_shouts_by(_, info, options):
""" """
@ -199,15 +166,13 @@ async def load_shouts_by(_, info, options):
for [shout, reacted_stat, commented_stat, rating_stat, last_comment] in session.execute(q).unique(): for [shout, reacted_stat, commented_stat, rating_stat, last_comment] in session.execute(q).unique():
shouts.append(shout) shouts.append(shout)
shout.stat = { shout.stat = {
"viewed": 0, "viewed": shout.views,
"reacted": reacted_stat, "reacted": reacted_stat,
"commented": commented_stat, "commented": commented_stat,
"rating": rating_stat "rating": rating_stat
} }
shouts_map[shout.id] = shout shouts_map[shout.id] = shout
add_viewed_stat(session, shouts_map)
return shouts return shouts
@ -277,13 +242,11 @@ async def get_my_feed(_, info, options):
for [shout, reacted_stat, commented_stat, rating_stat, last_comment] in session.execute(q).unique(): for [shout, reacted_stat, commented_stat, rating_stat, last_comment] in session.execute(q).unique():
shouts.append(shout) shouts.append(shout)
shout.stat = { shout.stat = {
"viewed": 0, "viewed": shout.views,
"reacted": reacted_stat, "reacted": reacted_stat,
"commented": commented_stat, "commented": commented_stat,
"rating": rating_stat "rating": rating_stat
} }
shouts_map[shout.id] = shout shouts_map[shout.id] = shout
add_viewed_stat(session, shouts_map)
return shouts return shouts

View File

@ -11,7 +11,6 @@ from sqlalchemy import func
from base.orm import local_session from base.orm import local_session
from orm import User, Topic from orm import User, Topic
from orm.shout import ShoutTopic, Shout from orm.shout import ShoutTopic, Shout
from orm.viewed import ViewedEntry
load_facts = gql(""" load_facts = gql("""
query getDomains { query getDomains {
@ -128,10 +127,7 @@ class ViewedStorage:
with local_session() as session: with local_session() as session:
try: try:
shout = session.query(Shout).where(Shout.slug == shout_slug).one() shout = session.query(Shout).where(Shout.slug == shout_slug).one()
shout_views = session.query(func.sum(ViewedEntry.amount)).where( self.by_shouts[shout_slug] = shout.views
ViewedEntry.shout == shout.id
).all()[0][0]
self.by_shouts[shout_slug] = shout_views
self.update_topics(session, shout_slug) self.update_topics(session, shout_slug)
except Exception as e: except Exception as e:
raise e raise e
@ -160,37 +156,17 @@ class ViewedStorage:
self.by_topics[topic.slug][shout_slug] = self.by_shouts[shout_slug] self.by_topics[topic.slug][shout_slug] = self.by_shouts[shout_slug]
@staticmethod @staticmethod
async def increment(shout_slug, amount=1, viewer='anonymous'): async def increment(shout_slug, amount=1):
""" the only way to change views counter """ """ the only way to change views counter """
self = ViewedStorage self = ViewedStorage
async with self.lock: async with self.lock:
# TODO optimize, currenty we execute 1 DB transaction per shout
with local_session() as session: with local_session() as session:
# TODO: user slug -> id shout = session.query(Shout).where(Shout.slug == shout_slug).one()
viewed = session.query( shout.views += amount
ViewedEntry
).join(
Shout, Shout.id == ViewedEntry.shout
).join(
User, User.id == ViewedEntry.viewer
).filter(
User.slug == viewer,
Shout.slug == shout_slug
).first()
if viewed:
viewed.amount = amount
print("amount: %d" % amount)
else:
shout = session.query(Shout).where(Shout.slug == shout_slug).one()
viewer = session.query(User).where(User.slug == viewer).one()
new_viewed = ViewedEntry.create(**{
"viewer": viewer.id,
"shout": shout.id,
"amount": amount
})
session.add(new_viewed)
session.commit() session.commit()
# this part is currently unused
self.by_shouts[shout_slug] = self.by_shouts.get(shout_slug, 0) + amount self.by_shouts[shout_slug] = self.by_shouts.get(shout_slug, 0) + amount
self.update_topics(session, shout_slug) self.update_topics(session, shout_slug)