diff --git a/.gitignore b/.gitignore index b8332f4b..10b8c415 100644 --- a/.gitignore +++ b/.gitignore @@ -148,3 +148,4 @@ dump *dump.sql *.csv dev-server-status.txt +/resetdb.sh diff --git a/migration/__init__.py b/migration/__init__.py index 4a25931d..eaded31b 100644 --- a/migration/__init__.py +++ b/migration/__init__.py @@ -119,8 +119,7 @@ async def shouts_handle(storage, args): # print main counter counter += 1 - line = str(counter + 1) + ": " + shout["slug"] + " @" + author["slug"] - print(line) + print('[migration] shouts_handle %d: %s @%s' % ((counter + 1), shout["slug"], author["slug"])) b = bs4.BeautifulSoup(shout["body"], "html.parser") texts = [shout["title"].lower().replace(r"[^а-яА-Яa-zA-Z]", "")] diff --git a/migration/tables/comments.py b/migration/tables/comments.py index 9fb099f7..09cfb491 100644 --- a/migration/tables/comments.py +++ b/migration/tables/comments.py @@ -5,8 +5,8 @@ from dateutil.parser import parse as date_parse from base.orm import local_session from migration.html2text import html2text from orm.reaction import Reaction, ReactionKind -from orm.shout import ShoutReactionsFollower -from orm.topic import TopicFollower +from orm.shout import ShoutReactionsFollower, Shout +from orm.topic import TopicFollower, Topic from orm.user import User 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() shout_dict = storage["shouts"]["by_oid"][shout_oid] if shout_dict: - reaction_dict["shout"] = shout_dict["slug"] - reaction_dict["createdBy"] = author.slug if author else "discours" + shout = session.query( + 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 # creating reaction from old comment @@ -80,15 +85,20 @@ async def migrate(entry, storage): # creating shout's reactions following for reaction author following1 = session.query( ShoutReactionsFollower + ).join( + User + ).join( + Shout ).where( - ShoutReactionsFollower.follower == reaction_dict["createdBy"] + User.id == reaction_dict["createdBy"] ).filter( - ShoutReactionsFollower.shout == reaction.shout + ShoutReactionsFollower.shout_id == reaction.shout_id ).first() + if not following1: following1 = ShoutReactionsFollower.create( - follower=reaction_dict["createdBy"], - shout=reaction.shout, + follower_id=reaction_dict["createdBy"], + shout_id=reaction.shout_id, auto=True ) session.add(following1) @@ -97,15 +107,22 @@ async def migrate(entry, storage): for t in shout_dict["topics"]: tf = session.query( TopicFollower + ).join( + Topic ).where( - TopicFollower.follower == reaction_dict["createdBy"] + TopicFollower.follower_id == reaction_dict["createdBy"] ).filter( - TopicFollower.topic == t + Topic.slug == t ).first() + if not tf: + topic = session.query( + Topic + ).where(Topic.slug == t).one() + topic_following = TopicFollower.create( - follower=reaction_dict["createdBy"], - topic=t, + follower_id=reaction_dict["createdBy"], + topic_id=topic.id, auto=True ) session.add(topic_following) @@ -114,16 +131,16 @@ async def migrate(entry, storage): for comment_rating_old in entry.get("ratings", []): rater = ( session.query(User) - .filter(User.oid == comment_rating_old["createdBy"]) - .first() + .filter(User.oid == comment_rating_old["createdBy"]) + .first() ) re_reaction_dict = { - "shout": reaction_dict["shout"], + "shout_id": reaction_dict["shout_id"], "replyTo": reaction.id, "kind": ReactionKind.LIKE if comment_rating_old["value"] > 0 else ReactionKind.DISLIKE, - "createdBy": rater.slug if rater else "discours", + "createdBy": rater.id if rater else 1, } cts = comment_rating_old.get("createdAt") if cts: @@ -134,14 +151,14 @@ async def migrate(entry, storage): following2 = session.query( ShoutReactionsFollower ).where( - ShoutReactionsFollower.follower == re_reaction_dict['createdBy'] + ShoutReactionsFollower.follower_id == re_reaction_dict['createdBy'] ).filter( - ShoutReactionsFollower.shout == rr.shout + ShoutReactionsFollower.shout_id == rr.shout_id ).first() if not following2: following2 = ShoutReactionsFollower.create( - follower=re_reaction_dict['createdBy'], - shout=rr.shout, + follower_id=re_reaction_dict['createdBy'], + shout_id=rr.shout_id, auto=True ) session.add(following2) @@ -163,22 +180,26 @@ def migrate_2stage(rr, old_new_id): reply_oid = rr.get("replyTo") if not reply_oid: return + new_id = old_new_id.get(rr.get("oid")) if not new_id: return + with local_session() as session: comment = session.query(Reaction).filter(Reaction.id == new_id).first() comment.replyTo = old_new_id.get(reply_oid) session.add(comment) srf = session.query(ShoutReactionsFollower).where( - ShoutReactionsFollower.shout == comment.shout + ShoutReactionsFollower.shout_id == comment.shout_id ).filter( - ShoutReactionsFollower.follower == comment.createdBy + ShoutReactionsFollower.follower_id == comment.createdBy ).first() + 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.commit() if not rr["body"]: raise Exception(rr) diff --git a/migration/tables/content_items.py b/migration/tables/content_items.py index 6d2eadbc..1de70f12 100644 --- a/migration/tables/content_items.py +++ b/migration/tables/content_items.py @@ -8,7 +8,7 @@ from migration.extract import extract_html, extract_media from orm.reaction import Reaction, ReactionKind from orm.shout import Shout, ShoutTopic, ShoutReactionsFollower from orm.user import User -from orm.topic import TopicFollower +from orm.topic import TopicFollower, Topic from services.stat.viewed import ViewedStorage OLD_DATE = "2016-03-05 22:22:00.350000" @@ -41,10 +41,10 @@ def create_author_from_app(app): name = app.get('name') slug = ( translit(name, "ru", reversed=True) - .replace(" ", "-") - .replace("'", "") - .replace(".", "-") - .lower() + .replace(" ", "-") + .replace("'", "") + .replace(".", "-") + .lower() ) # check if nameslug is used 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): s = Shout.create(**shout_dict) with local_session() as session: - srf = session.query(ShoutReactionsFollower).where( - ShoutReactionsFollower.shout == s.slug + follower = session.query(User).where(User.slug == userslug).one() + + srf = session.query( + ShoutReactionsFollower + ).join( + User + ).where( + ShoutReactionsFollower.shout_id == s.id ).filter( - ShoutReactionsFollower.follower == userslug + User.slug == userslug ).first() 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.commit() @@ -214,17 +220,21 @@ async def add_topics_follower(entry, storage, userslug): with local_session() as session: for tpc in topics: try: + topic = session.query(Topic).where(Topic.slug == tpc).one() + follower = session.query(User).where(User.slug == userslug).one() + tf = session.query( TopicFollower ).where( - TopicFollower.follower == userslug + TopicFollower.follower_id == follower.id ).filter( - TopicFollower.topic == tpc + TopicFollower.topic_id == topic.id ).first() + if not tf: tf = TopicFollower.create( - topic=tpc, - follower=userslug, + topic_id=topic.id, + follower_id=follower.id, auto=True ) session.add(tf) @@ -300,27 +310,35 @@ async def topics_aftermath(entry, storage): for tpc in filter(lambda x: bool(x), entry["topics"]): oldslug = tpc newslug = storage["replacements"].get(oldslug, oldslug) + if newslug: 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 = ( session.query(ShoutTopic) - .filter(ShoutTopic.shout == entry["slug"]) - .filter(ShoutTopic.topic == oldslug) - .first() + .join(Shout) + .join(Topic) + .filter(Shout.slug == entry["slug"]) + .filter(Topic.slug == oldslug) + .first() ) if shout_topic_old: - shout_topic_old.update({"slug": newslug}) + shout_topic_old.update({"topic_id": new_topic.id}) else: shout_topic_new = ( session.query(ShoutTopic) - .filter(ShoutTopic.shout == entry["slug"]) - .filter(ShoutTopic.topic == newslug) - .first() + .join(Shout) + .join(Topic) + .filter(Shout.slug == entry["slug"]) + .filter(Topic.slug == newslug) + .first() ) if not shout_topic_new: try: ShoutTopic.create( - **{"shout": entry["slug"], "topic": newslug} + **{"shout_id": shout.id, "topic_id": new_topic.id} ) except Exception: 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", []): rater = ( session.query(User) - .filter(User.oid == content_rating["createdBy"]) - .first() + .filter(User.oid == content_rating["createdBy"]) + .first() ) reactedBy = ( rater if rater - else session.query(User).filter(User.slug == "noname").first() + else session.query(User).filter(User.slug == "anonymous").first() ) if rater: + shout = session.query(Shout).where(Shout.slug == slug).one() + reaction_dict = { "kind": ReactionKind.LIKE if content_rating["value"] > 0 else ReactionKind.DISLIKE, - "createdBy": reactedBy.slug, - "shout": slug, + "createdBy": reactedBy.id, + "shout_id": shout.id, } cts = content_rating.get("createdAt") if cts: reaction_dict["createdAt"] = date_parse(cts) reaction = ( - session.query(Reaction) - .filter(Reaction.shout == reaction_dict["shout"]) - .filter(Reaction.createdBy == reaction_dict["createdBy"]) - .filter(Reaction.kind == reaction_dict["kind"]) - .first() + session.query(Reaction).filter( + Reaction.shout_id == reaction_dict["shout_id"] + ).filter( + Reaction.createdBy == reaction_dict["createdBy"] + ).filter( + Reaction.kind == reaction_dict["kind"] + ).first() ) if reaction: k = ReactionKind.AGREE if content_rating["value"] > 0 else ReactionKind.DISAGREE diff --git a/migration/tables/users.py b/migration/tables/users.py index bca86a76..759a1c3d 100644 --- a/migration/tables/users.py +++ b/migration/tables/users.py @@ -115,18 +115,23 @@ def migrate_2stage(entry, id_map): continue oid = entry["_id"] author_slug = id_map.get(oid) - user_rating_dict = { - "value": rating_entry["value"], - "rater": rater_slug, - "user": author_slug, - } + with local_session() as session: 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) if user_rating_dict['value'] > 0: af = AuthorFollower.create( - author=user_rating_dict['user'], - follower=user_rating_dict['rater'], + author_id=user.id, + follower_id=rater.id, auto=True ) session.add(af) diff --git a/orm/collab.py b/orm/collab.py index 3e3a5839..8cd8affe 100644 --- a/orm/collab.py +++ b/orm/collab.py @@ -11,14 +11,13 @@ class CollabAuthor(Base): id = None # type: ignore 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) class Collab(Base): __tablename__ = "collab" - authors = Column() title = Column(String, nullable=True, comment="Title") body = Column(String, nullable=True, comment="Body") pic = Column(String, nullable=True, comment="Picture") diff --git a/orm/collection.py b/orm/collection.py index 8d8b74eb..7651f2bf 100644 --- a/orm/collection.py +++ b/orm/collection.py @@ -9,8 +9,8 @@ class ShoutCollection(Base): __tablename__ = "shout_collection" id = None # type: ignore - shout = Column(ForeignKey("shout.slug"), primary_key=True) - collection = Column(ForeignKey("collection.slug"), primary_key=True) + shout_id = Column(ForeignKey("shout.id"), primary_key=True) + collection_id = Column(ForeignKey("collection.id"), primary_key=True) class Collection(Base): diff --git a/orm/community.py b/orm/community.py index 8c339714..251ac4a4 100644 --- a/orm/community.py +++ b/orm/community.py @@ -8,8 +8,8 @@ class CommunityFollower(Base): __tablename__ = "community_followers" id = None # type: ignore - follower = Column(ForeignKey("user.slug"), primary_key=True) - community = Column(ForeignKey("community.slug"), primary_key=True) + follower_id = Column(ForeignKey("user.id"), primary_key=True) + community_id = Column(ForeignKey("community.id"), primary_key=True) joinedAt = Column( DateTime, nullable=False, default=datetime.now, comment="Created at" ) diff --git a/orm/reaction.py b/orm/reaction.py index 123cfb22..c3a87385 100644 --- a/orm/reaction.py +++ b/orm/reaction.py @@ -28,12 +28,12 @@ class Reaction(Base): createdAt = Column( 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") - 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") - deletedBy = Column(ForeignKey("user.slug"), nullable=True, comment="Deleted by") - shout = Column(ForeignKey("shout.slug"), nullable=False) + deletedBy = Column(ForeignKey("user.id"), nullable=True, index=True, comment="Deleted by") + shout_id = Column(ForeignKey("shout.id"), nullable=False, index=True) replyTo = Column( ForeignKey("reaction.id"), nullable=True, comment="Reply to reaction ID" ) diff --git a/orm/shout.py b/orm/shout.py index f65b1268..452d292f 100644 --- a/orm/shout.py +++ b/orm/shout.py @@ -13,16 +13,16 @@ class ShoutTopic(Base): __tablename__ = "shout_topic" id = None # type: ignore - shout = Column(ForeignKey("shout.slug"), primary_key=True) - topic = Column(ForeignKey("topic.slug"), primary_key=True) + shout_id = Column(ForeignKey("shout.id"), primary_key=True, index=True) + topic_id = Column(ForeignKey("topic.id"), primary_key=True, index=True) class ShoutReactionsFollower(Base): __tablename__ = "shout_reactions_followers" id = None # type: ignore - follower = Column(ForeignKey("user.slug"), primary_key=True) - shout = Column(ForeignKey("shout.slug"), primary_key=True) + follower_id = Column(ForeignKey("user.id"), primary_key=True, index=True) + shout_id = Column(ForeignKey("shout.id"), primary_key=True, index=True) auto = Column(Boolean, nullable=False, default=False) createdAt = Column( DateTime, nullable=False, default=datetime.now, comment="Created at" @@ -34,8 +34,8 @@ class ShoutAuthor(Base): __tablename__ = "shout_author" id = None # type: ignore - shout = Column(ForeignKey("shout.slug"), primary_key=True) - user = Column(ForeignKey("user.slug"), primary_key=True) + shout_id = Column(ForeignKey("shout.id"), primary_key=True, index=True) + user_id = Column(ForeignKey("user.id"), primary_key=True, index=True) caption = Column(String, nullable=True, default="") @@ -55,7 +55,7 @@ class Shout(Base): topics = relationship(lambda: Topic, secondary=ShoutTopic.__tablename__) reactions = relationship(lambda: Reaction) 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) media = Column(JSON, nullable=True) diff --git a/orm/topic.py b/orm/topic.py index f4d094b7..3f99cb03 100644 --- a/orm/topic.py +++ b/orm/topic.py @@ -9,8 +9,8 @@ class TopicFollower(Base): __tablename__ = "topic_followers" id = None # type: ignore - follower = Column(ForeignKey("user.slug"), primary_key=True) - topic = Column(ForeignKey("topic.slug"), primary_key=True) + follower_id = Column(ForeignKey("user.id"), primary_key=True, index=True) + topic_id = Column(ForeignKey("topic.id"), primary_key=True, index=True) createdAt = Column( DateTime, nullable=False, default=datetime.now, comment="Created at" ) diff --git a/orm/user.py b/orm/user.py index 11055d29..1f283a2b 100644 --- a/orm/user.py +++ b/orm/user.py @@ -6,7 +6,6 @@ from sqlalchemy.orm import relationship from base.orm import Base, local_session from orm.rbac import Role -from services.auth.roles import RoleStorage class UserNotifications(Base): @@ -21,8 +20,8 @@ class UserRating(Base): __tablename__ = "user_rating" id = None # type: ignore - rater = Column(ForeignKey("user.slug"), primary_key=True) - user = Column(ForeignKey("user.slug"), primary_key=True) + rater_id = Column(ForeignKey("user.id"), primary_key=True, index=True) + user_id = Column(ForeignKey("user.id"), primary_key=True, index=True) value = Column(Integer) @staticmethod @@ -34,16 +33,16 @@ class UserRole(Base): __tablename__ = "user_role" id = None # type: ignore - user_id = Column(ForeignKey("user.id"), primary_key=True) - role_id = Column(ForeignKey("role.id"), primary_key=True) + user_id = Column(ForeignKey("user.id"), primary_key=True, index=True) + role_id = Column(ForeignKey("role.id"), primary_key=True, index=True) class AuthorFollower(Base): __tablename__ = "author_follower" id = None # type: ignore - follower = Column(ForeignKey("user.slug"), primary_key=True) - author = Column(ForeignKey("user.slug"), primary_key=True) + follower_id = Column(ForeignKey("user.id"), primary_key=True, index=True) + author_id = Column(ForeignKey("user.id"), primary_key=True, index=True) createdAt = Column( DateTime, nullable=False, default=datetime.now, comment="Created at" ) @@ -73,7 +72,7 @@ class User(Base): links = Column(JSONType, nullable=True, comment="Links") oauth = Column(String, nullable=True) 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__) oid = Column(String, nullable=True) diff --git a/orm/viewed.py b/orm/viewed.py index 6d089234..b269921a 100644 --- a/orm/viewed.py +++ b/orm/viewed.py @@ -6,8 +6,8 @@ from base.orm import Base, local_session class ViewedEntry(Base): __tablename__ = "viewed" - viewer = Column(ForeignKey("user.slug"), default='anonymous') - shout = Column(ForeignKey("shout.slug"), default="genesis-block") + viewer_id = Column(ForeignKey("user.id"), index=True, default=1) + shout_id = Column(ForeignKey("shout.id"), index=True, default=1) amount = Column(Integer, default=1) createdAt = Column( DateTime, nullable=False, default=datetime.now, comment="Created at" diff --git a/resolvers/create/editor.py b/resolvers/create/editor.py index 220ff229..499ffb8c 100644 --- a/resolvers/create/editor.py +++ b/resolvers/create/editor.py @@ -1,11 +1,13 @@ from datetime import datetime, timezone +from sqlalchemy import and_ + from auth.authenticate import login_required from base.orm import local_session from base.resolvers import mutation from orm.rbac import Resource from orm.shout import Shout, ShoutAuthor, ShoutTopic -from orm.topic import TopicFollower +from orm.topic import TopicFollower, Topic from orm.user import User from resolvers.zine.reactions import reactions_follow, reactions_unfollow from services.zine.gittask import GitTask @@ -24,7 +26,7 @@ async def create_shout(_, info, inp): new_shout = Shout.create(**inp) # 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) reactions_follow(user, new_shout.slug, True) @@ -33,11 +35,16 @@ async def create_shout(_, info, inp): topic_slugs.append(inp["mainTopic"]) 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) - 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: - 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) new_shout.topic_slugs = topic_slugs @@ -45,7 +52,7 @@ async def create_shout(_, info, inp): 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} @@ -75,7 +82,7 @@ async def update_shout(_, info, inp): session.add(shout) if inp.get("topics"): # 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: session.delete(topiclink) # add new topic links diff --git a/resolvers/inbox/search.py b/resolvers/inbox/search.py index 914af4eb..e7132506 100644 --- a/resolvers/inbox/search.py +++ b/resolvers/inbox/search.py @@ -4,7 +4,7 @@ from auth.authenticate import login_required from base.redis import redis from base.resolvers import query from base.orm import local_session -from orm.user import AuthorFollower +from orm.user import AuthorFollower, User @query.field("searchRecipients") @@ -30,13 +30,19 @@ async def search_recipients(_, info, query: str, limit: int = 50, offset: int = with local_session() as session: # followings - result += session.query(AuthorFollower.author).where(AuthorFollower.follower.startswith(query))\ - .offset(offset + len(result)).limit(more_amount) + result += session.query(AuthorFollower.author).join( + User, User.id == AuthorFollower.follower_id + ).where( + User.slug.startswith(query) + ).offset(offset + len(result)).limit(more_amount) more_amount = limit # followers - result += session.query(AuthorFollower.follower).where(AuthorFollower.author.startswith(query))\ - .offset(offset + len(result)).limit(offset + len(result) + limit) + result += session.query(AuthorFollower.follower).join( + User, User.id == AuthorFollower.author_id + ).where( + User.slug.startswith(query) + ).offset(offset + len(result)).limit(offset + len(result) + limit) return { "members": list(result), "error": None diff --git a/resolvers/zine/load.py b/resolvers/zine/load.py index bc76db02..0e0c1ade 100644 --- a/resolvers/zine/load.py +++ b/resolvers/zine/load.py @@ -1,11 +1,11 @@ from datetime import datetime, timedelta, timezone 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.resolvers import query from orm import ViewedEntry 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 @@ -18,7 +18,7 @@ def add_stat_columns(q): def apply_filters(q, filters, user=None): if filters.get("reacted") and user: - q.join(Reaction, Reaction.createdBy == user.slug) + q.join(Reaction, Reaction.createdBy == user.id) v = filters.get("visibility") if v == "public": @@ -66,9 +66,9 @@ async def load_shout(_, info, slug): "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: - if author.slug == author_caption.user: + if author.id == author_caption.user_id: author.caption = author_caption.caption return shout diff --git a/resolvers/zine/profile.py b/resolvers/zine/profile.py index be38eeff..86368ac5 100644 --- a/resolvers/zine/profile.py +++ b/resolvers/zine/profile.py @@ -19,17 +19,19 @@ from resolvers.zine.topics import followed_by_user def add_author_stat_columns(q): author_followers = aliased(AuthorFollower) author_following = aliased(AuthorFollower) + shout_author_aliased = aliased(ShoutAuthor) + user_rating_aliased = aliased(UserRating) - q = q.outerjoin(ShoutAuthor).add_columns( - func.count(distinct(ShoutAuthor.shout)).label('shouts_stat') - ).outerjoin(author_followers, author_followers.author == User.slug).add_columns( - func.count(distinct(author_followers.follower)).label('followers_stat') - ).outerjoin(author_following, author_following.follower == User.slug).add_columns( - func.count(distinct(author_following.author)).label('followings_stat') - ).outerjoin(UserRating).add_columns( + q = q.outerjoin(shout_author_aliased).add_columns( + func.count(distinct(shout_author_aliased.shout_id)).label('shouts_stat') + ).outerjoin(author_followers, author_followers.author_id == User.id).add_columns( + func.count(distinct(author_followers.follower_id)).label('followers_stat') + ).outerjoin(author_following, author_following.follower_id == User.id).add_columns( + func.count(distinct(author_following.author_id)).label('followings_stat') + ).outerjoin(user_rating_aliased, user_rating_aliased.user_id == User.id).add_columns( # TODO: check - func.sum(UserRating.value).label('rating_stat') - ).outerjoin(Reaction, and_(Reaction.createdBy == User.slug, Reaction.body.is_not(None))).add_columns( + func.sum(user_rating_aliased.value).label('rating_stat') + ).outerjoin(Reaction, and_(Reaction.createdBy == User.id, Reaction.body.is_not(None))).add_columns( func.count(distinct(Reaction.id)).label('commented_stat') ) @@ -83,7 +85,7 @@ async def followed_reactions(slug): return session.query( Reaction.shout ).where( - Reaction.createdBy == slug + Reaction.createdBy == user.id ).filter( Reaction.createdAt > user.lastSeen ).all() @@ -107,7 +109,7 @@ async def get_followed_authors(_, _info, slug) -> List[User]: async def followed_authors(slug) -> List[User]: q = select(User) 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) @@ -116,7 +118,13 @@ async def followed_authors(slug) -> List[User]: async def user_followers(_, _info, slug) -> List[User]: q = select(User) 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) @@ -173,7 +181,8 @@ async def rate_user(_, info, rated_userslug, value): # for mutation.field("follow") def author_follow(user, slug): 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.commit() @@ -182,13 +191,13 @@ def author_follow(user, slug): def author_unfollow(user, slug): with local_session() as session: flw = ( - session.query(AuthorFollower) - .filter( + session.query( + AuthorFollower + ).join(User, User.id == AuthorFollower.author_id).filter( and_( - AuthorFollower.follower == user.slug, AuthorFollower.author == slug + AuthorFollower.follower_id == user.id, User.slug == slug ) - ) - .first() + ).first() ) if not flw: 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"): q = q.filter(User.name.ilike(f"%{by['name']}%")) 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 days_before = datetime.now(tz=timezone.utc) - timedelta(days=by["lastSeen"]) q = q.filter(User.lastSeen > days_before) diff --git a/resolvers/zine/reactions.py b/resolvers/zine/reactions.py index 876e4791..de1ed409 100644 --- a/resolvers/zine/reactions.py +++ b/resolvers/zine/reactions.py @@ -16,16 +16,19 @@ def add_reaction_stat_columns(q): def reactions_follow(user: User, slug: str, auto=False): with local_session() as session: + shout = session.query(Shout).where(Shout.slug == slug).one() + following = ( session.query(ShoutReactionsFollower).where(and_( - ShoutReactionsFollower.follower == user.slug, - ShoutReactionsFollower.shout == slug + ShoutReactionsFollower.follower_id == user.id, + ShoutReactionsFollower.shout_id == shout.id, )).first() ) + if not following: following = ShoutReactionsFollower.create( - follower=user.slug, - shout=slug, + follower_id=user.id, + shout_id=shout.id, auto=auto ) session.add(following) @@ -34,12 +37,15 @@ def reactions_follow(user: User, slug: str, auto=False): def reactions_unfollow(user, slug): with local_session() as session: + shout = session.query(Shout).where(Shout.slug == slug).one() + following = ( session.query(ShoutReactionsFollower).where(and_( - ShoutReactionsFollower.follower == user.slug, - ShoutReactionsFollower.shout == slug + ShoutReactionsFollower.follower_id == user.id, + ShoutReactionsFollower.shout_id == shout.id )).first() ) + if following: session.delete(following) session.commit() @@ -68,7 +74,7 @@ def check_to_publish(session, user, reaction): ]: if is_published_author(user): # 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, ] for ar in approvers_reactions: a = ar.createdBy @@ -87,7 +93,7 @@ def check_to_hide(session, user, reaction): ReactionKind.UNPROOF ]: # 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 for r in approvers_reactions: if r.kind in [ @@ -224,22 +230,26 @@ async def load_reactions_by(_, _info, by, limit=50, offset=0): q = select( Reaction, CreatedByUser, ReactedShout ).join( - CreatedByUser, Reaction.createdBy == CreatedByUser.slug + CreatedByUser, Reaction.createdBy == CreatedByUser.id ).join( - ReactedShout, Reaction.shout == ReactedShout.slug + ReactedShout, Reaction.shout_id == ReactedShout.id ) 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"): - 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"): - 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"): + # TODO: check q = q.filter(Shout.topics.contains(by["topic"])) if by.get("comment"): 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"]}%')) if by.get("days"): after = datetime.now(tz=timezone.utc) - timedelta(days=int(by["days"]) or 30) diff --git a/resolvers/zine/topics.py b/resolvers/zine/topics.py index 459d95c8..610dc720 100644 --- a/resolvers/zine/topics.py +++ b/resolvers/zine/topics.py @@ -4,20 +4,20 @@ from base.orm import local_session from base.resolvers import mutation, query from orm.shout import ShoutTopic, ShoutAuthor from orm.topic import Topic, TopicFollower -from orm import Shout +from orm import Shout, User def add_topic_stat_columns(q): - q = q.outerjoin(ShoutTopic, Topic.slug == ShoutTopic.topic).add_columns( - func.count(distinct(ShoutTopic.shout)).label('shouts_stat') - ).outerjoin(ShoutAuthor, ShoutTopic.shout == ShoutAuthor.shout).add_columns( - func.count(distinct(ShoutAuthor.user)).label('authors_stat') + q = q.outerjoin(ShoutTopic, Topic.id == ShoutTopic.topic_id).add_columns( + func.count(distinct(ShoutTopic.shout_id)).label('shouts_stat') + ).outerjoin(ShoutAuthor, ShoutTopic.shout_id == ShoutAuthor.shout_id).add_columns( + func.count(distinct(ShoutAuthor.user_id)).label('authors_stat') ).outerjoin(TopicFollower, and_( - TopicFollower.topic == Topic.slug, - TopicFollower.follower == ShoutAuthor.user + TopicFollower.topic_id == Topic.id, + TopicFollower.follower_id == ShoutAuthor.id )).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) @@ -49,7 +49,7 @@ def get_topics_from_query(q): def followed_by_user(user_slug): q = select(Topic) 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) @@ -74,7 +74,7 @@ async def topics_by_community(_, info, community): async def topics_by_author(_, _info, author): q = select(Topic) 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) @@ -117,7 +117,9 @@ async def update_topic(_, _info, inp): async def topic_follow(user, slug): 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.commit() @@ -125,10 +127,10 @@ async def topic_follow(user, slug): async def topic_unfollow(user, slug): with local_session() as session: sub = ( - session.query(TopicFollower).filter( + session.query(TopicFollower).join(Topic).filter( and_( - TopicFollower.follower == user.slug, - TopicFollower.topic == slug + TopicFollower.follower_id == user.id, + Topic.slug == slug ) ).first() ) @@ -143,7 +145,7 @@ async def topic_unfollow(user, slug): async def topics_random(_, info, amount=12): q = select(Topic) 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) return get_topics_from_query(q) diff --git a/services/stat/viewed.py b/services/stat/viewed.py index 77ebe979..32445153 100644 --- a/services/stat/viewed.py +++ b/services/stat/viewed.py @@ -5,7 +5,9 @@ from gql import Client, gql from gql.transport.aiohttp import AIOHTTPTransport from base.orm import local_session 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 ssl import create_default_context from os import environ, path @@ -113,6 +115,7 @@ class ViewedStorage: async with self.lock: return self.client.execute_async(load_facts) + # unused yet @staticmethod async def get_shout(shout_slug): """ getting shout views metric by slug """ @@ -123,8 +126,9 @@ class ViewedStorage: shout_views = 0 with local_session() as session: try: + shout = session.query(Shout).where(Shout.slug == shout_slug).one() shout_views = session.query(func.sum(ViewedEntry.amount)).where( - ViewedEntry.shout == shout_slug + ViewedEntry.shout_id == shout.id ).all()[0][0] self.by_shouts[shout_slug] = shout_views self.update_topics(session, shout_slug) @@ -147,11 +151,12 @@ class ViewedStorage: def update_topics(session, shout_slug): """ updates topics counters by shout slug """ self = ViewedStorage - for t in session.query(ShoutTopic).where(ShoutTopic.shout == shout_slug).all(): - tpc = t.topic - if not self.by_topics.get(tpc): - self.by_topics[tpc] = {} - self.by_topics[tpc][shout_slug] = self.by_shouts[shout_slug] + for [shout_topic, topic] in session.query(ShoutTopic, Topic).join(Topic).join(Shout).where( + Shout.slug == shout_slug + ).all(): + if not self.by_topics.get(topic.slug): + self.by_topics[topic.slug] = {} + self.by_topics[topic.slug][shout_slug] = self.by_shouts[shout_slug] @staticmethod async def increment(shout_slug, amount=1, viewer='anonymous'): @@ -159,9 +164,12 @@ class ViewedStorage: self = ViewedStorage async with self.lock: 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(**{ - "viewer": viewer, - "shout": shout_slug, + "viewer_id": viewer.id, + "shout_id": shout.id, "amount": amount }) session.add(viewed)