Revert "store views on the shouts table, remove the viewed table"

This commit is contained in:
Ilya Y 2023-08-04 13:12:21 +03:00 committed by GitHub
parent 93552781d5
commit c83b38f4c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 99 additions and 29 deletions

View File

@ -6,6 +6,7 @@ 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
@ -21,6 +22,7 @@ __all__ = [
"Notification", "Notification",
"Reaction", "Reaction",
"UserRating", "UserRating",
"ViewedEntry"
] ]
@ -33,4 +35,5 @@ 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,7 +1,7 @@
from datetime import datetime from datetime import datetime
from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String, JSON from sqlalchemy import Boolean, Column, DateTime, ForeignKey, String, JSON
from sqlalchemy.orm import column_property, relationship from sqlalchemy.orm import relationship
from base.orm import Base, local_session from base.orm import Base, local_session
from orm.reaction import Reaction from orm.reaction import Reaction
@ -61,11 +61,6 @@ 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 from the old Discours website
viewsOld = Column(Integer, default=0)
# views from Ackee tracker on the new Discours website
viewsAckee = Column(Integer, default=0)
views = column_property(viewsOld + viewsAckee)
reactions = relationship(lambda: Reaction) reactions = relationship(lambda: Reaction)
# TODO: these field should be used or modified # TODO: these field should be used or modified

24
orm/viewed.py Normal file
View File

@ -0,0 +1,24 @@
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 TopicFollower from orm import ViewedEntry, 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,8 +101,24 @@ 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": shout.views, "viewed": viewed_stat,
"reacted": reacted_stat, "reacted": reacted_stat,
"commented": commented_stat, "commented": commented_stat,
"rating": rating_stat "rating": rating_stat
@ -117,6 +133,23 @@ 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):
""" """
@ -166,13 +199,15 @@ 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": shout.views, "viewed": 0,
"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
@ -242,11 +277,13 @@ 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": shout.views, "viewed": 0,
"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,6 +11,7 @@ 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 {
@ -127,7 +128,10 @@ 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()
self.by_shouts[shout_slug] = shout.views shout_views = session.query(func.sum(ViewedEntry.amount)).where(
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
@ -156,30 +160,37 @@ 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='ackee'): async def increment(shout_slug, amount=1, viewer='anonymous'):
""" 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
viewed = session.query(
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() shout = session.query(Shout).where(Shout.slug == shout_slug).one()
if viewer == 'old-discours': viewer = session.query(User).where(User.slug == viewer).one()
# this is needed for old db migration new_viewed = ViewedEntry.create(**{
if shout.viewsOld == amount: "viewer": viewer.id,
print(f"viewsOld amount: {amount}") "shout": shout.id,
else: "amount": amount
print(f"viewsOld amount changed: {shout.viewsOld} --> {amount}") })
shout.viewsOld = amount session.add(new_viewed)
else:
if shout.viewsAckee == amount:
print(f"viewsAckee amount: {amount}")
else:
print(f"viewsAckee amount changed: {shout.viewsAckee} --> {amount}")
shout.viewsAckee = amount
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)