model changes

This commit is contained in:
Igor Lobanov 2022-11-29 13:36:46 +01:00
parent b840823fce
commit 226aeddecd
20 changed files with 254 additions and 166 deletions

1
.gitignore vendored
View File

@ -148,3 +148,4 @@ dump
*dump.sql *dump.sql
*.csv *.csv
dev-server-status.txt dev-server-status.txt
/resetdb.sh

View File

@ -119,8 +119,7 @@ async def shouts_handle(storage, args):
# print main counter # print main counter
counter += 1 counter += 1
line = str(counter + 1) + ": " + shout["slug"] + " @" + author["slug"] print('[migration] shouts_handle %d: %s @%s' % ((counter + 1), shout["slug"], author["slug"]))
print(line)
b = bs4.BeautifulSoup(shout["body"], "html.parser") b = bs4.BeautifulSoup(shout["body"], "html.parser")
texts = [shout["title"].lower().replace(r"[^а-яА-Яa-zA-Z]", "")] texts = [shout["title"].lower().replace(r"[^а-яА-Яa-zA-Z]", "")]

View File

@ -5,8 +5,8 @@ from dateutil.parser import parse as date_parse
from base.orm import local_session from base.orm import local_session
from migration.html2text import html2text from migration.html2text import html2text
from orm.reaction import Reaction, ReactionKind from orm.reaction import Reaction, ReactionKind
from orm.shout import ShoutReactionsFollower from orm.shout import ShoutReactionsFollower, Shout
from orm.topic import TopicFollower from orm.topic import TopicFollower, Topic
from orm.user import User from orm.user import User
ts = datetime.now(tz=timezone.utc) ts = datetime.now(tz=timezone.utc)
@ -69,8 +69,13 @@ async def migrate(entry, storage):
author = session.query(User).filter(User.oid == entry["createdBy"]).first() author = session.query(User).filter(User.oid == entry["createdBy"]).first()
shout_dict = storage["shouts"]["by_oid"][shout_oid] shout_dict = storage["shouts"]["by_oid"][shout_oid]
if shout_dict: if shout_dict:
reaction_dict["shout"] = shout_dict["slug"] shout = session.query(
reaction_dict["createdBy"] = author.slug if author else "discours" Shout
).where(Shout.slug == shout_dict["slug"]).one()
reaction_dict["shout_id"] = shout.id
reaction_dict["createdBy"] = author.id if author else 1
reaction_dict["kind"] = ReactionKind.COMMENT reaction_dict["kind"] = ReactionKind.COMMENT
# creating reaction from old comment # creating reaction from old comment
@ -80,15 +85,20 @@ async def migrate(entry, storage):
# creating shout's reactions following for reaction author # creating shout's reactions following for reaction author
following1 = session.query( following1 = session.query(
ShoutReactionsFollower ShoutReactionsFollower
).join(
User
).join(
Shout
).where( ).where(
ShoutReactionsFollower.follower == reaction_dict["createdBy"] User.id == reaction_dict["createdBy"]
).filter( ).filter(
ShoutReactionsFollower.shout == reaction.shout ShoutReactionsFollower.shout_id == reaction.shout_id
).first() ).first()
if not following1: if not following1:
following1 = ShoutReactionsFollower.create( following1 = ShoutReactionsFollower.create(
follower=reaction_dict["createdBy"], follower_id=reaction_dict["createdBy"],
shout=reaction.shout, shout_id=reaction.shout_id,
auto=True auto=True
) )
session.add(following1) session.add(following1)
@ -97,15 +107,22 @@ async def migrate(entry, storage):
for t in shout_dict["topics"]: for t in shout_dict["topics"]:
tf = session.query( tf = session.query(
TopicFollower TopicFollower
).join(
Topic
).where( ).where(
TopicFollower.follower == reaction_dict["createdBy"] TopicFollower.follower_id == reaction_dict["createdBy"]
).filter( ).filter(
TopicFollower.topic == t Topic.slug == t
).first() ).first()
if not tf: if not tf:
topic = session.query(
Topic
).where(Topic.slug == t).one()
topic_following = TopicFollower.create( topic_following = TopicFollower.create(
follower=reaction_dict["createdBy"], follower_id=reaction_dict["createdBy"],
topic=t, topic_id=topic.id,
auto=True auto=True
) )
session.add(topic_following) session.add(topic_following)
@ -114,16 +131,16 @@ async def migrate(entry, storage):
for comment_rating_old in entry.get("ratings", []): for comment_rating_old in entry.get("ratings", []):
rater = ( rater = (
session.query(User) session.query(User)
.filter(User.oid == comment_rating_old["createdBy"]) .filter(User.oid == comment_rating_old["createdBy"])
.first() .first()
) )
re_reaction_dict = { re_reaction_dict = {
"shout": reaction_dict["shout"], "shout_id": reaction_dict["shout_id"],
"replyTo": reaction.id, "replyTo": reaction.id,
"kind": ReactionKind.LIKE "kind": ReactionKind.LIKE
if comment_rating_old["value"] > 0 if comment_rating_old["value"] > 0
else ReactionKind.DISLIKE, else ReactionKind.DISLIKE,
"createdBy": rater.slug if rater else "discours", "createdBy": rater.id if rater else 1,
} }
cts = comment_rating_old.get("createdAt") cts = comment_rating_old.get("createdAt")
if cts: if cts:
@ -134,14 +151,14 @@ async def migrate(entry, storage):
following2 = session.query( following2 = session.query(
ShoutReactionsFollower ShoutReactionsFollower
).where( ).where(
ShoutReactionsFollower.follower == re_reaction_dict['createdBy'] ShoutReactionsFollower.follower_id == re_reaction_dict['createdBy']
).filter( ).filter(
ShoutReactionsFollower.shout == rr.shout ShoutReactionsFollower.shout_id == rr.shout_id
).first() ).first()
if not following2: if not following2:
following2 = ShoutReactionsFollower.create( following2 = ShoutReactionsFollower.create(
follower=re_reaction_dict['createdBy'], follower_id=re_reaction_dict['createdBy'],
shout=rr.shout, shout_id=rr.shout_id,
auto=True auto=True
) )
session.add(following2) session.add(following2)
@ -163,22 +180,26 @@ def migrate_2stage(rr, old_new_id):
reply_oid = rr.get("replyTo") reply_oid = rr.get("replyTo")
if not reply_oid: if not reply_oid:
return return
new_id = old_new_id.get(rr.get("oid")) new_id = old_new_id.get(rr.get("oid"))
if not new_id: if not new_id:
return return
with local_session() as session: with local_session() as session:
comment = session.query(Reaction).filter(Reaction.id == new_id).first() comment = session.query(Reaction).filter(Reaction.id == new_id).first()
comment.replyTo = old_new_id.get(reply_oid) comment.replyTo = old_new_id.get(reply_oid)
session.add(comment) session.add(comment)
srf = session.query(ShoutReactionsFollower).where( srf = session.query(ShoutReactionsFollower).where(
ShoutReactionsFollower.shout == comment.shout ShoutReactionsFollower.shout_id == comment.shout_id
).filter( ).filter(
ShoutReactionsFollower.follower == comment.createdBy ShoutReactionsFollower.follower_id == comment.createdBy
).first() ).first()
if not srf: if not srf:
srf = ShoutReactionsFollower.create(shout=comment.shout, follower=comment.createdBy, auto=True) srf = ShoutReactionsFollower.create(shout_id=comment.shout_id, follower_id=comment.createdBy, auto=True)
session.add(srf) session.add(srf)
session.commit() session.commit()
if not rr["body"]: if not rr["body"]:
raise Exception(rr) raise Exception(rr)

View File

@ -8,7 +8,7 @@ from migration.extract import extract_html, extract_media
from orm.reaction import Reaction, ReactionKind from orm.reaction import Reaction, ReactionKind
from orm.shout import Shout, ShoutTopic, ShoutReactionsFollower from orm.shout import Shout, ShoutTopic, ShoutReactionsFollower
from orm.user import User from orm.user import User
from orm.topic import TopicFollower from orm.topic import TopicFollower, Topic
from services.stat.viewed import ViewedStorage from services.stat.viewed import ViewedStorage
OLD_DATE = "2016-03-05 22:22:00.350000" OLD_DATE = "2016-03-05 22:22:00.350000"
@ -41,10 +41,10 @@ def create_author_from_app(app):
name = app.get('name') name = app.get('name')
slug = ( slug = (
translit(name, "ru", reversed=True) translit(name, "ru", reversed=True)
.replace(" ", "-") .replace(" ", "-")
.replace("'", "") .replace("'", "")
.replace(".", "-") .replace(".", "-")
.lower() .lower()
) )
# check if nameslug is used # check if nameslug is used
user = session.query(User).where(User.slug == slug).first() user = session.query(User).where(User.slug == slug).first()
@ -84,13 +84,19 @@ def create_author_from_app(app):
async def create_shout(shout_dict, userslug): async def create_shout(shout_dict, userslug):
s = Shout.create(**shout_dict) s = Shout.create(**shout_dict)
with local_session() as session: with local_session() as session:
srf = session.query(ShoutReactionsFollower).where( follower = session.query(User).where(User.slug == userslug).one()
ShoutReactionsFollower.shout == s.slug
srf = session.query(
ShoutReactionsFollower
).join(
User
).where(
ShoutReactionsFollower.shout_id == s.id
).filter( ).filter(
ShoutReactionsFollower.follower == userslug User.slug == userslug
).first() ).first()
if not srf: if not srf:
srf = ShoutReactionsFollower.create(shout=s.slug, follower=userslug, auto=True) srf = ShoutReactionsFollower.create(shout_id=s.id, follower_id=follower.id, auto=True)
session.add(srf) session.add(srf)
session.commit() session.commit()
@ -214,17 +220,21 @@ async def add_topics_follower(entry, storage, userslug):
with local_session() as session: with local_session() as session:
for tpc in topics: for tpc in topics:
try: try:
topic = session.query(Topic).where(Topic.slug == tpc).one()
follower = session.query(User).where(User.slug == userslug).one()
tf = session.query( tf = session.query(
TopicFollower TopicFollower
).where( ).where(
TopicFollower.follower == userslug TopicFollower.follower_id == follower.id
).filter( ).filter(
TopicFollower.topic == tpc TopicFollower.topic_id == topic.id
).first() ).first()
if not tf: if not tf:
tf = TopicFollower.create( tf = TopicFollower.create(
topic=tpc, topic_id=topic.id,
follower=userslug, follower_id=follower.id,
auto=True auto=True
) )
session.add(tf) session.add(tf)
@ -300,27 +310,35 @@ async def topics_aftermath(entry, storage):
for tpc in filter(lambda x: bool(x), entry["topics"]): for tpc in filter(lambda x: bool(x), entry["topics"]):
oldslug = tpc oldslug = tpc
newslug = storage["replacements"].get(oldslug, oldslug) newslug = storage["replacements"].get(oldslug, oldslug)
if newslug: if newslug:
with local_session() as session: with local_session() as session:
shout = session.query(Shout).where(Shout.slug == entry["slug"]).one()
new_topic = session.query(Topic).where(Topic.slug == newslug).one()
shout_topic_old = ( shout_topic_old = (
session.query(ShoutTopic) session.query(ShoutTopic)
.filter(ShoutTopic.shout == entry["slug"]) .join(Shout)
.filter(ShoutTopic.topic == oldslug) .join(Topic)
.first() .filter(Shout.slug == entry["slug"])
.filter(Topic.slug == oldslug)
.first()
) )
if shout_topic_old: if shout_topic_old:
shout_topic_old.update({"slug": newslug}) shout_topic_old.update({"topic_id": new_topic.id})
else: else:
shout_topic_new = ( shout_topic_new = (
session.query(ShoutTopic) session.query(ShoutTopic)
.filter(ShoutTopic.shout == entry["slug"]) .join(Shout)
.filter(ShoutTopic.topic == newslug) .join(Topic)
.first() .filter(Shout.slug == entry["slug"])
.filter(Topic.slug == newslug)
.first()
) )
if not shout_topic_new: if not shout_topic_new:
try: try:
ShoutTopic.create( ShoutTopic.create(
**{"shout": entry["slug"], "topic": newslug} **{"shout_id": shout.id, "topic_id": new_topic.id}
) )
except Exception: except Exception:
print("[migration] shout topic error: " + newslug) print("[migration] shout topic error: " + newslug)
@ -339,31 +357,35 @@ async def content_ratings_to_reactions(entry, slug):
for content_rating in entry.get("ratings", []): for content_rating in entry.get("ratings", []):
rater = ( rater = (
session.query(User) session.query(User)
.filter(User.oid == content_rating["createdBy"]) .filter(User.oid == content_rating["createdBy"])
.first() .first()
) )
reactedBy = ( reactedBy = (
rater rater
if rater if rater
else session.query(User).filter(User.slug == "noname").first() else session.query(User).filter(User.slug == "anonymous").first()
) )
if rater: if rater:
shout = session.query(Shout).where(Shout.slug == slug).one()
reaction_dict = { reaction_dict = {
"kind": ReactionKind.LIKE "kind": ReactionKind.LIKE
if content_rating["value"] > 0 if content_rating["value"] > 0
else ReactionKind.DISLIKE, else ReactionKind.DISLIKE,
"createdBy": reactedBy.slug, "createdBy": reactedBy.id,
"shout": slug, "shout_id": shout.id,
} }
cts = content_rating.get("createdAt") cts = content_rating.get("createdAt")
if cts: if cts:
reaction_dict["createdAt"] = date_parse(cts) reaction_dict["createdAt"] = date_parse(cts)
reaction = ( reaction = (
session.query(Reaction) session.query(Reaction).filter(
.filter(Reaction.shout == reaction_dict["shout"]) Reaction.shout_id == reaction_dict["shout_id"]
.filter(Reaction.createdBy == reaction_dict["createdBy"]) ).filter(
.filter(Reaction.kind == reaction_dict["kind"]) Reaction.createdBy == reaction_dict["createdBy"]
.first() ).filter(
Reaction.kind == reaction_dict["kind"]
).first()
) )
if reaction: if reaction:
k = ReactionKind.AGREE if content_rating["value"] > 0 else ReactionKind.DISAGREE k = ReactionKind.AGREE if content_rating["value"] > 0 else ReactionKind.DISAGREE

View File

@ -115,18 +115,23 @@ def migrate_2stage(entry, id_map):
continue continue
oid = entry["_id"] oid = entry["_id"]
author_slug = id_map.get(oid) author_slug = id_map.get(oid)
user_rating_dict = {
"value": rating_entry["value"],
"rater": rater_slug,
"user": author_slug,
}
with local_session() as session: with local_session() as session:
try: try:
rater = session.query(User).where(User.slug == rater_slug).one()
user = session.query(User).where(User.slug == author_slug).one()
user_rating_dict = {
"value": rating_entry["value"],
"rater_id": rater.id,
"user_id": user.id,
}
user_rating = UserRating.create(**user_rating_dict) user_rating = UserRating.create(**user_rating_dict)
if user_rating_dict['value'] > 0: if user_rating_dict['value'] > 0:
af = AuthorFollower.create( af = AuthorFollower.create(
author=user_rating_dict['user'], author_id=user.id,
follower=user_rating_dict['rater'], follower_id=rater.id,
auto=True auto=True
) )
session.add(af) session.add(af)

View File

@ -11,14 +11,13 @@ class CollabAuthor(Base):
id = None # type: ignore id = None # type: ignore
collab = Column(ForeignKey("collab.id"), primary_key=True) collab = Column(ForeignKey("collab.id"), primary_key=True)
author = Column(ForeignKey("user.slug"), primary_key=True) author_id = Column(ForeignKey("user.id"), primary_key=True)
accepted = Column(Boolean, default=False) accepted = Column(Boolean, default=False)
class Collab(Base): class Collab(Base):
__tablename__ = "collab" __tablename__ = "collab"
authors = Column()
title = Column(String, nullable=True, comment="Title") title = Column(String, nullable=True, comment="Title")
body = Column(String, nullable=True, comment="Body") body = Column(String, nullable=True, comment="Body")
pic = Column(String, nullable=True, comment="Picture") pic = Column(String, nullable=True, comment="Picture")

View File

@ -9,8 +9,8 @@ class ShoutCollection(Base):
__tablename__ = "shout_collection" __tablename__ = "shout_collection"
id = None # type: ignore id = None # type: ignore
shout = Column(ForeignKey("shout.slug"), primary_key=True) shout_id = Column(ForeignKey("shout.id"), primary_key=True)
collection = Column(ForeignKey("collection.slug"), primary_key=True) collection_id = Column(ForeignKey("collection.id"), primary_key=True)
class Collection(Base): class Collection(Base):

View File

@ -8,8 +8,8 @@ class CommunityFollower(Base):
__tablename__ = "community_followers" __tablename__ = "community_followers"
id = None # type: ignore id = None # type: ignore
follower = Column(ForeignKey("user.slug"), primary_key=True) follower_id = Column(ForeignKey("user.id"), primary_key=True)
community = Column(ForeignKey("community.slug"), primary_key=True) community_id = Column(ForeignKey("community.id"), primary_key=True)
joinedAt = Column( joinedAt = Column(
DateTime, nullable=False, default=datetime.now, comment="Created at" DateTime, nullable=False, default=datetime.now, comment="Created at"
) )

View File

@ -28,12 +28,12 @@ class Reaction(Base):
createdAt = Column( createdAt = Column(
DateTime, nullable=False, default=datetime.now, comment="Created at" DateTime, nullable=False, default=datetime.now, comment="Created at"
) )
createdBy = Column(ForeignKey("user.slug"), nullable=False, comment="Sender") createdBy = Column(ForeignKey("user.id"), nullable=False, index=True, comment="Sender")
updatedAt = Column(DateTime, nullable=True, comment="Updated at") updatedAt = Column(DateTime, nullable=True, comment="Updated at")
updatedBy = Column(ForeignKey("user.slug"), nullable=True, comment="Last Editor") updatedBy = Column(ForeignKey("user.id"), nullable=True, index=True, comment="Last Editor")
deletedAt = Column(DateTime, nullable=True, comment="Deleted at") deletedAt = Column(DateTime, nullable=True, comment="Deleted at")
deletedBy = Column(ForeignKey("user.slug"), nullable=True, comment="Deleted by") deletedBy = Column(ForeignKey("user.id"), nullable=True, index=True, comment="Deleted by")
shout = Column(ForeignKey("shout.slug"), nullable=False) shout_id = Column(ForeignKey("shout.id"), nullable=False, index=True)
replyTo = Column( replyTo = Column(
ForeignKey("reaction.id"), nullable=True, comment="Reply to reaction ID" ForeignKey("reaction.id"), nullable=True, comment="Reply to reaction ID"
) )

View File

@ -13,16 +13,16 @@ class ShoutTopic(Base):
__tablename__ = "shout_topic" __tablename__ = "shout_topic"
id = None # type: ignore id = None # type: ignore
shout = Column(ForeignKey("shout.slug"), primary_key=True) shout_id = Column(ForeignKey("shout.id"), primary_key=True, index=True)
topic = Column(ForeignKey("topic.slug"), primary_key=True) topic_id = Column(ForeignKey("topic.id"), primary_key=True, index=True)
class ShoutReactionsFollower(Base): class ShoutReactionsFollower(Base):
__tablename__ = "shout_reactions_followers" __tablename__ = "shout_reactions_followers"
id = None # type: ignore id = None # type: ignore
follower = Column(ForeignKey("user.slug"), primary_key=True) follower_id = Column(ForeignKey("user.id"), primary_key=True, index=True)
shout = Column(ForeignKey("shout.slug"), primary_key=True) shout_id = Column(ForeignKey("shout.id"), primary_key=True, index=True)
auto = Column(Boolean, nullable=False, default=False) auto = Column(Boolean, nullable=False, default=False)
createdAt = Column( createdAt = Column(
DateTime, nullable=False, default=datetime.now, comment="Created at" DateTime, nullable=False, default=datetime.now, comment="Created at"
@ -34,8 +34,8 @@ class ShoutAuthor(Base):
__tablename__ = "shout_author" __tablename__ = "shout_author"
id = None # type: ignore id = None # type: ignore
shout = Column(ForeignKey("shout.slug"), primary_key=True) shout_id = Column(ForeignKey("shout.id"), primary_key=True, index=True)
user = Column(ForeignKey("user.slug"), primary_key=True) user_id = Column(ForeignKey("user.id"), primary_key=True, index=True)
caption = Column(String, nullable=True, default="") caption = Column(String, nullable=True, default="")
@ -55,7 +55,7 @@ class Shout(Base):
topics = relationship(lambda: Topic, secondary=ShoutTopic.__tablename__) topics = relationship(lambda: Topic, secondary=ShoutTopic.__tablename__)
reactions = relationship(lambda: Reaction) reactions = relationship(lambda: Reaction)
visibility = Column(String, nullable=True) # owner authors community public visibility = Column(String, nullable=True) # owner authors community public
versionOf = Column(ForeignKey("shout.slug"), nullable=True) versionOf = Column(ForeignKey("shout.id"), nullable=True)
oid = Column(String, nullable=True) oid = Column(String, nullable=True)
media = Column(JSON, nullable=True) media = Column(JSON, nullable=True)

View File

@ -9,8 +9,8 @@ class TopicFollower(Base):
__tablename__ = "topic_followers" __tablename__ = "topic_followers"
id = None # type: ignore id = None # type: ignore
follower = Column(ForeignKey("user.slug"), primary_key=True) follower_id = Column(ForeignKey("user.id"), primary_key=True, index=True)
topic = Column(ForeignKey("topic.slug"), primary_key=True) topic_id = Column(ForeignKey("topic.id"), primary_key=True, index=True)
createdAt = Column( createdAt = Column(
DateTime, nullable=False, default=datetime.now, comment="Created at" DateTime, nullable=False, default=datetime.now, comment="Created at"
) )

View File

@ -6,7 +6,6 @@ from sqlalchemy.orm import relationship
from base.orm import Base, local_session from base.orm import Base, local_session
from orm.rbac import Role from orm.rbac import Role
from services.auth.roles import RoleStorage
class UserNotifications(Base): class UserNotifications(Base):
@ -21,8 +20,8 @@ class UserRating(Base):
__tablename__ = "user_rating" __tablename__ = "user_rating"
id = None # type: ignore id = None # type: ignore
rater = Column(ForeignKey("user.slug"), primary_key=True) rater_id = Column(ForeignKey("user.id"), primary_key=True, index=True)
user = Column(ForeignKey("user.slug"), primary_key=True) user_id = Column(ForeignKey("user.id"), primary_key=True, index=True)
value = Column(Integer) value = Column(Integer)
@staticmethod @staticmethod
@ -34,16 +33,16 @@ class UserRole(Base):
__tablename__ = "user_role" __tablename__ = "user_role"
id = None # type: ignore id = None # type: ignore
user_id = Column(ForeignKey("user.id"), primary_key=True) user_id = Column(ForeignKey("user.id"), primary_key=True, index=True)
role_id = Column(ForeignKey("role.id"), primary_key=True) role_id = Column(ForeignKey("role.id"), primary_key=True, index=True)
class AuthorFollower(Base): class AuthorFollower(Base):
__tablename__ = "author_follower" __tablename__ = "author_follower"
id = None # type: ignore id = None # type: ignore
follower = Column(ForeignKey("user.slug"), primary_key=True) follower_id = Column(ForeignKey("user.id"), primary_key=True, index=True)
author = Column(ForeignKey("user.slug"), primary_key=True) author_id = Column(ForeignKey("user.id"), primary_key=True, index=True)
createdAt = Column( createdAt = Column(
DateTime, nullable=False, default=datetime.now, comment="Created at" DateTime, nullable=False, default=datetime.now, comment="Created at"
) )
@ -73,7 +72,7 @@ class User(Base):
links = Column(JSONType, nullable=True, comment="Links") links = Column(JSONType, nullable=True, comment="Links")
oauth = Column(String, nullable=True) oauth = Column(String, nullable=True)
notifications = relationship(lambda: UserNotifications) notifications = relationship(lambda: UserNotifications)
ratings = relationship(UserRating, foreign_keys=UserRating.user) ratings = relationship(UserRating, foreign_keys=UserRating.user_id)
roles = relationship(lambda: Role, secondary=UserRole.__tablename__) roles = relationship(lambda: Role, secondary=UserRole.__tablename__)
oid = Column(String, nullable=True) oid = Column(String, nullable=True)

View File

@ -6,8 +6,8 @@ from base.orm import Base, local_session
class ViewedEntry(Base): class ViewedEntry(Base):
__tablename__ = "viewed" __tablename__ = "viewed"
viewer = Column(ForeignKey("user.slug"), default='anonymous') viewer_id = Column(ForeignKey("user.id"), index=True, default=1)
shout = Column(ForeignKey("shout.slug"), default="genesis-block") shout_id = Column(ForeignKey("shout.id"), index=True, default=1)
amount = Column(Integer, default=1) amount = Column(Integer, default=1)
createdAt = Column( createdAt = Column(
DateTime, nullable=False, default=datetime.now, comment="Created at" DateTime, nullable=False, default=datetime.now, comment="Created at"

View File

@ -1,11 +1,13 @@
from datetime import datetime, timezone from datetime import datetime, timezone
from sqlalchemy import and_
from auth.authenticate import login_required from auth.authenticate import login_required
from base.orm import local_session from base.orm import local_session
from base.resolvers import mutation from base.resolvers import mutation
from orm.rbac import Resource from orm.rbac import Resource
from orm.shout import Shout, ShoutAuthor, ShoutTopic from orm.shout import Shout, ShoutAuthor, ShoutTopic
from orm.topic import TopicFollower from orm.topic import TopicFollower, Topic
from orm.user import User from orm.user import User
from resolvers.zine.reactions import reactions_follow, reactions_unfollow from resolvers.zine.reactions import reactions_follow, reactions_unfollow
from services.zine.gittask import GitTask from services.zine.gittask import GitTask
@ -24,7 +26,7 @@ async def create_shout(_, info, inp):
new_shout = Shout.create(**inp) new_shout = Shout.create(**inp)
# NOTE: shout made by one first author # NOTE: shout made by one first author
sa = ShoutAuthor.create(shout=new_shout.slug, user=user.slug) sa = ShoutAuthor.create(shout_id=new_shout.id, user_id=user.id)
session.add(sa) session.add(sa)
reactions_follow(user, new_shout.slug, True) reactions_follow(user, new_shout.slug, True)
@ -33,11 +35,16 @@ async def create_shout(_, info, inp):
topic_slugs.append(inp["mainTopic"]) topic_slugs.append(inp["mainTopic"])
for slug in topic_slugs: for slug in topic_slugs:
st = ShoutTopic.create(shout=new_shout.slug, topic=slug) topic = session.query(Topic).where(Topic.slug == slug).one()
st = ShoutTopic.create(shout_id=new_shout.id, topic_id=topic.id)
session.add(st) session.add(st)
tf = session.query(TopicFollower).where(follower=user.slug, topic=slug) tf = session.query(TopicFollower).where(
and_(TopicFollower.follower_id == user.id, TopicFollower.topic_id == topic.id)
)
if not tf: if not tf:
tf = TopicFollower.create(follower=user.slug, topic=slug, auto=True) tf = TopicFollower.create(follower_id=user.id, topic_id=topic.id, auto=True)
session.add(tf) session.add(tf)
new_shout.topic_slugs = topic_slugs new_shout.topic_slugs = topic_slugs
@ -45,7 +52,7 @@ async def create_shout(_, info, inp):
session.commit() session.commit()
GitTask(inp, user.username, user.email, "new shout %s" % (new_shout.slug)) GitTask(inp, user.username, user.email, "new shout %s" % new_shout.slug)
return {"shout": new_shout} return {"shout": new_shout}
@ -75,7 +82,7 @@ async def update_shout(_, info, inp):
session.add(shout) session.add(shout)
if inp.get("topics"): if inp.get("topics"):
# remove old links # remove old links
links = session.query(ShoutTopic).where(ShoutTopic.shout == slug).all() links = session.query(ShoutTopic).where(ShoutTopic.shout_id == shout.id).all()
for topiclink in links: for topiclink in links:
session.delete(topiclink) session.delete(topiclink)
# add new topic links # add new topic links

View File

@ -4,7 +4,7 @@ from auth.authenticate import login_required
from base.redis import redis from base.redis import redis
from base.resolvers import query from base.resolvers import query
from base.orm import local_session from base.orm import local_session
from orm.user import AuthorFollower from orm.user import AuthorFollower, User
@query.field("searchRecipients") @query.field("searchRecipients")
@ -30,13 +30,19 @@ async def search_recipients(_, info, query: str, limit: int = 50, offset: int =
with local_session() as session: with local_session() as session:
# followings # followings
result += session.query(AuthorFollower.author).where(AuthorFollower.follower.startswith(query))\ result += session.query(AuthorFollower.author).join(
.offset(offset + len(result)).limit(more_amount) User, User.id == AuthorFollower.follower_id
).where(
User.slug.startswith(query)
).offset(offset + len(result)).limit(more_amount)
more_amount = limit more_amount = limit
# followers # followers
result += session.query(AuthorFollower.follower).where(AuthorFollower.author.startswith(query))\ result += session.query(AuthorFollower.follower).join(
.offset(offset + len(result)).limit(offset + len(result) + limit) User, User.id == AuthorFollower.author_id
).where(
User.slug.startswith(query)
).offset(offset + len(result)).limit(offset + len(result) + limit)
return { return {
"members": list(result), "members": list(result),
"error": None "error": None

View File

@ -1,11 +1,11 @@
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
from sqlalchemy.orm import joinedload, aliased from sqlalchemy.orm import joinedload, aliased
from sqlalchemy.sql.expression import desc, asc, select, case, func from sqlalchemy.sql.expression import desc, asc, select, func
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 from orm import ViewedEntry
from orm.shout import Shout, ShoutAuthor from orm.shout import Shout, ShoutAuthor
from orm.reaction import Reaction, ReactionKind from orm.reaction import Reaction
from resolvers.zine._common import add_common_stat_columns from resolvers.zine._common import add_common_stat_columns
@ -18,7 +18,7 @@ def add_stat_columns(q):
def apply_filters(q, filters, user=None): def apply_filters(q, filters, user=None):
if filters.get("reacted") and user: if filters.get("reacted") and user:
q.join(Reaction, Reaction.createdBy == user.slug) q.join(Reaction, Reaction.createdBy == user.id)
v = filters.get("visibility") v = filters.get("visibility")
if v == "public": if v == "public":
@ -66,9 +66,9 @@ async def load_shout(_, info, slug):
"rating": rating_stat "rating": rating_stat
} }
for author_caption in session.query(ShoutAuthor).where(ShoutAuthor.shout == slug): for author_caption in session.query(ShoutAuthor).join(Shout).where(Shout.slug == slug):
for author in shout.authors: for author in shout.authors:
if author.slug == author_caption.user: if author.id == author_caption.user_id:
author.caption = author_caption.caption author.caption = author_caption.caption
return shout return shout

View File

@ -19,17 +19,19 @@ from resolvers.zine.topics import followed_by_user
def add_author_stat_columns(q): def add_author_stat_columns(q):
author_followers = aliased(AuthorFollower) author_followers = aliased(AuthorFollower)
author_following = aliased(AuthorFollower) author_following = aliased(AuthorFollower)
shout_author_aliased = aliased(ShoutAuthor)
user_rating_aliased = aliased(UserRating)
q = q.outerjoin(ShoutAuthor).add_columns( q = q.outerjoin(shout_author_aliased).add_columns(
func.count(distinct(ShoutAuthor.shout)).label('shouts_stat') func.count(distinct(shout_author_aliased.shout_id)).label('shouts_stat')
).outerjoin(author_followers, author_followers.author == User.slug).add_columns( ).outerjoin(author_followers, author_followers.author_id == User.id).add_columns(
func.count(distinct(author_followers.follower)).label('followers_stat') func.count(distinct(author_followers.follower_id)).label('followers_stat')
).outerjoin(author_following, author_following.follower == User.slug).add_columns( ).outerjoin(author_following, author_following.follower_id == User.id).add_columns(
func.count(distinct(author_following.author)).label('followings_stat') func.count(distinct(author_following.author_id)).label('followings_stat')
).outerjoin(UserRating).add_columns( ).outerjoin(user_rating_aliased, user_rating_aliased.user_id == User.id).add_columns(
# TODO: check # TODO: check
func.sum(UserRating.value).label('rating_stat') func.sum(user_rating_aliased.value).label('rating_stat')
).outerjoin(Reaction, and_(Reaction.createdBy == User.slug, Reaction.body.is_not(None))).add_columns( ).outerjoin(Reaction, and_(Reaction.createdBy == User.id, Reaction.body.is_not(None))).add_columns(
func.count(distinct(Reaction.id)).label('commented_stat') func.count(distinct(Reaction.id)).label('commented_stat')
) )
@ -83,7 +85,7 @@ async def followed_reactions(slug):
return session.query( return session.query(
Reaction.shout Reaction.shout
).where( ).where(
Reaction.createdBy == slug Reaction.createdBy == user.id
).filter( ).filter(
Reaction.createdAt > user.lastSeen Reaction.createdAt > user.lastSeen
).all() ).all()
@ -107,7 +109,7 @@ async def get_followed_authors(_, _info, slug) -> List[User]:
async def followed_authors(slug) -> List[User]: async def followed_authors(slug) -> List[User]:
q = select(User) q = select(User)
q = add_author_stat_columns(q) q = add_author_stat_columns(q)
q = q.join(AuthorFollower).where(AuthorFollower.follower == slug) q = q.join(AuthorFollower).join(User, User.id == AuthorFollower.follower_id).where(User.slug == slug)
return get_authors_from_query(q) return get_authors_from_query(q)
@ -116,7 +118,13 @@ async def followed_authors(slug) -> List[User]:
async def user_followers(_, _info, slug) -> List[User]: async def user_followers(_, _info, slug) -> List[User]:
q = select(User) q = select(User)
q = add_author_stat_columns(q) q = add_author_stat_columns(q)
q = q.join(AuthorFollower).where(AuthorFollower.author == slug)
aliased_user = aliased(User)
q = q.join(AuthorFollower).join(
aliased_user, aliased_user.id == AuthorFollower.author_id
).where(
aliased_user.slug == slug
)
return get_authors_from_query(q) return get_authors_from_query(q)
@ -173,7 +181,8 @@ async def rate_user(_, info, rated_userslug, value):
# for mutation.field("follow") # for mutation.field("follow")
def author_follow(user, slug): def author_follow(user, slug):
with local_session() as session: with local_session() as session:
af = AuthorFollower.create(follower=user.slug, author=slug) author = session.query(User).where(User.slug == slug).one()
af = AuthorFollower.create(follower_id=user.id, author_id=author.id)
session.add(af) session.add(af)
session.commit() session.commit()
@ -182,13 +191,13 @@ def author_follow(user, slug):
def author_unfollow(user, slug): def author_unfollow(user, slug):
with local_session() as session: with local_session() as session:
flw = ( flw = (
session.query(AuthorFollower) session.query(
.filter( AuthorFollower
).join(User, User.id == AuthorFollower.author_id).filter(
and_( and_(
AuthorFollower.follower == user.slug, AuthorFollower.author == slug AuthorFollower.follower_id == user.id, User.slug == slug
) )
) ).first()
.first()
) )
if not flw: if not flw:
raise Exception("[resolvers.profile] follower not exist, cant unfollow") raise Exception("[resolvers.profile] follower not exist, cant unfollow")
@ -224,7 +233,7 @@ async def load_authors_by(_, info, by, limit, offset):
elif by.get("name"): elif by.get("name"):
q = q.filter(User.name.ilike(f"%{by['name']}%")) q = q.filter(User.name.ilike(f"%{by['name']}%"))
elif by.get("topic"): elif by.get("topic"):
q = q.join(ShoutAuthor).join(ShoutTopic).where(ShoutTopic.topic == by["topic"]) q = q.join(ShoutAuthor).join(ShoutTopic).join(Topic).where(Topic.slug == by["topic"])
if by.get("lastSeen"): # in days if by.get("lastSeen"): # in days
days_before = datetime.now(tz=timezone.utc) - timedelta(days=by["lastSeen"]) days_before = datetime.now(tz=timezone.utc) - timedelta(days=by["lastSeen"])
q = q.filter(User.lastSeen > days_before) q = q.filter(User.lastSeen > days_before)

View File

@ -16,16 +16,19 @@ def add_reaction_stat_columns(q):
def reactions_follow(user: User, slug: str, auto=False): def reactions_follow(user: User, slug: str, auto=False):
with local_session() as session: with local_session() as session:
shout = session.query(Shout).where(Shout.slug == slug).one()
following = ( following = (
session.query(ShoutReactionsFollower).where(and_( session.query(ShoutReactionsFollower).where(and_(
ShoutReactionsFollower.follower == user.slug, ShoutReactionsFollower.follower_id == user.id,
ShoutReactionsFollower.shout == slug ShoutReactionsFollower.shout_id == shout.id,
)).first() )).first()
) )
if not following: if not following:
following = ShoutReactionsFollower.create( following = ShoutReactionsFollower.create(
follower=user.slug, follower_id=user.id,
shout=slug, shout_id=shout.id,
auto=auto auto=auto
) )
session.add(following) session.add(following)
@ -34,12 +37,15 @@ def reactions_follow(user: User, slug: str, auto=False):
def reactions_unfollow(user, slug): def reactions_unfollow(user, slug):
with local_session() as session: with local_session() as session:
shout = session.query(Shout).where(Shout.slug == slug).one()
following = ( following = (
session.query(ShoutReactionsFollower).where(and_( session.query(ShoutReactionsFollower).where(and_(
ShoutReactionsFollower.follower == user.slug, ShoutReactionsFollower.follower_id == user.id,
ShoutReactionsFollower.shout == slug ShoutReactionsFollower.shout_id == shout.id
)).first() )).first()
) )
if following: if following:
session.delete(following) session.delete(following)
session.commit() session.commit()
@ -68,7 +74,7 @@ def check_to_publish(session, user, reaction):
]: ]:
if is_published_author(user): if is_published_author(user):
# now count how many approvers are voted already # now count how many approvers are voted already
approvers_reactions = session.query(Reaction).where(Reaction.shout == reaction.shout).all() approvers_reactions = session.query(Reaction).where(Reaction.shout_id == reaction.shout_id).all()
approvers = [user.slug, ] approvers = [user.slug, ]
for ar in approvers_reactions: for ar in approvers_reactions:
a = ar.createdBy a = ar.createdBy
@ -87,7 +93,7 @@ def check_to_hide(session, user, reaction):
ReactionKind.UNPROOF ReactionKind.UNPROOF
]: ]:
# if is_published_author(user): # if is_published_author(user):
approvers_reactions = session.query(Reaction).where(Reaction.shout == reaction.shout).all() approvers_reactions = session.query(Reaction).where(Reaction.shout_id == reaction.shout_id).all()
declines = 0 declines = 0
for r in approvers_reactions: for r in approvers_reactions:
if r.kind in [ if r.kind in [
@ -224,22 +230,26 @@ async def load_reactions_by(_, _info, by, limit=50, offset=0):
q = select( q = select(
Reaction, CreatedByUser, ReactedShout Reaction, CreatedByUser, ReactedShout
).join( ).join(
CreatedByUser, Reaction.createdBy == CreatedByUser.slug CreatedByUser, Reaction.createdBy == CreatedByUser.id
).join( ).join(
ReactedShout, Reaction.shout == ReactedShout.slug ReactedShout, Reaction.shout_id == ReactedShout.id
) )
if by.get("shout"): if by.get("shout"):
q = q.filter(Reaction.shout == by["shout"]) aliased_shout = aliased(Shout)
q = q.join(aliased_shout).filter(aliased_shout.slug == by["shout"])
elif by.get("shouts"): elif by.get("shouts"):
q = q.filter(Reaction.shout.in_(by["shouts"])) aliased_shout = aliased(Shout)
q = q.join(aliased_shout).filter(aliased_shout.shout.in_(by["shouts"]))
if by.get("createdBy"): if by.get("createdBy"):
q = q.filter(Reaction.createdBy == by.get("createdBy")) aliased_user = aliased(User)
q = q.join(aliased_user).filter(aliased_user.slug == by.get("createdBy"))
if by.get("topic"): if by.get("topic"):
# TODO: check
q = q.filter(Shout.topics.contains(by["topic"])) q = q.filter(Shout.topics.contains(by["topic"]))
if by.get("comment"): if by.get("comment"):
q = q.filter(func.length(Reaction.body) > 0) q = q.filter(func.length(Reaction.body) > 0)
if by.get('search', 0) > 2: if len(by.get('search', '')) > 2:
q = q.filter(Reaction.body.ilike(f'%{by["body"]}%')) q = q.filter(Reaction.body.ilike(f'%{by["body"]}%'))
if by.get("days"): if by.get("days"):
after = datetime.now(tz=timezone.utc) - timedelta(days=int(by["days"]) or 30) after = datetime.now(tz=timezone.utc) - timedelta(days=int(by["days"]) or 30)

View File

@ -4,20 +4,20 @@ from base.orm import local_session
from base.resolvers import mutation, query from base.resolvers import mutation, query
from orm.shout import ShoutTopic, ShoutAuthor from orm.shout import ShoutTopic, ShoutAuthor
from orm.topic import Topic, TopicFollower from orm.topic import Topic, TopicFollower
from orm import Shout from orm import Shout, User
def add_topic_stat_columns(q): def add_topic_stat_columns(q):
q = q.outerjoin(ShoutTopic, Topic.slug == ShoutTopic.topic).add_columns( q = q.outerjoin(ShoutTopic, Topic.id == ShoutTopic.topic_id).add_columns(
func.count(distinct(ShoutTopic.shout)).label('shouts_stat') func.count(distinct(ShoutTopic.shout_id)).label('shouts_stat')
).outerjoin(ShoutAuthor, ShoutTopic.shout == ShoutAuthor.shout).add_columns( ).outerjoin(ShoutAuthor, ShoutTopic.shout_id == ShoutAuthor.shout_id).add_columns(
func.count(distinct(ShoutAuthor.user)).label('authors_stat') func.count(distinct(ShoutAuthor.user_id)).label('authors_stat')
).outerjoin(TopicFollower, ).outerjoin(TopicFollower,
and_( and_(
TopicFollower.topic == Topic.slug, TopicFollower.topic_id == Topic.id,
TopicFollower.follower == ShoutAuthor.user TopicFollower.follower_id == ShoutAuthor.id
)).add_columns( )).add_columns(
func.count(distinct(TopicFollower.follower)).label('followers_stat') func.count(distinct(TopicFollower.follower_id)).label('followers_stat')
) )
q = q.group_by(Topic.id) q = q.group_by(Topic.id)
@ -49,7 +49,7 @@ def get_topics_from_query(q):
def followed_by_user(user_slug): def followed_by_user(user_slug):
q = select(Topic) q = select(Topic)
q = add_topic_stat_columns(q) q = add_topic_stat_columns(q)
q = q.where(TopicFollower.follower == user_slug) q = q.join(User).where(User.slug == user_slug)
return get_topics_from_query(q) return get_topics_from_query(q)
@ -74,7 +74,7 @@ async def topics_by_community(_, info, community):
async def topics_by_author(_, _info, author): async def topics_by_author(_, _info, author):
q = select(Topic) q = select(Topic)
q = add_topic_stat_columns(q) q = add_topic_stat_columns(q)
q = q.where(ShoutAuthor.user == author) q = q.join(User).where(User.slug == author)
return get_topics_from_query(q) return get_topics_from_query(q)
@ -117,7 +117,9 @@ async def update_topic(_, _info, inp):
async def topic_follow(user, slug): async def topic_follow(user, slug):
with local_session() as session: with local_session() as session:
following = TopicFollower.create(topic=slug, follower=user.slug) topic = session.query(Topic).where(Topic.slug == slug).one()
following = TopicFollower.create(topic_id=topic.id, follower=user.id)
session.add(following) session.add(following)
session.commit() session.commit()
@ -125,10 +127,10 @@ async def topic_follow(user, slug):
async def topic_unfollow(user, slug): async def topic_unfollow(user, slug):
with local_session() as session: with local_session() as session:
sub = ( sub = (
session.query(TopicFollower).filter( session.query(TopicFollower).join(Topic).filter(
and_( and_(
TopicFollower.follower == user.slug, TopicFollower.follower_id == user.id,
TopicFollower.topic == slug Topic.slug == slug
) )
).first() ).first()
) )
@ -143,7 +145,7 @@ async def topic_unfollow(user, slug):
async def topics_random(_, info, amount=12): async def topics_random(_, info, amount=12):
q = select(Topic) q = select(Topic)
q = add_topic_stat_columns(q) q = add_topic_stat_columns(q)
q = q.join(Shout, ShoutTopic.shout == Shout.slug).group_by(Topic.id).having(func.count(Shout.id) > 2) q = q.join(Shout, ShoutTopic.shout_id == Shout.id).group_by(Topic.id).having(func.count(Shout.id) > 2)
q = q.order_by(func.random()).limit(amount) q = q.order_by(func.random()).limit(amount)
return get_topics_from_query(q) return get_topics_from_query(q)

View File

@ -5,7 +5,9 @@ from gql import Client, gql
from gql.transport.aiohttp import AIOHTTPTransport from gql.transport.aiohttp import AIOHTTPTransport
from base.orm import local_session from base.orm import local_session
from sqlalchemy import func from sqlalchemy import func
from orm.shout import ShoutTopic
from orm import User, Topic
from orm.shout import ShoutTopic, Shout
from orm.viewed import ViewedEntry from orm.viewed import ViewedEntry
from ssl import create_default_context from ssl import create_default_context
from os import environ, path from os import environ, path
@ -113,6 +115,7 @@ class ViewedStorage:
async with self.lock: async with self.lock:
return self.client.execute_async(load_facts) return self.client.execute_async(load_facts)
# unused yet
@staticmethod @staticmethod
async def get_shout(shout_slug): async def get_shout(shout_slug):
""" getting shout views metric by slug """ """ getting shout views metric by slug """
@ -123,8 +126,9 @@ class ViewedStorage:
shout_views = 0 shout_views = 0
with local_session() as session: with local_session() as session:
try: try:
shout = session.query(Shout).where(Shout.slug == shout_slug).one()
shout_views = session.query(func.sum(ViewedEntry.amount)).where( shout_views = session.query(func.sum(ViewedEntry.amount)).where(
ViewedEntry.shout == shout_slug ViewedEntry.shout_id == shout.id
).all()[0][0] ).all()[0][0]
self.by_shouts[shout_slug] = shout_views self.by_shouts[shout_slug] = shout_views
self.update_topics(session, shout_slug) self.update_topics(session, shout_slug)
@ -147,11 +151,12 @@ class ViewedStorage:
def update_topics(session, shout_slug): def update_topics(session, shout_slug):
""" updates topics counters by shout slug """ """ updates topics counters by shout slug """
self = ViewedStorage self = ViewedStorage
for t in session.query(ShoutTopic).where(ShoutTopic.shout == shout_slug).all(): for [shout_topic, topic] in session.query(ShoutTopic, Topic).join(Topic).join(Shout).where(
tpc = t.topic Shout.slug == shout_slug
if not self.by_topics.get(tpc): ).all():
self.by_topics[tpc] = {} if not self.by_topics.get(topic.slug):
self.by_topics[tpc][shout_slug] = self.by_shouts[shout_slug] self.by_topics[topic.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, viewer='anonymous'):
@ -159,9 +164,12 @@ class ViewedStorage:
self = ViewedStorage self = ViewedStorage
async with self.lock: async with self.lock:
with local_session() as session: with local_session() as session:
shout = session.query(Shout).where(Shout.slug == shout_slug).one()
viewer = session.query(User).where(User.slug == viewer).one()
viewed = ViewedEntry.create(**{ viewed = ViewedEntry.create(**{
"viewer": viewer, "viewer_id": viewer.id,
"shout": shout_slug, "shout_id": shout.id,
"amount": amount "amount": amount
}) })
session.add(viewed) session.add(viewed)