poestmerge
All checks were successful
Deploy on push / deploy (push) Successful in 27s

This commit is contained in:
2024-05-30 12:49:46 +03:00
17 changed files with 168 additions and 431 deletions

View File

@@ -1,6 +1,6 @@
import time
from sqlalchemy import desc, or_, select, text
from sqlalchemy import desc, select, text
from orm.author import Author
from orm.shout import ShoutAuthor, ShoutTopic
@@ -54,34 +54,18 @@ def get_authors_all(_, _info):
@query.field("get_author")
async def get_author(_, _info, slug="", author_id=0):
author_query = ""
author = None
author_dict = None
try:
# lookup for cached author
author_query = select(Author)
if slug:
author_query = author_query.filter(Author.slug == slug)
elif author_id:
author_query = author_query.filter(Author.id == author_id)
else:
raise ValueError("Author not found")
lookup_result = local_session().execute(author_query).first()
if lookup_result:
[found_author] = lookup_result
# logger.debug(found_author)
if found_author:
logger.debug(f"found author id: {found_author.id}")
author_id = found_author.id if found_author.id else author_id
author_dict = await get_cached_author(int(author_id), get_with_stat)
author_id = get_author_id_from(slug=slug, user="", author_id=author_id)
if not author_id:
raise ValueError("cant find")
author_dict = await get_cached_author(int(author_id), get_with_stat)
# update stat from db
if not author_dict or not author_dict.get("stat"):
result = get_with_stat(author_query)
if not result:
raise ValueError("Author not found")
[author] = result
# use found author
# update stat from db
author_query = select(Author).filter(Author.id == author_id)
[author] = get_with_stat(author_query)
if isinstance(author, Author):
logger.debug(f"update @{author.slug} with id {author.id}")
author_dict = author.dict()
@@ -131,10 +115,7 @@ async def load_authors_by(_, _info, by, limit, offset):
authors_query = authors_query.filter(Author.name.ilike(f"%{by['name']}%"))
elif by.get("topic"):
authors_query = (
authors_query.join(ShoutAuthor)
.join(ShoutTopic)
.join(Topic)
.where(Topic.slug == str(by["topic"]))
authors_query.join(ShoutAuthor).join(ShoutTopic).join(Topic).where(Topic.slug == str(by["topic"]))
)
if by.get("last_seen"): # in unix time
@@ -143,16 +124,19 @@ async def load_authors_by(_, _info, by, limit, offset):
elif by.get("created_at"): # in unix time
before = int(time.time()) - by["created_at"]
authors_query = authors_query.filter(Author.created_at > before)
authors_query = authors_query.limit(limit).offset(offset)
authors_nostat = local_session().execute(authors_query)
authors = []
if authors_nostat:
for [a] in authors_nostat:
author_dict = None
if isinstance(a, Author):
author_dict = await get_cached_author(a.id, get_with_stat)
if not author_dict or not isinstance(author_dict.get("shouts"), int):
break
authors_query = authors_query.limit(limit).offset(offset)
with local_session() as session:
authors_nostat = session.execute(authors_query)
if authors_nostat:
for [a] in authors_nostat:
author_dict = None
if isinstance(a, Author):
author_dict = await get_cached_author(a.id, get_with_stat)
if not author_dict or not isinstance(author_dict.get("shouts"), int):
break
# order
order = by.get("order")
@@ -163,74 +147,70 @@ async def load_authors_by(_, _info, by, limit, offset):
return authors or []
def get_author_id_from(slug="", user=None, author_id=None):
if not slug and not user and not author_id:
raise ValueError("One of slug, user, or author_id must be provided")
author_query = select(Author.id)
if user:
author_query = author_query.filter(Author.user == user)
elif slug:
author_query = author_query.filter(Author.slug == slug)
elif author_id:
author_query = author_query.filter(Author.id == author_id)
with local_session() as session:
author_id_result = session.execute(author_query).first()
author_id = author_id_result[0] if author_id_result else None
if not author_id:
raise ValueError("Author not found")
return author_id
@query.field("get_author_follows")
async def get_author_follows(_, _info, slug="", user=None, author_id=0):
try:
author_query = select(Author)
if user:
author_query = author_query.filter(Author.user == user)
elif slug:
author_query = author_query.filter(Author.slug == slug)
elif author_id:
author_query = author_query.filter(Author.id == author_id)
else:
return {"error": "One of slug, user, or author_id must be provided"}
result = local_session().execute(author_query)
if result:
# logger.debug(result)
[author] = result
# logger.debug(author)
if author and isinstance(author, Author):
# logger.debug(author.dict())
author_id = author.id if not author_id else author_id
topics = []
authors = []
if bool(author_id):
logger.debug(f"getting {author_id} follows authors")
authors = await get_cached_author_follows_authors(author_id)
topics = await get_cached_author_follows_topics(author_id)
return {
"topics": topics,
"authors": authors,
"communities": [
{"id": 1, "name": "Дискурс", "slug": "discours", "pic": ""}
],
}
author_id = get_author_id_from(slug, user, author_id)
if bool(author_id):
logger.debug(f"getting {author_id} follows authors")
authors = await get_cached_author_follows_authors(author_id)
topics = await get_cached_author_follows_topics(author_id)
return {
"topics": topics,
"authors": authors,
"communities": [{"id": 1, "name": "Дискурс", "slug": "discours", "pic": ""}],
}
except Exception:
import traceback
traceback.print_exc()
return {"error": "Author not found"}
return {"error": "Author not found"}
@query.field("get_author_follows_topics")
async def get_author_follows_topics(_, _info, slug="", user=None, author_id=None):
with local_session() as session:
if user or slug:
author_id_result = (
session.query(Author.id)
.filter(or_(Author.user == user, Author.slug == slug))
.first()
)
author_id = author_id_result[0] if author_id_result else None
if not author_id:
raise ValueError("Author not found")
return get_cached_author_follows_topics(author_id)
try:
author_id = get_author_id_from(slug, user, author_id)
topics = await get_cached_author_follows_topics(author_id)
return topics
except Exception:
import traceback
traceback.print_exc()
@query.field("get_author_follows_authors")
async def get_author_follows_authors(_, _info, slug="", user=None, author_id=None):
with local_session() as session:
if user or slug:
author_id_result = (
session.query(Author.id)
.filter(or_(Author.user == user, Author.slug == slug))
.first()
)
author_id = author_id_result[0] if author_id_result else None
if not author_id:
raise ValueError("Author not found")
try:
author_id = get_author_id_from(slug, user, author_id)
return await get_cached_author_follows_authors(author_id)
except Exception:
import traceback
traceback.print_exc()
def create_author(user_id: str, slug: str, name: str = ""):
@@ -253,8 +233,7 @@ def create_author(user_id: str, slug: str, name: str = ""):
@query.field("get_author_followers")
async def get_author_followers(_, _info, slug: str):
logger.debug(f"getting followers for @{slug}")
author_query = select(Author.id).filter(Author.slug == slug)
author_id = local_session().execute(author_query).scalar()
author_id = get_author_id_from(slug=slug, user="", author_id=0)
followers = []
if author_id:
followers = await get_cached_author_followers(author_id)

View File

@@ -18,18 +18,12 @@ async def accept_invite(_, info, invite_id: int):
with local_session() as session:
# Check if the invite exists
invite = session.query(Invite).filter(Invite.id == invite_id).first()
if (
invite
and invite.author_id is author_id
and invite.status is InviteStatus.PENDING.value
):
if invite and invite.author_id is author_id and invite.status is InviteStatus.PENDING.value:
# Add the user to the shout authors
shout = session.query(Shout).filter(Shout.id == invite.shout_id).first()
if shout:
if author_id not in shout.authors:
author = (
session.query(Author).filter(Author.id == author_id).first()
)
author = session.query(Author).filter(Author.id == author_id).first()
if author:
shout.authors.append(author)
session.add(shout)
@@ -57,11 +51,7 @@ async def reject_invite(_, info, invite_id: int):
author_id = int(author_id)
# Check if the invite exists
invite = session.query(Invite).filter(Invite.id == invite_id).first()
if (
invite
and invite.author_id is author_id
and invite.status is InviteStatus.PENDING.value
):
if invite and invite.author_id is author_id and invite.status is InviteStatus.PENDING.value:
# Delete the invite
session.delete(invite)
session.commit()
@@ -124,9 +114,7 @@ async def remove_author(_, info, slug: str = "", author_id: int = 0):
shout = session.query(Shout).filter(Shout.slug == slug).first()
# NOTE: owner should be first in a list
if shout and author.id is shout.created_by:
shout.authors = [
author for author in shout.authors if author.id != author_id
]
shout.authors = [author for author in shout.authors if author.id != author_id]
session.commit()
return {}
return {"error": "Access denied"}

View File

@@ -14,9 +14,7 @@ def get_communities_from_query(q):
for [c, shouts_stat, followers_stat] in session.execute(q):
c.stat = {
"shouts": session.execute(
select(func.count(distinct(ShoutCommunity.shout))).filter(
ShoutCommunity.community == c.id
)
select(func.count(distinct(ShoutCommunity.shout))).filter(ShoutCommunity.community == c.id)
),
# "authors": session.execute(select(func.count(distinct(ShoutCommunity.shout))).filter(ShoutCommunity.community == c.id)),
# "followers": session.execute(select(func.count(distinct(ShoutCommunity.shout))).filter(ShoutCommunity.community == c.id)),

View File

@@ -57,16 +57,12 @@ async def get_my_shout(_, info, shout_id: int):
if not shout:
return {"error": "no shout found", "shout": None}
logger.debug(
f"got shout authors: {shout.authors} created by {shout.created_by}"
)
logger.debug(f"got shout authors: {shout.authors} created by {shout.created_by}")
is_editor = "editor" in roles
logger.debug(f'viewer is{'' if is_editor else ' not'} editor')
is_creator = author_id == shout.created_by
logger.debug(f'viewer is{'' if is_creator else ' not'} creator')
is_author = bool(
list(filter(lambda x: x.id == int(author_id), [x for x in shout.authors]))
)
is_author = bool(list(filter(lambda x: x.id == int(author_id), [x for x in shout.authors])))
logger.debug(f'viewer is{'' if is_creator else ' not'} author')
can_edit = is_editor or is_author or is_creator
@@ -91,9 +87,7 @@ async def get_shouts_drafts(_, info):
q = (
select(Shout)
.options(joinedload(Shout.authors), joinedload(Shout.topics))
.filter(
and_(Shout.deleted_at.is_(None), Shout.created_by == int(author_id))
)
.filter(and_(Shout.deleted_at.is_(None), Shout.created_by == int(author_id)))
.filter(Shout.published_at.is_(None))
.order_by(desc(coalesce(Shout.updated_at, Shout.created_at)))
.group_by(Shout.id)
@@ -129,18 +123,10 @@ async def create_shout(_, info, inp):
"published_at": None,
"created_at": current_time, # Set created_at as Unix timestamp
}
same_slug_shout = (
session.query(Shout)
.filter(Shout.slug == shout_dict.get("slug"))
.first()
)
same_slug_shout = session.query(Shout).filter(Shout.slug == shout_dict.get("slug")).first()
c = 1
while same_slug_shout is not None:
same_slug_shout = (
session.query(Shout)
.filter(Shout.slug == shout_dict.get("slug"))
.first()
)
same_slug_shout = session.query(Shout).filter(Shout.slug == shout_dict.get("slug")).first()
c += 1
shout_dict["slug"] += f"-{c}"
new_shout = Shout(**shout_dict)
@@ -153,11 +139,7 @@ async def create_shout(_, info, inp):
sa = ShoutAuthor(shout=shout.id, author=author_id)
session.add(sa)
topics = (
session.query(Topic)
.filter(Topic.slug.in_(inp.get("topics", [])))
.all()
)
topics = session.query(Topic).filter(Topic.slug.in_(inp.get("topics", []))).all()
for topic in topics:
t = ShoutTopic(topic=topic.id, shout=shout.id)
session.add(t)
@@ -176,18 +158,11 @@ async def create_shout(_, info, inp):
def patch_main_topic(session, main_topic, shout):
with session.begin():
shout = (
session.query(Shout)
.options(joinedload(Shout.topics))
.filter(Shout.id == shout.id)
.first()
)
shout = session.query(Shout).options(joinedload(Shout.topics)).filter(Shout.id == shout.id).first()
if not shout:
return
old_main_topic = (
session.query(ShoutTopic)
.filter(and_(ShoutTopic.shout == shout.id, ShoutTopic.main.is_(True)))
.first()
session.query(ShoutTopic).filter(and_(ShoutTopic.shout == shout.id, ShoutTopic.main.is_(True))).first()
)
main_topic = session.query(Topic).filter(Topic.slug == main_topic).first()
@@ -195,19 +170,11 @@ def patch_main_topic(session, main_topic, shout):
if main_topic:
new_main_topic = (
session.query(ShoutTopic)
.filter(
and_(
ShoutTopic.shout == shout.id, ShoutTopic.topic == main_topic.id
)
)
.filter(and_(ShoutTopic.shout == shout.id, ShoutTopic.topic == main_topic.id))
.first()
)
if (
old_main_topic
and new_main_topic
and old_main_topic is not new_main_topic
):
if old_main_topic and new_main_topic and old_main_topic is not new_main_topic:
ShoutTopic.update(old_main_topic, {"main": False})
session.add(old_main_topic)
@@ -216,9 +183,7 @@ def patch_main_topic(session, main_topic, shout):
def patch_topics(session, shout, topics_input):
new_topics_to_link = [
Topic(**new_topic) for new_topic in topics_input if new_topic["id"] < 0
]
new_topics_to_link = [Topic(**new_topic) for new_topic in topics_input if new_topic["id"] < 0]
if new_topics_to_link:
session.add_all(new_topics_to_link)
session.commit()
@@ -227,9 +192,7 @@ def patch_topics(session, shout, topics_input):
created_unlinked_topic = ShoutTopic(shout=shout.id, topic=new_topic_to_link.id)
session.add(created_unlinked_topic)
existing_topics_input = [
topic_input for topic_input in topics_input if topic_input.get("id", 0) > 0
]
existing_topics_input = [topic_input for topic_input in topics_input if topic_input.get("id", 0) > 0]
existing_topic_to_link_ids = [
existing_topic_input["id"]
for existing_topic_input in existing_topics_input
@@ -237,9 +200,7 @@ def patch_topics(session, shout, topics_input):
]
for existing_topic_to_link_id in existing_topic_to_link_ids:
created_unlinked_topic = ShoutTopic(
shout=shout.id, topic=existing_topic_to_link_id
)
created_unlinked_topic = ShoutTopic(shout=shout.id, topic=existing_topic_to_link_id)
session.add(created_unlinked_topic)
topic_to_unlink_ids = [
@@ -276,24 +237,15 @@ async def update_shout(_, info, shout_id: int, shout_input=None, publish=False):
if not shout_by_id:
return {"error": "shout not found"}
if slug != shout_by_id.slug:
same_slug_shout = (
session.query(Shout).filter(Shout.slug == slug).first()
)
same_slug_shout = session.query(Shout).filter(Shout.slug == slug).first()
c = 1
while same_slug_shout is not None:
c += 1
slug = f"{slug}-{c}"
same_slug_shout = (
session.query(Shout).filter(Shout.slug == slug).first()
)
same_slug_shout = session.query(Shout).filter(Shout.slug == slug).first()
shout_input["slug"] = slug
if (
filter(
lambda x: x.id == author_id, [x for x in shout_by_id.authors]
)
or "editor" in roles
):
if filter(lambda x: x.id == author_id, [x for x in shout_by_id.authors]) or "editor" in roles:
# topics patch
topics_input = shout_input.get("topics")
if topics_input:
@@ -376,17 +328,9 @@ async def delete_shout(_, info, shout_id: int):
def handle_proposing(session, r, shout):
if is_positive(r.kind):
replied_reaction = (
session.query(Reaction)
.filter(Reaction.id == r.reply_to, Reaction.shout == r.shout)
.first()
)
replied_reaction = session.query(Reaction).filter(Reaction.id == r.reply_to, Reaction.shout == r.shout).first()
if (
replied_reaction
and replied_reaction.kind is ReactionKind.PROPOSE.value
and replied_reaction.quote
):
if replied_reaction and replied_reaction.kind is ReactionKind.PROPOSE.value and replied_reaction.quote:
# patch all the proposals' quotes
proposals = (
session.query(Reaction)
@@ -403,9 +347,7 @@ def handle_proposing(session, r, shout):
if proposal.quote:
proposal_diff = get_diff(shout.body, proposal.quote)
proposal_dict = proposal.dict()
proposal_dict["quote"] = apply_diff(
replied_reaction.quote, proposal_diff
)
proposal_dict["quote"] = apply_diff(replied_reaction.quote, proposal_diff)
Reaction.update(proposal, proposal_dict)
session.add(proposal)

View File

@@ -56,9 +56,7 @@ async def follow(_, info, what, slug):
follower_id = int(follower_id)
error = author_follow(follower_id, slug)
if not error:
[author_id] = (
local_session().query(Author.id).filter(Author.slug == slug).first()
)
[author_id] = local_session().query(Author.id).filter(Author.slug == slug).first()
if author_id and author_id not in follows:
follows.append(author_id)
await cache_author(follower_dict)
@@ -104,9 +102,7 @@ async def unfollow(_, info, what, slug):
# NOTE: after triggers should update cached stats
if not error:
logger.info(f"@{follower_dict.get('slug')} unfollowed @{slug}")
[author_id] = (
local_session().query(Author.id).filter(Author.slug == slug).first()
)
[author_id] = local_session().query(Author.id).filter(Author.slug == slug).first()
if author_id and author_id in follows:
follows.remove(author_id)
await cache_author(follower_dict)
@@ -201,9 +197,7 @@ def reactions_follow(author_id, shout_id, auto=False):
)
if not following:
following = ShoutReactionsFollower(
follower=author_id, shout=shout.id, auto=auto
)
following = ShoutReactionsFollower(follower=author_id, shout=shout.id, auto=auto)
session.add(following)
session.commit()
return None
@@ -258,9 +252,7 @@ def author_unfollow(follower_id, slug):
flw = (
session.query(AuthorFollower)
.join(Author, Author.id == AuthorFollower.author)
.filter(
and_(AuthorFollower.follower == follower_id, Author.slug == slug)
)
.filter(and_(AuthorFollower.follower == follower_id, Author.slug == slug))
.first()
)
if flw:
@@ -273,9 +265,7 @@ def author_unfollow(follower_id, slug):
@query.field("get_shout_followers")
def get_shout_followers(
_, _info, slug: str = "", shout_id: int | None = None
) -> List[Author]:
def get_shout_followers(_, _info, slug: str = "", shout_id: int | None = None) -> List[Author]:
followers = []
with local_session() as session:
shout = None

View File

@@ -16,9 +16,7 @@ from services.logger import root_logger as logger
from services.schema import mutation, query
def query_notifications(
author_id: int, after: int = 0
) -> Tuple[int, int, List[Tuple[Notification, bool]]]:
def query_notifications(author_id: int, after: int = 0) -> Tuple[int, int, List[Tuple[Notification, bool]]]:
notification_seen_alias = aliased(NotificationSeen)
q = select(Notification, notification_seen_alias.viewer.label("seen")).outerjoin(
NotificationSeen,
@@ -63,9 +61,7 @@ def query_notifications(
return total, unread, notifications
def group_notification(
thread, authors=None, shout=None, reactions=None, entity="follower", action="follow"
):
def group_notification(thread, authors=None, shout=None, reactions=None, entity="follower", action="follow"):
reactions = reactions or []
authors = authors or []
return {
@@ -79,9 +75,7 @@ def group_notification(
}
def get_notifications_grouped(
author_id: int, after: int = 0, limit: int = 10, offset: int = 0
):
def get_notifications_grouped(author_id: int, after: int = 0, limit: int = 10, offset: int = 0):
"""
Retrieves notifications for a given author.
@@ -147,9 +141,7 @@ def get_notifications_grouped(
author_id = reaction.get("created_by", 0)
if shout_id and author_id:
with local_session() as session:
author = (
session.query(Author).filter(Author.id == author_id).first()
)
author = session.query(Author).filter(Author.id == author_id).first()
shout = session.query(Shout).filter(Shout.id == shout_id).first()
if shout and author:
author = author.dict()
@@ -162,9 +154,7 @@ def get_notifications_grouped(
if existing_group:
existing_group["seen"] = False
existing_group["authors"].append(author_id)
existing_group["reactions"] = (
existing_group["reactions"] or []
)
existing_group["reactions"] = existing_group["reactions"] or []
existing_group["reactions"].append(reaction)
groups_by_thread[thread_id] = existing_group
else:
@@ -217,9 +207,7 @@ async def load_notifications(_, info, after: int, limit: int = 50, offset=0):
try:
if author_id:
groups, unread, total = get_notifications_grouped(author_id, after, limit)
notifications = sorted(
groups.values(), key=lambda group: group.updated_at, reverse=True
)
notifications = sorted(groups.values(), key=lambda group: group.updated_at, reverse=True)
except Exception as e:
error = e
logger.error(e)
@@ -257,11 +245,7 @@ async def notifications_seen_after(_, info, after: int):
author_id = info.context.get("author", {}).get("id")
if author_id:
with local_session() as session:
nnn = (
session.query(Notification)
.filter(and_(Notification.created_at > after))
.all()
)
nnn = session.query(Notification).filter(and_(Notification.created_at > after)).all()
for n in nnn:
try:
ns = NotificationSeen(notification=n.id, viewer=author_id)

View File

@@ -35,9 +35,7 @@ async def rate_author(_, info, rated_slug, value):
return {}
else:
try:
rating = AuthorRating(
rater=rater_id, author=rated_author.id, plus=value > 0
)
rating = AuthorRating(rater=rater_id, author=rated_author.id, plus=value > 0)
session.add(rating)
session.commit()
except Exception as err:
@@ -105,9 +103,7 @@ def count_author_shouts_rating(session, author_id) -> int:
def get_author_rating_old(session, author: Author):
likes_count = (
session.query(AuthorRating)
.filter(and_(AuthorRating.author == author.id, AuthorRating.plus.is_(True)))
.count()
session.query(AuthorRating).filter(and_(AuthorRating.author == author.id, AuthorRating.plus.is_(True))).count()
)
dislikes_count = (
session.query(AuthorRating)
@@ -167,9 +163,7 @@ def get_author_rating_comments(session, author: Author) -> int:
and_(
replied_comment.kind == ReactionKind.COMMENT.value,
replied_comment.created_by == author.id,
Reaction.kind.in_(
[ReactionKind.LIKE.value, ReactionKind.DISLIKE.value]
),
Reaction.kind.in_([ReactionKind.LIKE.value, ReactionKind.DISLIKE.value]),
Reaction.reply_to == replied_comment.id,
Reaction.deleted_at.is_(None),
),
@@ -184,9 +178,7 @@ def add_author_rating_columns(q, group_list):
# old karma
q = q.outerjoin(AuthorRating, AuthorRating.author == Author.id)
q = q.add_columns(
func.sum(case((AuthorRating.plus == true(), 1), else_=-1)).label("rating")
)
q = q.add_columns(func.sum(case((AuthorRating.plus == true(), 1), else_=-1)).label("rating"))
# by shouts rating
shout_reaction = aliased(Reaction)
@@ -243,9 +235,7 @@ def add_author_rating_columns(q, group_list):
and_(
replied_comment.kind == ReactionKind.COMMENT.value,
replied_comment.created_by == Author.id,
reaction_2.kind.in_(
[ReactionKind.LIKE.value, ReactionKind.DISLIKE.value]
),
reaction_2.kind.in_([ReactionKind.LIKE.value, ReactionKind.DISLIKE.value]),
reaction_2.reply_to == replied_comment.id,
reaction_2.deleted_at.is_(None),
),

View File

@@ -23,15 +23,9 @@ from services.viewed import ViewedStorage
def add_reaction_stat_columns(q, aliased_reaction):
q = q.outerjoin(aliased_reaction).add_columns(
func.sum(aliased_reaction.id).label("reacted_stat"),
func.sum(
case((aliased_reaction.kind == str(ReactionKind.COMMENT.value), 1), else_=0)
).label("comments_stat"),
func.sum(
case((aliased_reaction.kind == str(ReactionKind.LIKE.value), 1), else_=0)
).label("likes_stat"),
func.sum(
case((aliased_reaction.kind == str(ReactionKind.DISLIKE.value), 1), else_=0)
).label("dislikes_stat"),
func.sum(case((aliased_reaction.kind == str(ReactionKind.COMMENT.value), 1), else_=0)).label("comments_stat"),
func.sum(case((aliased_reaction.kind == str(ReactionKind.LIKE.value), 1), else_=0)).label("likes_stat"),
func.sum(case((aliased_reaction.kind == str(ReactionKind.DISLIKE.value), 1), else_=0)).label("dislikes_stat"),
func.max(
case(
(aliased_reaction.kind != str(ReactionKind.COMMENT.value), None),
@@ -60,9 +54,7 @@ def check_to_feature(session, approver_id, reaction):
if is_featured_author(session, approver_id):
approvers = [approver_id]
# now count how many approvers are voted already
reacted_readers = (
session.query(Reaction).where(Reaction.shout == reaction.shout).all()
)
reacted_readers = session.query(Reaction).where(Reaction.shout == reaction.shout).all()
for reacted_reader in reacted_readers:
if is_featured_author(session, reacted_reader.id):
approvers.append(reacted_reader.id)
@@ -87,9 +79,7 @@ def check_to_unfeature(session, rejecter_id, reaction):
)
rejects = 0
for r in reactions:
approver = (
session.query(Author).filter(Author.id == r.created_by).first()
)
approver = session.query(Author).filter(Author.id == r.created_by).first()
if is_featured_author(session, approver):
if is_negative(r.kind):
rejects += 1
@@ -127,11 +117,7 @@ async def _create_reaction(session, shout, author_id: int, reaction):
await update_author_stat(author_id)
# collaborative editing
if (
rdict.get("reply_to")
and r.kind in PROPOSAL_REACTIONS
and author_id in shout.authors
):
if rdict.get("reply_to") and r.kind in PROPOSAL_REACTIONS and author_id in shout.authors:
handle_proposing(session, r, shout)
# рейтинг и саморегуляция
@@ -165,9 +151,7 @@ async def _create_reaction(session, shout, author_id: int, reaction):
def prepare_new_rating(reaction: dict, shout_id: int, session, author_id: int):
kind = reaction.get("kind")
opposite_kind = (
ReactionKind.DISLIKE.value if is_positive(kind) else ReactionKind.LIKE.value
)
opposite_kind = ReactionKind.DISLIKE.value if is_positive(kind) else ReactionKind.LIKE.value
q = select(Reaction).filter(
and_(
@@ -226,9 +210,7 @@ async def create_reaction(_, info, reaction):
return {"error": "cannot create reaction without a kind"}
if kind in RATING_REACTIONS:
error_result = prepare_new_rating(
reaction, shout_id, session, author_id
)
error_result = prepare_new_rating(reaction, shout_id, session, author_id)
if error_result:
return error_result
@@ -275,9 +257,7 @@ async def update_reaction(_, info, reaction):
if not r:
return {"error": "invalid reaction id"}
author = (
session.query(Author).filter(Author.user == user_id).first()
)
author = session.query(Author).filter(Author.user == user_id).first()
if author:
if r.created_by != author.id and "editor" not in roles:
return {"error": "access denied"}
@@ -460,9 +440,7 @@ async def reacted_shouts_updates(follower_id: int, limit=50, offset=0) -> List[S
select(Shout)
.outerjoin(
Reaction,
and_(
Reaction.shout == Shout.id, Reaction.created_by == follower_id
),
and_(Reaction.shout == Shout.id, Reaction.created_by == follower_id),
)
.outerjoin(Author, Shout.authors.any(id=follower_id))
.options(joinedload(Shout.reactions), joinedload(Shout.authors))
@@ -481,12 +459,7 @@ async def reacted_shouts_updates(follower_id: int, limit=50, offset=0) -> List[S
q2 = add_reaction_stat_columns(q2, aliased(Reaction))
# Sort shouts by the `last_comment` field
combined_query = (
union(q1, q2)
.order_by(desc(text("last_comment_stat")))
.limit(limit)
.offset(offset)
)
combined_query = union(q1, q2).order_by(desc(text("last_comment_stat"))).limit(limit).offset(offset)
results = session.execute(combined_query).scalars()
for [

View File

@@ -28,21 +28,14 @@ def filter_my(info, session, q):
user_id = info.context.get("user_id")
reader_id = info.context.get("author", {}).get("id")
if user_id and reader_id:
reader_followed_authors = select(AuthorFollower.author).where(
AuthorFollower.follower == reader_id
)
reader_followed_topics = select(TopicFollower.topic).where(
TopicFollower.follower == reader_id
)
reader_followed_authors = select(AuthorFollower.author).where(AuthorFollower.follower == reader_id)
reader_followed_topics = select(TopicFollower.topic).where(TopicFollower.follower == reader_id)
subquery = (
select(Shout.id)
.where(Shout.id == ShoutAuthor.shout)
.where(Shout.id == ShoutTopic.shout)
.where(
(ShoutAuthor.author.in_(reader_followed_authors))
| (ShoutTopic.topic.in_(reader_followed_topics))
)
.where((ShoutAuthor.author.in_(reader_followed_authors)) | (ShoutTopic.topic.in_(reader_followed_topics)))
)
q = q.filter(Shout.id.in_(subquery))
return q, reader_id
@@ -188,9 +181,7 @@ async def load_shouts_by(_, _info, options):
order_str = options.get("order_by")
if order_str in ["likes", "followers", "comments", "last_comment"]:
q = q.order_by(desc(text(f"{order_str}_stat")))
query_order_by = (
desc(order_by) if options.get("order_by_desc", True) else asc(order_by)
)
query_order_by = desc(order_by) if options.get("order_by_desc", True) else asc(order_by)
q = q.order_by(nulls_last(query_order_by))
# limit offset
@@ -257,20 +248,13 @@ async def load_shouts_feed(_, info, options):
Shout.featured_at if filters.get("featured") else Shout.published_at,
)
query_order_by = (
desc(order_by) if options.get("order_by_desc", True) else asc(order_by)
)
query_order_by = desc(order_by) if options.get("order_by_desc", True) else asc(order_by)
# pagination
offset = options.get("offset", 0)
limit = options.get("limit", 10)
q = (
q.group_by(Shout.id)
.order_by(nulls_last(query_order_by))
.limit(limit)
.offset(offset)
)
q = q.group_by(Shout.id).order_by(nulls_last(query_order_by)).limit(limit).offset(offset)
logger.debug(q.compile(compile_kwargs={"literal_binds": True}))
@@ -328,9 +312,7 @@ async def load_shouts_unrated(_, info, limit: int = 50, offset: int = 0):
and_(
Reaction.shout == Shout.id,
Reaction.reply_to.is_(None),
Reaction.kind.in_(
[ReactionKind.LIKE.value, ReactionKind.DISLIKE.value]
),
Reaction.kind.in_([ReactionKind.LIKE.value, ReactionKind.DISLIKE.value]),
),
)
.outerjoin(Author, Author.user == bindparam("user_id"))
@@ -403,9 +385,7 @@ async def load_shouts_random_top(_, _info, options):
aliased_reaction = aliased(Reaction)
subquery = (
select(Shout.id)
.outerjoin(aliased_reaction)
.where(and_(Shout.deleted_at.is_(None), Shout.layout.is_not(None)))
select(Shout.id).outerjoin(aliased_reaction).where(and_(Shout.deleted_at.is_(None), Shout.layout.is_not(None)))
)
subquery = apply_filters(subquery, options.get("filters", {}))
@@ -428,11 +408,7 @@ async def load_shouts_random_top(_, _info, options):
if random_limit:
subquery = subquery.limit(random_limit)
q = (
select(Shout)
.options(joinedload(Shout.authors), joinedload(Shout.topics))
.where(Shout.id.in_(subquery))
)
q = select(Shout).options(joinedload(Shout.authors), joinedload(Shout.topics)).where(Shout.id.in_(subquery))
q = add_reaction_stat_columns(q, aliased_reaction)

View File

@@ -12,9 +12,7 @@ from services.logger import root_logger as logger
def add_topic_stat_columns(q):
aliased_shout = aliased(ShoutTopic)
q = q.outerjoin(aliased_shout).add_columns(
func.count(distinct(aliased_shout.shout)).label("shouts_stat")
)
q = q.outerjoin(aliased_shout).add_columns(func.count(distinct(aliased_shout.shout)).label("shouts_stat"))
aliased_follower = aliased(TopicFollower)
q = q.outerjoin(aliased_follower, aliased_follower.topic == Topic.id).add_columns(
func.count(distinct(aliased_follower.follower)).label("followers_stat")
@@ -27,9 +25,7 @@ def add_topic_stat_columns(q):
def add_author_stat_columns(q):
aliased_shout = aliased(ShoutAuthor)
q = q.outerjoin(aliased_shout).add_columns(
func.count(distinct(aliased_shout.shout)).label("shouts_stat")
)
q = q.outerjoin(aliased_shout).add_columns(func.count(distinct(aliased_shout.shout)).label("shouts_stat"))
aliased_follower = aliased(AuthorFollower)
q = q.outerjoin(aliased_follower, aliased_follower.author == Author.id).add_columns(
func.count(distinct(aliased_follower.follower)).label("followers_stat")
@@ -79,9 +75,7 @@ def get_topic_authors_stat(topic_id: int):
def get_topic_followers_stat(topic_id: int):
aliased_followers = aliased(TopicFollower)
q = select(func.count(distinct(aliased_followers.follower))).filter(
aliased_followers.topic == topic_id
)
q = select(func.count(distinct(aliased_followers.follower))).filter(aliased_followers.topic == topic_id)
with local_session() as session:
result = session.execute(q).first()
return result[0] if result else 0
@@ -106,9 +100,7 @@ def get_topic_comments_stat(topic_id: int):
.group_by(Shout.id)
.subquery()
)
q = select(func.coalesce(func.sum(sub_comments.c.comments_count), 0)).filter(
ShoutTopic.topic == topic_id
)
q = select(func.coalesce(func.sum(sub_comments.c.comments_count), 0)).filter(ShoutTopic.topic == topic_id)
q = q.outerjoin(sub_comments, ShoutTopic.shout == sub_comments.c.shout_id)
with local_session() as session:
result = session.execute(q).first()
@@ -152,9 +144,7 @@ def get_author_authors_stat(author_id: int):
def get_author_followers_stat(author_id: int):
aliased_followers = aliased(AuthorFollower)
q = select(func.count(distinct(aliased_followers.follower))).filter(
aliased_followers.author == author_id
)
q = select(func.count(distinct(aliased_followers.follower))).filter(aliased_followers.author == author_id)
with local_session() as session:
result = session.execute(q).first()
return result[0] if result else 0
@@ -162,9 +152,7 @@ def get_author_followers_stat(author_id: int):
def get_author_comments_stat(author_id: int):
sub_comments = (
select(
Author.id, func.coalesce(func.count(Reaction.id)).label("comments_count")
)
select(Author.id, func.coalesce(func.count(Reaction.id)).label("comments_count"))
.select_from(Author) # явно указываем левый элемент join'а
.outerjoin(
Reaction,
@@ -219,9 +207,7 @@ def get_with_stat(q):
def author_follows_authors(author_id: int):
af = aliased(AuthorFollower, name="af")
author_follows_authors_query = (
select(Author)
.select_from(join(Author, af, Author.id == af.author))
.where(af.follower == author_id)
select(Author).select_from(join(Author, af, Author.id == af.author)).where(af.follower == author_id)
)
return get_with_stat(author_follows_authors_query)

View File

@@ -40,17 +40,11 @@ def get_topics_by_community(_, _info, community_id: int):
async def get_topics_by_author(_, _info, author_id=0, slug="", user=""):
topics_by_author_query = select(Topic)
if author_id:
topics_by_author_query = topics_by_author_query.join(Author).where(
Author.id == author_id
)
topics_by_author_query = topics_by_author_query.join(Author).where(Author.id == author_id)
elif slug:
topics_by_author_query = topics_by_author_query.join(Author).where(
Author.slug == slug
)
topics_by_author_query = topics_by_author_query.join(Author).where(Author.slug == slug)
elif user:
topics_by_author_query = topics_by_author_query.join(Author).where(
Author.user == user
)
topics_by_author_query = topics_by_author_query.join(Author).where(Author.user == user)
return get_with_stat(topics_by_author_query)