refactored-author-on-login-required
All checks were successful
Deploy on push / deploy (push) Successful in 23s

This commit is contained in:
2024-04-19 18:22:07 +03:00
parent 0ca6676474
commit b7d82d9cc5
18 changed files with 316 additions and 346 deletions

View File

@@ -1,52 +1,24 @@
from resolvers.author import (
get_author,
get_author_followers,
get_author_follows,
get_author_follows_authors,
get_author_follows_topics,
get_author_id,
get_authors_all,
load_authors_by,
search_authors,
update_author,
)
from resolvers.author import (get_author, get_author_followers,
get_author_follows, get_author_follows_authors,
get_author_follows_topics, get_author_id,
get_authors_all, load_authors_by, search_authors,
update_author)
from resolvers.community import get_communities_all, get_community
from resolvers.editor import create_shout, delete_shout, update_shout
from resolvers.follower import (
follow,
get_shout_followers,
get_topic_followers,
unfollow,
)
from resolvers.notifier import (
load_notifications,
notification_mark_seen,
notifications_seen_after,
notifications_seen_thread,
)
from resolvers.follower import (follow, get_shout_followers,
get_topic_followers, unfollow)
from resolvers.notifier import (load_notifications, notification_mark_seen,
notifications_seen_after,
notifications_seen_thread)
from resolvers.rating import rate_author
from resolvers.reaction import (
create_reaction,
delete_reaction,
load_reactions_by,
load_shouts_followed,
update_reaction,
)
from resolvers.reader import (
get_shout,
load_shouts_by,
load_shouts_feed,
load_shouts_random_top,
load_shouts_random_topic,
load_shouts_search,
load_shouts_unrated,
)
from resolvers.topic import (
get_topic,
get_topics_all,
get_topics_by_author,
get_topics_by_community,
)
from resolvers.reaction import (create_reaction, delete_reaction,
load_reactions_by, load_shouts_followed,
update_reaction)
from resolvers.reader import (get_shout, load_shouts_by, load_shouts_feed,
load_shouts_random_top, load_shouts_random_topic,
load_shouts_search, load_shouts_unrated)
from resolvers.topic import (get_topic, get_topics_all, get_topics_by_author,
get_topics_by_community)
from services.triggers import events_register
events_register()

View File

@@ -8,7 +8,8 @@ from sqlalchemy_searchable import search
from orm.author import Author, AuthorFollower
from orm.shout import ShoutAuthor, ShoutTopic
from orm.topic import Topic
from resolvers.stat import author_follows_authors, author_follows_topics, get_with_stat
from resolvers.stat import (author_follows_authors, author_follows_topics,
get_with_stat)
from services.auth import login_required
from services.cache import cache_author, cache_follower
from services.db import local_session

View File

@@ -9,50 +9,57 @@ from services.schema import mutation
@mutation.field("accept_invite")
@login_required
async def accept_invite(_, info, invite_id: int):
user_id = info.context["user_id"]
# Check if the user exists
with local_session() as session:
author = session.query(Author).filter(Author.user == user_id).first()
if author:
info.context["user_id"]
author_dict = info.context["author"]
author_id = author_dict.get("id")
if author_id:
author_id = int(author_id)
# Check if the user exists
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_d is author.id
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 not in shout.authors:
shout.authors.append(author)
session.delete(invite)
session.add(shout)
session.commit()
if author_id not in shout.authors:
author = (
session.query(Author).filter(Author.id == author_id).first()
)
if author:
shout.authors.append(author)
session.add(shout)
session.delete(invite)
session.commit()
return {"success": True, "message": "Invite accepted"}
else:
return {"error": "Shout not found"}
else:
return {"error": "Invalid invite or already accepted/rejected"}
else:
return {"error": "User not found"}
else:
return {"error": "Unauthorized"}
@mutation.field("reject_invite")
@login_required
async def reject_invite(_, info, invite_id: int):
user_id = info.context["user_id"]
info.context["user_id"]
author_dict = info.context["author"]
author_id = author_dict.get("id")
# Check if the user exists
with local_session() as session:
author = session.query(Author).filter(Author.user == user_id).first()
if author:
if author_id:
# Check if the user exists
with local_session() as session:
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.author_id is author_id
and invite.status is InviteStatus.PENDING.value
):
# Delete the invite
@@ -61,23 +68,21 @@ async def reject_invite(_, info, invite_id: int):
return {"success": True, "message": "Invite rejected"}
else:
return {"error": "Invalid invite or already accepted/rejected"}
else:
return {"error": "User not found"}
return {"error": "User not found"}
@mutation.field("create_invite")
@login_required
async def create_invite(_, info, slug: str = "", author_id: int = 0):
user_id = info.context["user_id"]
# Check if the inviter is the owner of the shout
with local_session() as session:
shout = session.query(Shout).filter(Shout.slug == slug).first()
inviter = session.query(Author).filter(Author.user == user_id).first()
if inviter and shout and shout.authors and inviter.id is shout.created_by:
# Check if the author is a valid author
author = session.query(Author).filter(Author.id == author_id).first()
if author:
author_dict = info.context["author"]
author_id = author_dict.get("id")
if author_id:
# Check if the inviter is the owner of the shout
with local_session() as session:
shout = session.query(Shout).filter(Shout.slug == slug).first()
inviter = session.query(Author).filter(Author.user == user_id).first()
if inviter and shout and shout.authors and inviter.id is shout.created_by:
# Check if an invite already exists
existing_invite = (
session.query(Invite)
@@ -105,8 +110,8 @@ async def create_invite(_, info, slug: str = "", author_id: int = 0):
return {"error": None, "invite": new_invite}
else:
return {"error": "Invalid author"}
else:
return {"error": "Access denied"}
else:
return {"error": "Access denied"}
@mutation.field("remove_author")
@@ -130,18 +135,20 @@ async def remove_author(_, info, slug: str = "", author_id: int = 0):
@mutation.field("remove_invite")
@login_required
async def remove_invite(_, info, invite_id: int):
user_id = info.context["user_id"]
info.context["user_id"]
# Check if the user exists
with local_session() as session:
author = session.query(Author).filter(Author.user == user_id).first()
if author:
author_dict = info.context["author"]
author_id = author_dict.get("id")
if author_id:
# Check if the user exists
with local_session() as session:
author_id == int(author_id)
# Check if the invite exists
invite = session.query(Invite).filter(Invite.id == invite_id).first()
if isinstance(invite, Invite):
shout = session.query(Shout).filter(Shout.id == invite.shout_id).first()
if shout and shout.deleted_at is None and invite:
if invite.inviter_id is author.id or author.id is shout.created_by:
if invite.inviter_id is author_id or author_id == shout.created_by:
if invite.status is InviteStatus.PENDING.value:
# Delete the invite
session.delete(invite)
@@ -149,5 +156,5 @@ async def remove_invite(_, info, invite_id: int):
return {}
else:
return {"error": "Invalid invite or already accepted/rejected"}
else:
return {"error": "Author not found"}
else:
return {"error": "Author not found"}

View File

@@ -4,7 +4,6 @@ from sqlalchemy import and_, desc, select
from sqlalchemy.orm import joinedload
from sqlalchemy.sql.functions import coalesce
from orm.author import Author
from orm.rating import is_negative, is_positive
from orm.reaction import Reaction, ReactionKind
from orm.shout import Shout, ShoutAuthor, ShoutTopic
@@ -24,6 +23,8 @@ from services.search import search_service
async def get_my_shout(_, info, shout_id: int):
with local_session() as session:
user_id = info.context.get("user_id", "")
author_dict = info.context["author"]
author_id = author_dict.get("id")
if not user_id:
return {"error": "unauthorized", "shout": None}
shout = (
@@ -36,12 +37,11 @@ async def get_my_shout(_, info, shout_id: int):
if not shout:
return {"error": "no shout found", "shout": None}
if not bool(shout.published_at):
author = session.query(Author).filter(Author.user == user_id).first()
if not author:
if not author_id:
return {"error": "no author found", "shout": None}
roles = info.context.get("roles", [])
if "editor" not in roles and not filter(
lambda x: x.id == author.id, [x for x in shout.authors]
lambda x: x.id == int(author_id), [x for x in shout.authors]
):
return {"error": "forbidden", "shout": None}
return {"error": None, "shout": shout}
@@ -50,15 +50,18 @@ async def get_my_shout(_, info, shout_id: int):
@query.field("get_shouts_drafts")
@login_required
async def get_shouts_drafts(_, info):
user_id = info.context.get("user_id")
info.context.get("user_id")
author_dict = info.context["author"]
author_id = author_dict.get("id")
shouts = []
with local_session() as session:
author = session.query(Author).filter(Author.user == user_id).first()
if author:
if author_id:
q = (
select(Shout)
.options(joinedload(Shout.authors), joinedload(Shout.topics))
.filter(and_(Shout.deleted_at.is_(None), Shout.created_by == 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)
@@ -71,67 +74,68 @@ async def get_shouts_drafts(_, info):
@login_required
async def create_shout(_, info, inp):
user_id = info.context.get("user_id")
if user_id:
author_dict = info.context["author"]
author_id = author_dict.get("id")
if user_id and author_id:
with local_session() as session:
author = session.query(Author).filter(Author.user == user_id).first()
if isinstance(author, Author):
current_time = int(time.time())
slug = inp.get("slug") or f"draft-{current_time}"
shout_dict = {
"title": inp.get("title", ""),
"subtitle": inp.get("subtitle", ""),
"lead": inp.get("lead", ""),
"description": inp.get("description", ""),
"body": inp.get("body", ""),
"layout": inp.get("layout", "article"),
"created_by": author.id,
"authors": [],
"slug": slug,
"topics": inp.get("topics", []),
"published_at": None,
"created_at": current_time, # Set created_at as Unix timestamp
}
author_id = int(author_id)
current_time = int(time.time())
slug = inp.get("slug") or f"draft-{current_time}"
shout_dict = {
"title": inp.get("title", ""),
"subtitle": inp.get("subtitle", ""),
"lead": inp.get("lead", ""),
"description": inp.get("description", ""),
"body": inp.get("body", ""),
"layout": inp.get("layout", "article"),
"created_by": author_id,
"authors": [],
"slug": slug,
"topics": inp.get("topics", []),
"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()
)
c = 1
while same_slug_shout is not None:
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()
)
c += 1
shout_dict["slug"] += f"-{c}"
new_shout = Shout(**shout_dict)
session.add(new_shout)
c += 1
shout_dict["slug"] += f"-{c}"
new_shout = Shout(**shout_dict)
session.add(new_shout)
session.commit()
# NOTE: requesting new shout back
shout = session.query(Shout).where(Shout.slug == slug).first()
if shout:
sa = ShoutAuthor(shout=shout.id, author=author_id)
session.add(sa)
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)
session.commit()
# NOTE: requesting new shout back
shout = session.query(Shout).where(Shout.slug == slug).first()
if shout:
sa = ShoutAuthor(shout=shout.id, author=author.id)
session.add(sa)
reactions_follow(author_id, shout.id, True)
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)
# notifier
# await notify_shout(shout_dict, 'create')
session.commit()
reactions_follow(author.id, shout.id, True)
# notifier
# await notify_shout(shout_dict, 'create')
return {"shout": shout}
return {"shout": shout}
return {"error": "cant create shout" if user_id else "unauthorized"}
@@ -220,6 +224,8 @@ def patch_topics(session, shout, topics_input):
async def update_shout(_, info, shout_id: int, shout_input=None, publish=False):
user_id = info.context.get("user_id")
roles = info.context.get("roles", [])
author_dict = info.context["author"]
author_id = author_dict.get("id")
shout_input = shout_input or {}
current_time = int(time.time())
shout_id = shout_id or shout_input.get("id", shout_id)
@@ -228,9 +234,8 @@ async def update_shout(_, info, shout_id: int, shout_input=None, publish=False):
return {"error": "unauthorized"}
try:
with local_session() as session:
author = session.query(Author).filter(Author.user == user_id).first()
if author:
logger.info(f"author for shout#{shout_id} detected {author.dict()}")
if author_id:
logger.info(f"author for shout#{shout_id} detected author #{author_id}")
shout_by_id = session.query(Shout).filter(Shout.id == shout_id).first()
if not shout_by_id:
return {"error": "shout not found"}
@@ -249,7 +254,7 @@ async def update_shout(_, info, shout_id: int, shout_input=None, publish=False):
if (
filter(
lambda x: x.id == author.id, [x for x in shout_by_id.authors]
lambda x: x.id == author_id, [x for x in shout_by_id.authors]
)
or "editor" in roles
):
@@ -297,28 +302,29 @@ async def update_shout(_, info, shout_id: int, shout_input=None, publish=False):
@login_required
async def delete_shout(_, info, shout_id: int):
user_id = info.context.get("user_id")
roles = info.context.get("roles")
if user_id:
roles = info.context.get("roles", [])
author_dict = info.context["author"]
author_id = author_dict.get("id")
if user_id and author_id:
author_id = int(author_id)
with local_session() as session:
author = session.query(Author).filter(Author.user == user_id).first()
shout = session.query(Shout).filter(Shout.id == shout_id).first()
if not shout:
if not isinstance(shout, Shout):
return {"error": "invalid shout id"}
if author and shout:
# NOTE: only owner and editor can mark the shout as deleted
if shout.created_by == author.id or "editor" in roles:
for author_id in shout.authors:
reactions_unfollow(author_id, shout_id)
shout_dict = shout.dict()
# NOTE: only owner and editor can mark the shout as deleted
if shout_dict["created_by"] == author_id or "editor" in roles:
for author_id in shout.authors:
reactions_unfollow(author_id, shout_id)
shout_dict = shout.dict()
shout_dict["deleted_at"] = int(time.time())
Shout.update(shout, shout_dict)
session.add(shout)
session.commit()
await notify_shout(shout_dict, "delete")
return {"error": None}
else:
return {"error": "access denied"}
shout_dict["deleted_at"] = int(time.time())
Shout.update(shout, shout_dict)
session.add(shout)
session.commit()
await notify_shout(shout_dict, "delete")
return {"error": None}
else:
return {"error": "access denied"}
def handle_proposing(session, r, shout):

View File

@@ -11,7 +11,8 @@ from orm.community import Community
from orm.reaction import Reaction
from orm.shout import Shout, ShoutReactionsFollower
from orm.topic import Topic, TopicFollower
from resolvers.stat import author_follows_authors, author_follows_topics, get_with_stat
from resolvers.stat import (author_follows_authors, author_follows_topics,
get_with_stat)
from services.auth import login_required
from services.cache import DEFAULT_FOLLOWS, cache_follower
from services.db import local_session
@@ -26,43 +27,42 @@ from services.schema import mutation, query
async def follow(_, info, what, slug):
error = None
user_id = info.context.get("user_id")
if not user_id:
follower_dict = info.context["author"]
follower_id = follower_dict.get("id")
if not user_id or not follower_id:
return {"error": "unauthorized"}
follower = local_session().query(Author).filter(Author.user == user_id).first()
if not follower:
return {"error": "cant find follower account"}
entity = what.lower()
follows = []
follows_str = await redis.execute("GET", f"author:{follower.id}:follows-{entity}s")
follows_str = await redis.execute("GET", f"author:{follower_id}:follows-{entity}s")
if isinstance(follows_str, str):
follows = json.loads(follows_str)
if not follower:
return {"error": "cant find follower"}
if not follows:
return {"error": "cant find following cache"}
if what == "AUTHOR":
error = author_follow(follower.id, slug)
follower_id = int(follower_id)
error = author_follow(follower_id, slug)
if not error:
result = get_with_stat(select(Author).where(Author.slug == slug))
if result:
[author] = result
if author:
await cache_follower(follower, author)
await notify_follower(follower.dict(), author.id, "follow")
await cache_follower(follower_dict, author.dict())
await notify_follower(follower_dict, author.id, "follow")
if not any(a["id"] == author.id for a in follows):
follows.append(author.dict())
elif what == "TOPIC":
error = topic_follow(follower.id, slug)
error = topic_follow(follower_id, slug)
elif what == "COMMUNITY":
# FIXME: when more communities
follows = local_session().execute(select(Community))
elif what == "SHOUT":
error = reactions_follow(follower.id, slug)
error = reactions_follow(follower_id, slug)
if error:
return {"error": error}
@@ -76,38 +76,39 @@ async def unfollow(_, info, what, slug):
follows = []
error = None
user_id = info.context.get("user_id")
follower_dict = info.context["author"]
follower_id = follower_dict.get("id")
if not user_id:
return {"error": "unauthorized"}
follower = local_session().query(Author).filter(Author.user == user_id).first()
if not follower:
if not follower_id:
return {"error": "cant find follower account"}
if what == "AUTHOR":
error = author_unfollow(follower.id, slug)
error = author_unfollow(follower_id, slug)
# NOTE: after triggers should update cached stats
if not error:
logger.info(f"@{follower.slug} unfollowed @{slug}")
logger.info(f"@{follower_dict.get('slug')} unfollowed @{slug}")
author = local_session().query(Author).where(Author.slug == slug).first()
if isinstance(author, Author):
await cache_follower(follower, author, False)
await notify_follower(follower.dict(), author.id, "unfollow")
await cache_follower(follower_dict, author.dict(), False)
await notify_follower(follower_dict, author.id, "unfollow")
for idx, item in enumerate(follows):
if item["id"] == author.id:
follows.pop(idx) # Remove the author_dict from the follows list
break
elif what == "TOPIC":
error = topic_unfollow(follower.id, slug)
error = topic_unfollow(follower_id, slug)
elif what == "COMMUNITY":
follows = local_session().execute(select(Community))
elif what == "SHOUT":
error = reactions_unfollow(follower.id, slug)
error = reactions_unfollow(follower_id, slug)
entity = what.lower()
follows_str = await redis.execute("GET", f"author:{follower.id}:follows-{entity}s")
follows_str = await redis.execute("GET", f"author:{follower_id}:follows-{entity}s")
if isinstance(follows_str, str):
follows = json.loads(follows_str)
return {"error": error, f"{entity}s": follows}

View File

@@ -8,12 +8,8 @@ from sqlalchemy.orm import aliased
from sqlalchemy.sql import not_
from orm.author import Author
from orm.notification import (
Notification,
NotificationAction,
NotificationEntity,
NotificationSeen,
)
from orm.notification import (Notification, NotificationAction,
NotificationEntity, NotificationSeen)
from orm.shout import Shout
from services.auth import login_required
from services.db import local_session
@@ -213,7 +209,8 @@ def get_notifications_grouped(
@query.field("load_notifications")
@login_required
async def load_notifications(_, info, after: int, limit: int = 50, offset=0):
author_id = info.context.get("author_id")
author_dict = info.context.get("author")
author_id = author_dict.get("id")
error = None
total = 0
unread = 0
@@ -238,7 +235,7 @@ async def load_notifications(_, info, after: int, limit: int = 50, offset=0):
@mutation.field("notification_mark_seen")
@login_required
async def notification_mark_seen(_, info, notification_id: int):
author_id = info.context.get("author_id")
author_id = info.context.get("author", {}).get("id")
if author_id:
with local_session() as session:
try:
@@ -258,7 +255,7 @@ async def notifications_seen_after(_, info, after: int):
# TODO: use latest loaded notification_id as input offset parameter
error = None
try:
author_id = info.context.get("author_id")
author_id = info.context.get("author", {}).get("id")
if author_id:
with local_session() as session:
nnn = (
@@ -283,7 +280,7 @@ async def notifications_seen_after(_, info, after: int):
@login_required
async def notifications_seen_thread(_, info, thread: str, after: int):
error = None
author_id = info.context.get("author_id")
author_id = info.context.get("author", {}).get("id")
if author_id:
[shout_id, reply_to_id] = thread.split(":")
with local_session() as session:

View File

@@ -12,17 +12,17 @@ from services.schema import mutation
@mutation.field("rate_author")
@login_required
async def rate_author(_, info, rated_slug, value):
user_id = info.context["user_id"]
info.context["user_id"]
rater_id = info.context.get("author", {}).get("id")
with local_session() as session:
rater_id = int(rater_id)
rated_author = session.query(Author).filter(Author.slug == rated_slug).first()
rater = session.query(Author).filter(Author.slug == user_id).first()
if rater and rated_author:
if rater_id and rated_author:
rating: AuthorRating = (
session.query(AuthorRating)
.filter(
and_(
AuthorRating.rater == rater.id,
AuthorRating.rater == rater_id,
AuthorRating.author == rated_author.id,
)
)
@@ -36,7 +36,7 @@ async def rate_author(_, info, rated_slug, value):
else:
try:
rating = AuthorRating(
rater=rater.id, author=rated_author.id, plus=value > 0
rater=rater_id, author=rated_author.id, plus=value > 0
)
session.add(rating)
session.commit()

View File

@@ -1,17 +1,18 @@
import time
from typing import List
from resolvers.stat import update_author_stat
from sqlalchemy import and_, asc, case, desc, func, select, text
from sqlalchemy.orm import aliased, joinedload
from sqlalchemy.sql import union
from orm.author import Author
from orm.rating import PROPOSAL_REACTIONS, RATING_REACTIONS, is_negative, is_positive
from orm.rating import (PROPOSAL_REACTIONS, RATING_REACTIONS, is_negative,
is_positive)
from orm.reaction import Reaction, ReactionKind
from orm.shout import Shout
from resolvers.editor import handle_proposing
from resolvers.follower import reactions_follow
from resolvers.stat import update_author_stat
from services.auth import add_user_role, login_required
from services.db import local_session
from services.logger import root_logger as logger
@@ -116,7 +117,7 @@ def set_unfeatured(session, shout_id):
session.commit()
async def _create_reaction(session, shout, author, reaction):
async def _create_reaction(session, shout, author_id: int, reaction):
r = Reaction(**reaction)
session.add(r)
session.commit()
@@ -124,38 +125,38 @@ async def _create_reaction(session, shout, author, reaction):
# пересчет счетчика комментариев
if str(r.kind) == ReactionKind.COMMENT.value:
await update_author_stat(author)
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
and author_id in shout.authors
):
handle_proposing(session, r, shout)
# рейтинг и саморегуляция
if r.kind in RATING_REACTIONS:
# self-regultaion mechanics
if check_to_unfeature(session, author.id, r):
if check_to_unfeature(session, author_id, r):
set_unfeatured(session, shout.id)
elif check_to_feature(session, author.id, r):
elif check_to_feature(session, author_id, r):
await set_featured(session, shout.id)
# follow if liked
if r.kind == ReactionKind.LIKE.value:
try:
# reactions auto-following
reactions_follow(author.id, reaction["shout"], True)
reactions_follow(author_id, reaction["shout"], True)
except Exception:
pass
# обновление счетчика комментариев в кеше
if str(r.kind) == ReactionKind.COMMENT.value:
await update_author_stat(author)
await update_author_stat(author_id)
rdict["shout"] = shout.dict()
rdict["created_by"] = author.id
rdict["created_by"] = author_id
rdict["stat"] = {"commented": 0, "reacted": 0, "rating": 0}
# notifications call
@@ -164,7 +165,7 @@ async def _create_reaction(session, shout, author, reaction):
return rdict
def prepare_new_rating(reaction: dict, shout_id: int, session, author: Author):
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
@@ -173,7 +174,7 @@ def prepare_new_rating(reaction: dict, shout_id: int, session, author: Author):
q = select(Reaction).filter(
and_(
Reaction.shout == shout_id,
Reaction.created_by == author.id,
Reaction.created_by == author_id,
Reaction.kind.in_(RATING_REACTIONS),
)
)
@@ -182,18 +183,18 @@ def prepare_new_rating(reaction: dict, shout_id: int, session, author: Author):
q = q.filter(Reaction.reply_to == reply_to)
rating_reactions = session.execute(q).all()
same_rating = filter(
lambda r: r.created_by == author.id and r.kind == opposite_kind,
lambda r: r.created_by == author_id and r.kind == opposite_kind,
rating_reactions,
)
opposite_rating = filter(
lambda r: r.created_by == author.id and r.kind == opposite_kind,
lambda r: r.created_by == author_id and r.kind == opposite_kind,
rating_reactions,
)
if same_rating:
return {"error": "You can't rate the same thing twice"}
elif opposite_rating:
return {"error": "Remove opposite vote first"}
elif filter(lambda r: r.created_by == author.id, rating_reactions):
elif filter(lambda r: r.created_by == author_id, rating_reactions):
return {"error": "You can't rate your own thing"}
return
@@ -202,7 +203,8 @@ def prepare_new_rating(reaction: dict, shout_id: int, session, author: Author):
@login_required
async def create_reaction(_, info, reaction):
logger.debug(f"{info.context} for {reaction}")
user_id = info.context.get("user_id")
info.context.get("user_id")
author_id = info.context.get("author", {}).get("id")
shout_id = reaction.get("shout")
if not shout_id:
@@ -211,9 +213,8 @@ async def create_reaction(_, info, reaction):
try:
with local_session() as session:
shout = session.query(Shout).filter(Shout.id == shout_id).first()
author = session.query(Author).filter(Author.user == user_id).first()
if shout and author:
reaction["created_by"] = author.id
if shout and author_id:
reaction["created_by"] = int(author_id)
kind = reaction.get("kind")
if not kind and isinstance(reaction.get("body"), str):
@@ -224,12 +225,12 @@ async def create_reaction(_, info, reaction):
if kind in RATING_REACTIONS:
error_result = prepare_new_rating(
reaction, shout_id, session, author
reaction, shout_id, session, author_id
)
if error_result:
return error_result
rdict = await _create_reaction(session, shout, author, reaction)
rdict = await _create_reaction(session, shout, author_id, reaction)
# TODO: call recount ratings periodically
@@ -315,13 +316,14 @@ async def update_reaction(_, info, reaction):
async def delete_reaction(_, info, reaction_id: int):
logger.debug(f"{info.context} for {reaction_id}")
user_id = info.context.get("user_id")
author_id = info.context.get("author", {}).get("id")
roles = info.context.get("roles", [])
if user_id:
with local_session() as session:
try:
author = session.query(Author).filter(Author.user == user_id).one()
r = session.query(Reaction).filter(Reaction.id == reaction_id).one()
if r.created_by != author.id and "editor" not in roles:
if r.created_by != author_id and "editor" not in roles:
return {"error": "access denied"}
logger.debug(f"{user_id} user removing his #{reaction_id} reaction")

View File

@@ -1,6 +1,7 @@
from sqlalchemy import bindparam, distinct, or_, text
from sqlalchemy.orm import aliased, joinedload
from sqlalchemy.sql.expression import and_, asc, case, desc, func, nulls_last, select
from sqlalchemy.sql.expression import (and_, asc, case, desc, func, nulls_last,
select)
from orm.author import Author, AuthorFollower
from orm.reaction import Reaction, ReactionKind
@@ -25,31 +26,26 @@ def query_shouts():
def filter_my(info, session, q):
reader_id = None
user_id = None
if isinstance(info.context, dict):
user_id = info.context.get("user_id")
if user_id:
reader = session.query(Author).filter(Author.user == user_id).first()
if reader:
reader_followed_authors = select(AuthorFollower.author).where(
AuthorFollower.follower == reader.id
)
reader_followed_topics = select(TopicFollower.topic).where(
TopicFollower.follower == reader.id
)
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
)
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))
)
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))
)
q = q.filter(Shout.id.in_(subquery))
reader_id = reader.id
)
q = q.filter(Shout.id.in_(subquery))
return q, reader_id

View File

@@ -5,8 +5,8 @@ from orm.author import Author, AuthorFollower
from orm.reaction import Reaction, ReactionKind
from orm.shout import Shout, ShoutAuthor, ShoutTopic
from orm.topic import Topic, TopicFollower
from services.db import local_session
from services.cache import cache_author
from services.db import local_session
from services.logger import root_logger as logger
@@ -167,8 +167,8 @@ def author_follows_topics(author_id: int):
return get_with_stat(q)
async def update_author_stat(author: Author):
author_with_stat = get_with_stat(select(Author).where(Author.id == author.id))
async def update_author_stat(author_id: int):
author_with_stat = get_with_stat(select(Author).where(Author.id == author_id))
if isinstance(author_with_stat, Author):
author_dict = author_with_stat.dict()
await cache_author(author_dict)