wip refactoring: reactions, storages isolated
This commit is contained in:
@@ -1,14 +1,12 @@
|
||||
from resolvers.auth import login, sign_out, is_email_used, register, confirm, auth_forget, auth_reset
|
||||
from resolvers.zine import get_shout_by_slug, subscribe, unsubscribe, view_shout, rate_shout, \
|
||||
from resolvers.zine import get_shout_by_slug, follow, unfollow, view_shout, \
|
||||
top_month, top_overall, recent_published, recent_all, top_viewed, \
|
||||
shouts_by_authors, shouts_by_topics, shouts_by_communities
|
||||
from resolvers.profile import get_users_by_slugs, get_current_user, shouts_reviewed
|
||||
from resolvers.topics import topic_subscribe, topic_unsubscribe, topics_by_author, \
|
||||
topics_by_community, topics_by_slugs
|
||||
from resolvers.comments import create_comment, delete_comment, update_comment, rate_comment
|
||||
from resolvers.collab import get_shout_proposals, create_proposal, delete_proposal, \
|
||||
update_proposal, rate_proposal, decline_proposal, disable_proposal, accept_proposal, \
|
||||
invite_author, remove_author
|
||||
from resolvers.profile import get_users_by_slugs, get_current_user, get_user_reacted_shouts, get_user_roles
|
||||
from resolvers.topics import topic_follow, topic_unfollow, topics_by_author, topics_by_community, topics_by_slugs
|
||||
# from resolvers.feed import shouts_for_feed, my_candidates
|
||||
from resolvers.reactions import create_reaction, delete_reaction, update_reaction, get_all_reactions
|
||||
from resolvers.collab import invite_author, remove_author
|
||||
from resolvers.editor import create_shout, delete_shout, update_shout
|
||||
from resolvers.community import create_community, delete_community, get_community, get_communities
|
||||
|
||||
@@ -20,36 +18,43 @@ __all__ = [
|
||||
"confirm",
|
||||
"auth_forget",
|
||||
"auth_reset"
|
||||
"sign_out",
|
||||
|
||||
# profile
|
||||
"get_current_user",
|
||||
"get_users_by_slugs",
|
||||
|
||||
# zine
|
||||
"shouts_for_feed",
|
||||
"my_candidates",
|
||||
"recent_published",
|
||||
"recent_reacted",
|
||||
"recent_all",
|
||||
"shouts_by_topics",
|
||||
"shouts_by_authors",
|
||||
"shouts_by_communities",
|
||||
"shouts_reviewed",
|
||||
"get_user_reacted_shouts",
|
||||
"top_month",
|
||||
"top_overall",
|
||||
"top_viewed",
|
||||
"rate_shout",
|
||||
"view_shout",
|
||||
"view_reaction",
|
||||
"get_shout_by_slug",
|
||||
|
||||
# editor
|
||||
"create_shout",
|
||||
"update_shout",
|
||||
"delete_shout",
|
||||
# collab
|
||||
"invite_author",
|
||||
"remove_author"
|
||||
|
||||
# topics
|
||||
"topics_by_slugs",
|
||||
"topics_by_community",
|
||||
"topics_by_author",
|
||||
"topic_subscribe",
|
||||
"topic_unsubscribe",
|
||||
"topic_follow",
|
||||
"topic_unfollow",
|
||||
|
||||
# communities
|
||||
"get_community",
|
||||
@@ -57,22 +62,12 @@ __all__ = [
|
||||
"create_community",
|
||||
"delete_community",
|
||||
|
||||
# comments
|
||||
"get_shout_comments",
|
||||
"comments_subscribe",
|
||||
"comments_unsubscribe",
|
||||
"create_comment",
|
||||
"update_comment",
|
||||
"delete_comment",
|
||||
|
||||
# collab
|
||||
"get_shout_proposals",
|
||||
"create_proposal",
|
||||
"update_proposal",
|
||||
"disable_proposal",
|
||||
"accept_proposal",
|
||||
"decline_proposal",
|
||||
"delete_proposal",
|
||||
"invite_author",
|
||||
"remove_author"
|
||||
# reactions
|
||||
"get_shout_reactions",
|
||||
"reactions_follow",
|
||||
"reactions_unfollow",
|
||||
"create_reaction",
|
||||
"update_reaction",
|
||||
"delete_reaction",
|
||||
"get_all_reactions",
|
||||
]
|
||||
|
@@ -1,7 +1,6 @@
|
||||
from graphql import GraphQLResolveInfo
|
||||
from transliterate import translit
|
||||
from urllib.parse import quote_plus
|
||||
|
||||
from auth.authenticate import login_required, ResetPassword
|
||||
from auth.authorize import Authorize
|
||||
from auth.identity import Identity
|
||||
@@ -12,7 +11,6 @@ from orm.base import local_session
|
||||
from resolvers.base import mutation, query
|
||||
from resolvers.profile import get_user_info
|
||||
from exceptions import InvalidPassword, InvalidToken
|
||||
|
||||
from settings import JWT_AUTH_HEADER
|
||||
|
||||
@mutation.field("confirmEmail")
|
||||
|
@@ -1,221 +1,9 @@
|
||||
import asyncio
|
||||
from orm import Proposal, ProposalRating, UserStorage
|
||||
from datetime import datetime
|
||||
from orm.base import local_session
|
||||
from orm.shout import Shout
|
||||
from sqlalchemy.orm import selectinload
|
||||
from orm.user import User
|
||||
from resolvers.base import mutation, query
|
||||
from resolvers.base import mutation
|
||||
from auth.authenticate import login_required
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class ProposalResult:
|
||||
def __init__(self, status, proposal):
|
||||
self.status = status
|
||||
self.proposal = proposal
|
||||
|
||||
class ProposalStorage:
|
||||
lock = asyncio.Lock()
|
||||
subscriptions = []
|
||||
|
||||
@staticmethod
|
||||
async def register_subscription(subs):
|
||||
async with ProposalStorage.lock:
|
||||
ProposalStorage.subscriptions.append(subs)
|
||||
|
||||
@staticmethod
|
||||
async def del_subscription(subs):
|
||||
async with ProposalStorage.lock:
|
||||
ProposalStorage.subscriptions.remove(subs)
|
||||
|
||||
@staticmethod
|
||||
async def put(message_result):
|
||||
async with ProposalStorage.lock:
|
||||
for subs in ProposalStorage.subscriptions:
|
||||
if message_result.message["chatId"] == subs.chat_id:
|
||||
subs.queue.put_nowait(message_result)
|
||||
|
||||
|
||||
|
||||
@query.field("getShoutProposals")
|
||||
@login_required
|
||||
async def get_shout_proposals(_, info, slug):
|
||||
auth = info.context["request"].auth
|
||||
user_id = auth.user_id
|
||||
with local_session() as session:
|
||||
proposals = session.query(Proposal).\
|
||||
options(selectinload(Proposal.ratings)).\
|
||||
filter(Proposal.shout == slug).\
|
||||
group_by(Proposal.id).all()
|
||||
shout = session.query(Shout).filter(Shout.slug == slug).first()
|
||||
authors = [author.id for author in shout.authors]
|
||||
if user_id not in authors:
|
||||
return {"error": "access denied"}
|
||||
for proposal in proposals:
|
||||
proposal.createdBy = await UserStorage.get_user(proposal.createdBy)
|
||||
return proposals
|
||||
|
||||
|
||||
@mutation.field("createProposal")
|
||||
@login_required
|
||||
async def create_proposal(_, info, body, shout, range = None):
|
||||
auth = info.context["request"].auth
|
||||
user_id = auth.user_id
|
||||
|
||||
proposal = Proposal.create(
|
||||
createdBy = user_id,
|
||||
body = body,
|
||||
shout = shout,
|
||||
range = range
|
||||
)
|
||||
|
||||
result = ProposalResult("NEW", proposal)
|
||||
await ProposalStorage.put(result)
|
||||
|
||||
return {"proposal": proposal}
|
||||
|
||||
@mutation.field("updateProposal")
|
||||
@login_required
|
||||
async def update_proposal(_, info, id, body):
|
||||
auth = info.context["request"].auth
|
||||
user_id = auth.user_id
|
||||
|
||||
with local_session() as session:
|
||||
proposal = session.query(Proposal).filter(Proposal.id == id).first()
|
||||
shout = session.query(Shout).filter(Shout.sllug == proposal.shout).first()
|
||||
authors = [author.id for author in shout.authors]
|
||||
if not proposal:
|
||||
return {"error": "invalid proposal id"}
|
||||
if proposal.author in authors:
|
||||
return {"error": "access denied"}
|
||||
proposal.body = body
|
||||
proposal.updatedAt = datetime.now()
|
||||
session.commit()
|
||||
|
||||
result = ProposalResult("UPDATED", proposal)
|
||||
await ProposalStorage.put(result)
|
||||
|
||||
return {"proposal": proposal}
|
||||
|
||||
@mutation.field("deleteProposal")
|
||||
@login_required
|
||||
async def delete_proposal(_, info, id):
|
||||
auth = info.context["request"].auth
|
||||
user_id = auth.user_id
|
||||
|
||||
with local_session() as session:
|
||||
proposal = session.query(Proposal).filter(Proposal.id == id).first()
|
||||
if not proposal:
|
||||
return {"error": "invalid proposal id"}
|
||||
if proposal.createdBy != user_id:
|
||||
return {"error": "access denied"}
|
||||
|
||||
proposal.deletedAt = datetime.now()
|
||||
session.commit()
|
||||
|
||||
result = ProposalResult("DELETED", proposal)
|
||||
await ProposalStorage.put(result)
|
||||
|
||||
return {}
|
||||
|
||||
@mutation.field("disableProposal")
|
||||
@login_required
|
||||
async def disable_proposal(_, info, id):
|
||||
auth = info.context["request"].auth
|
||||
user_id = auth.user_id
|
||||
|
||||
with local_session() as session:
|
||||
proposal = session.query(Proposal).filter(Proposal.id == id).first()
|
||||
if not proposal:
|
||||
return {"error": "invalid proposal id"}
|
||||
if proposal.createdBy != user_id:
|
||||
return {"error": "access denied"}
|
||||
|
||||
proposal.deletedAt = datetime.now()
|
||||
session.commit()
|
||||
|
||||
result = ProposalResult("DISABLED", proposal)
|
||||
await ProposalStorage.put(result)
|
||||
|
||||
return {}
|
||||
|
||||
@mutation.field("rateProposal")
|
||||
@login_required
|
||||
async def rate_proposal(_, info, id, value):
|
||||
auth = info.context["request"].auth
|
||||
user_id = auth.user_id
|
||||
|
||||
with local_session() as session:
|
||||
proposal = session.query(Proposal).filter(Proposal.id == id).first()
|
||||
if not proposal:
|
||||
return {"error": "invalid proposal id"}
|
||||
|
||||
rating = session.query(ProposalRating).\
|
||||
filter(ProposalRating.proposal_id == id and ProposalRating.createdBy == user_id).first()
|
||||
if rating:
|
||||
rating.value = value
|
||||
session.commit()
|
||||
|
||||
if not rating:
|
||||
ProposalRating.create(
|
||||
proposal_id = id,
|
||||
createdBy = user_id,
|
||||
value = value)
|
||||
|
||||
result = ProposalResult("UPDATED_RATING", proposal)
|
||||
await ProposalStorage.put(result)
|
||||
|
||||
return {}
|
||||
|
||||
|
||||
@mutation.field("acceptProposal")
|
||||
@login_required
|
||||
async def accept_proposal(_, info, id):
|
||||
auth = info.context["request"].auth
|
||||
user_id = auth.user_id
|
||||
|
||||
with local_session() as session:
|
||||
proposal = session.query(Proposal).filter(Proposal.id == id).first()
|
||||
shout = session.query(Shout).filter(Shout.slug == proposal.shout).first()
|
||||
authors = [author.id for author in shout.authors]
|
||||
if not proposal:
|
||||
return {"error": "invalid proposal id"}
|
||||
if user_id not in authors:
|
||||
return {"error": "access denied"}
|
||||
|
||||
proposal.acceptedAt = datetime.now()
|
||||
proposal.acceptedBy = user_id
|
||||
session.commit()
|
||||
|
||||
result = ProposalResult("ACCEPTED", proposal)
|
||||
await ProposalStorage.put(result)
|
||||
|
||||
return {}
|
||||
|
||||
@mutation.field("declineProposal")
|
||||
@login_required
|
||||
async def decline_proposal(_, info, id):
|
||||
auth = info.context["request"].auth
|
||||
user_id = auth.user_id
|
||||
|
||||
with local_session() as session:
|
||||
proposal = session.query(Proposal).filter(Proposal.id == id).first()
|
||||
shout = session.query(Shout).filter(Shout.slug == proposal.shout).first()
|
||||
authors = [author.id for author in shout.authors]
|
||||
if not proposal:
|
||||
return {"error": "invalid proposal id"}
|
||||
if user_id not in authors:
|
||||
return {"error": "access denied"}
|
||||
|
||||
proposal.acceptedAt = datetime.now()
|
||||
proposal.acceptedBy = user_id
|
||||
session.commit()
|
||||
|
||||
result = ProposalResult("DECLINED", proposal)
|
||||
await ProposalStorage.put(result)
|
||||
|
||||
return {}
|
||||
|
||||
|
||||
@mutation.field("inviteAuthor")
|
||||
@login_required
|
||||
@@ -234,11 +22,10 @@ async def invite_author(_, info, author, shout):
|
||||
if author.id in authors:
|
||||
return {"error": "already added"}
|
||||
shout.authors.append(author)
|
||||
shout.updated_at = datetime.now()
|
||||
shout.save()
|
||||
session.commit()
|
||||
|
||||
# result = Result("INVITED")
|
||||
# FIXME: await ShoutStorage.put(result)
|
||||
|
||||
# TODO: email notify
|
||||
|
||||
return {}
|
||||
@@ -260,6 +47,8 @@ async def remove_author(_, info, author, shout):
|
||||
if author.id not in authors:
|
||||
return {"error": "not in authors"}
|
||||
shout.authors.remove(author)
|
||||
shout.updated_at = datetime.now()
|
||||
shout.save()
|
||||
session.commit()
|
||||
|
||||
# result = Result("INVITED")
|
||||
|
@@ -1,136 +0,0 @@
|
||||
from orm import Comment, CommentRating
|
||||
from orm.base import local_session
|
||||
from orm.shout import ShoutCommentsSubscription
|
||||
from orm.user import User
|
||||
from resolvers.base import mutation, query
|
||||
from auth.authenticate import login_required
|
||||
from datetime import datetime
|
||||
|
||||
def comments_subscribe(user, slug, auto = False):
|
||||
with local_session() as session:
|
||||
sub = session.query(ShoutCommentsSubscription).\
|
||||
filter(ShoutCommentsSubscription.subscriber == user.slug, ShoutCommentsSubscription.shout == slug).\
|
||||
first()
|
||||
if auto and sub:
|
||||
return
|
||||
elif not auto and sub:
|
||||
if not sub.deletedAt is None:
|
||||
sub.deletedAt = None
|
||||
sub.auto = False
|
||||
session.commit()
|
||||
return
|
||||
raise Exception("subscription already exist")
|
||||
|
||||
ShoutCommentsSubscription.create(
|
||||
subscriber = user.slug,
|
||||
shout = slug,
|
||||
auto = auto)
|
||||
|
||||
def comments_unsubscribe(user, slug):
|
||||
with local_session() as session:
|
||||
sub = session.query(ShoutCommentsSubscription).\
|
||||
filter(ShoutCommentsSubscription.subscriber == user.slug, ShoutCommentsSubscription.shout == slug).\
|
||||
first()
|
||||
if not sub:
|
||||
raise Exception("subscription not exist")
|
||||
if sub.auto:
|
||||
sub.deletedAt = datetime.now()
|
||||
else:
|
||||
session.delete(sub)
|
||||
session.commit()
|
||||
|
||||
@mutation.field("createComment")
|
||||
@login_required
|
||||
async def create_comment(_, info, body, shout, replyTo = None):
|
||||
user = info.context["request"].user
|
||||
|
||||
comment = Comment.create(
|
||||
createdBy = user.slug,
|
||||
body = body,
|
||||
shout = shout,
|
||||
replyTo = replyTo
|
||||
)
|
||||
|
||||
try:
|
||||
comments_subscribe(user, shout, True)
|
||||
except Exception as e:
|
||||
print(f"error on comment autosubscribe: {e}")
|
||||
|
||||
return {"comment": comment}
|
||||
|
||||
@mutation.field("updateComment")
|
||||
@login_required
|
||||
async def update_comment(_, info, id, body):
|
||||
auth = info.context["request"].auth
|
||||
user_id = auth.user_id
|
||||
|
||||
with local_session() as session:
|
||||
comment = session.query(Comment).filter(Comment.id == id).first()
|
||||
if not comment:
|
||||
return {"error": "invalid comment id"}
|
||||
if comment.createdBy != user_id:
|
||||
return {"error": "access denied"}
|
||||
|
||||
comment.body = body
|
||||
comment.updatedAt = datetime.now()
|
||||
|
||||
session.commit()
|
||||
|
||||
return {"comment": comment}
|
||||
|
||||
@mutation.field("deleteComment")
|
||||
@login_required
|
||||
async def delete_comment(_, info, id):
|
||||
auth = info.context["request"].auth
|
||||
user_id = auth.user_id
|
||||
|
||||
with local_session() as session:
|
||||
comment = session.query(Comment).filter(Comment.id == id).first()
|
||||
if not comment:
|
||||
return {"error": "invalid comment id"}
|
||||
if comment.createdBy != user_id:
|
||||
return {"error": "access denied"}
|
||||
|
||||
comment.deletedAt = datetime.now()
|
||||
session.commit()
|
||||
|
||||
return {}
|
||||
|
||||
@mutation.field("rateComment")
|
||||
@login_required
|
||||
async def rate_comment(_, info, id, value):
|
||||
auth = info.context["request"].auth
|
||||
user_id = auth.user_id
|
||||
|
||||
with local_session() as session:
|
||||
user = session.query(User).filter(User.id == user_id).first()
|
||||
comment = session.query(Comment).filter(Comment.id == id).first()
|
||||
if not comment:
|
||||
return {"error": "invalid comment id"}
|
||||
rating = session.query(CommentRating).\
|
||||
filter(CommentRating.comment_id == id, CommentRating.createdBy == user.slug).first()
|
||||
if rating:
|
||||
rating.value = value
|
||||
session.commit()
|
||||
|
||||
if not rating:
|
||||
CommentRating.create(
|
||||
comment_id = id,
|
||||
createdBy = user_id,
|
||||
value = value)
|
||||
|
||||
return {}
|
||||
|
||||
def get_subscribed_shout_comments(slug):
|
||||
with local_session() as session:
|
||||
rows = session.query(ShoutCommentsSubscription.shout).\
|
||||
filter(ShoutCommentsSubscription.subscriber == slug,\
|
||||
ShoutCommentsSubscription.deletedAt == None).\
|
||||
all()
|
||||
slugs = [row.shout for row in rows]
|
||||
return slugs
|
||||
|
||||
@query.field("commentsAll")
|
||||
def get_top10_comments(_, info, page = 1, size = 10):
|
||||
with local_session() as session:
|
||||
rows = session.query(Comment).limit(size).all()
|
@@ -1,5 +1,6 @@
|
||||
from orm import Community, CommunitySubscription
|
||||
from orm.community import Community, CommunityFollower
|
||||
from orm.base import local_session
|
||||
from orm.user import User
|
||||
from resolvers.base import mutation, query
|
||||
from auth.authenticate import login_required
|
||||
from datetime import datetime
|
||||
@@ -26,12 +27,15 @@ async def create_community(_, info, input):
|
||||
async def update_community(_, info, input):
|
||||
auth = info.context["request"].auth
|
||||
user_id = auth.user_id
|
||||
community_slug = input.get('slug', '')
|
||||
|
||||
with local_session() as session:
|
||||
community = session.query(Community).filter(Community.slug == input.get('slug', '')).first()
|
||||
owner = session.query(User).filter(User.id == user_id) # note list here
|
||||
community = session.query(Community).filter(Community.slug == community_slug).first()
|
||||
editors = [e.slug for e in community.editors]
|
||||
if not community:
|
||||
return {"error": "invalid community id"}
|
||||
if community.createdBy != user_id:
|
||||
if community.createdBy not in (owner + editors):
|
||||
return {"error": "access denied"}
|
||||
community.title = input.get('title', '')
|
||||
community.desc = input.get('desc', '')
|
||||
@@ -71,27 +75,28 @@ async def get_communities(_, info):
|
||||
communities = session.query(Community)
|
||||
return communities
|
||||
|
||||
def community_subscribe(user, slug):
|
||||
CommunitySubscription.create(
|
||||
subscriber = user.slug,
|
||||
def community_follow(user, slug):
|
||||
CommunityFollower.create(
|
||||
follower = user.slug,
|
||||
community = slug
|
||||
)
|
||||
|
||||
def community_unsubscribe(user, slug):
|
||||
def community_unfollow(user, slug):
|
||||
with local_session() as session:
|
||||
sub = session.query(CommunitySubscription).\
|
||||
filter(and_(CommunitySubscription.subscriber == user.slug, CommunitySubscription.community == slug)).\
|
||||
following = session.query(CommunityFollower).\
|
||||
filter(and_(CommunityFollower.follower == user.slug, CommunityFollower.community == slug)).\
|
||||
first()
|
||||
if not sub:
|
||||
raise Exception("subscription not exist")
|
||||
session.delete(sub)
|
||||
if not following:
|
||||
raise Exception("[orm.community] following was not exist")
|
||||
session.delete(following)
|
||||
session.commit()
|
||||
|
||||
def get_subscribed_communities(user_slug):
|
||||
@query.field("userFollowedCommunities")
|
||||
def get_followed_communities(_, user_slug) -> list[Community]:
|
||||
ccc = []
|
||||
with local_session() as session:
|
||||
rows = session.query(Community.slug).\
|
||||
join(CommunitySubscription).\
|
||||
where(CommunitySubscription.subscriber == user_slug).\
|
||||
ccc = session.query(Community.slug).\
|
||||
join(CommunityFollower).\
|
||||
where(CommunityFollower.follower == user_slug).\
|
||||
all()
|
||||
slugs = [row.slug for row in rows]
|
||||
return slugs
|
||||
return ccc
|
||||
|
@@ -4,11 +4,10 @@ from orm.rbac import Resource
|
||||
from orm.shout import ShoutAuthor, ShoutTopic
|
||||
from orm.user import User
|
||||
from resolvers.base import mutation
|
||||
from resolvers.comments import comments_subscribe
|
||||
from resolvers.reactions import reactions_follow, reactions_unfollow
|
||||
from auth.authenticate import login_required
|
||||
from datetime import datetime
|
||||
|
||||
from resolvers.zine import GitTask
|
||||
from storages.gittask import GitTask
|
||||
|
||||
|
||||
@mutation.field("createShout")
|
||||
@@ -26,7 +25,7 @@ async def create_shout(_, info, input):
|
||||
user = user.slug
|
||||
)
|
||||
|
||||
comments_subscribe(user, new_shout.slug, True)
|
||||
reactions_follow(user, new_shout.slug, True)
|
||||
|
||||
if "mainTopic" in input:
|
||||
topic_slugs.append(input["mainTopic"])
|
||||
@@ -110,8 +109,10 @@ async def delete_shout(_, info, slug):
|
||||
return {"error": "invalid shout slug"}
|
||||
if user_id not in authors:
|
||||
return {"error": "access denied"}
|
||||
|
||||
for a in authors:
|
||||
reactions_unfollow(a.slug, slug, True)
|
||||
shout.deletedAt = datetime.now()
|
||||
session.commit()
|
||||
|
||||
|
||||
return {}
|
||||
|
41
resolvers/feed.py
Normal file
41
resolvers/feed.py
Normal file
@@ -0,0 +1,41 @@
|
||||
from auth.authenticate import login_required
|
||||
from orm.base import local_session
|
||||
from sqlalchemy import and_, desc, query
|
||||
from orm.reaction import Reaction
|
||||
from orm.shout import Shout, ShoutAuthor, ShoutTopic
|
||||
from orm.topic import TopicFollower
|
||||
from orm.user import AuthorFollower
|
||||
|
||||
@query.field("shoutsForFeed")
|
||||
@login_required
|
||||
def get_user_feed(_, info, page, size) -> list[Shout]:
|
||||
user = info.context["request"].user
|
||||
shouts = []
|
||||
with local_session() as session:
|
||||
shouts = session.query(Shout).\
|
||||
join(ShoutAuthor).\
|
||||
join(AuthorFollower).\
|
||||
where(AuthorFollower.follower == user.slug).\
|
||||
order_by(desc(Shout.createdAt))
|
||||
topicrows = session.query(Shout).\
|
||||
join(ShoutTopic).\
|
||||
join(TopicFollower).\
|
||||
where(TopicFollower.follower == user.slug).\
|
||||
order_by(desc(Shout.createdAt))
|
||||
shouts = shouts.union(topicrows).limit(size).offset(page * size).all()
|
||||
return shouts
|
||||
|
||||
@query.field("myCandidates")
|
||||
@login_required
|
||||
async def user_unpublished_shouts(_, info, page = 1, size = 10) -> list[Shout]:
|
||||
user = info.context["request"].user
|
||||
shouts = []
|
||||
with local_session() as session:
|
||||
shouts = session.query(Shout).\
|
||||
join(ShoutAuthor).\
|
||||
where(and_(Shout.publishedAt == None, ShoutAuthor.user == user.slug)).\
|
||||
order_by(desc(Shout.createdAt)).\
|
||||
limit(size).\
|
||||
offset( page * size).\
|
||||
all()
|
||||
return shouts
|
@@ -1,224 +1,154 @@
|
||||
from orm import User, UserRole, Role, UserRating
|
||||
from orm.user import AuthorSubscription, UserStorage
|
||||
from orm.comment import Comment
|
||||
from orm.user import User, UserRole, Role, UserRating, AuthorFollower
|
||||
from storages.users import UserStorage
|
||||
from orm.shout import Shout
|
||||
from orm.reaction import Reaction
|
||||
from orm.base import local_session
|
||||
from orm.topic import Topic, TopicSubscription
|
||||
from resolvers.base import mutation, query, subscription
|
||||
from resolvers.community import get_subscribed_communities
|
||||
from resolvers.comments import get_subscribed_shout_comments
|
||||
from orm.topic import Topic, TopicFollower
|
||||
from resolvers.base import mutation, query
|
||||
from resolvers.community import get_followed_communities
|
||||
from resolvers.reactions import get_shout_reactions
|
||||
from auth.authenticate import login_required
|
||||
|
||||
from inbox_resolvers.inbox import get_total_unread_messages_for_user
|
||||
|
||||
from sqlalchemy import func, and_, desc
|
||||
from inbox_resolvers.inbox import get_inbox_counter
|
||||
from sqlalchemy import and_, desc
|
||||
from sqlalchemy.orm import selectinload
|
||||
import asyncio
|
||||
|
||||
def _get_user_subscribed_topic_slugs(slug):
|
||||
with local_session() as session:
|
||||
rows = session.query(Topic.slug).\
|
||||
join(TopicSubscription).\
|
||||
where(TopicSubscription.subscriber == slug).\
|
||||
all()
|
||||
slugs = [row.slug for row in rows]
|
||||
return slugs
|
||||
|
||||
def _get_user_subscribed_authors(slug):
|
||||
with local_session() as session:
|
||||
authors = session.query(User.slug).\
|
||||
join(AuthorSubscription, User.slug == AuthorSubscription.author).\
|
||||
where(AuthorSubscription.subscriber == slug)
|
||||
return authors
|
||||
@query.field("userReactedShouts")
|
||||
async def get_user_reacted_shouts(_, info, slug, page, size) -> list[Shout]:
|
||||
user = await UserStorage.get_user_by_slug(slug)
|
||||
if not user: return {}
|
||||
with local_session() as session:
|
||||
shouts = session.query(Shout).\
|
||||
join(Reaction).\
|
||||
where(Reaction.createdBy == user.slug).\
|
||||
order_by(desc(Reaction.createdAt)).\
|
||||
limit(size).\
|
||||
offset(page * size).all()
|
||||
return shouts
|
||||
|
||||
|
||||
@query.field("userFollowedTopics")
|
||||
@login_required
|
||||
def get_followed_topics(_, slug) -> list[Topic]:
|
||||
rows = []
|
||||
with local_session() as session:
|
||||
rows = session.query(Topic).\
|
||||
join(TopicFollower).\
|
||||
where(TopicFollower.follower == slug).\
|
||||
all()
|
||||
return rows
|
||||
|
||||
|
||||
@query.field("userFollowedAuthors")
|
||||
def get_followed_authors(_, slug) -> list[User]:
|
||||
authors = []
|
||||
with local_session() as session:
|
||||
authors = session.query(User).\
|
||||
join(AuthorFollower, User.slug == AuthorFollower.author).\
|
||||
where(AuthorFollower.follower == slug).\
|
||||
all()
|
||||
return authors
|
||||
|
||||
|
||||
@query.field("userFollowers")
|
||||
async def user_followers(_, slug) -> list[User]:
|
||||
with local_session() as session:
|
||||
users = session.query(User).\
|
||||
join(AuthorFollower, User.slug == AuthorFollower.follower).\
|
||||
where(AuthorFollower.author == slug).\
|
||||
all()
|
||||
return users
|
||||
|
||||
# for query.field("getCurrentUser")
|
||||
async def get_user_info(slug):
|
||||
return {
|
||||
"totalUnreadMessages" : await get_total_unread_messages_for_user(slug),
|
||||
"userSubscribedTopics" : _get_user_subscribed_topic_slugs(slug),
|
||||
"userSubscribedAuthors" : _get_user_subscribed_authors(slug),
|
||||
"userSubscribedCommunities" : get_subscribed_communities(slug),
|
||||
"userSubscribedShoutComments": get_subscribed_shout_comments(slug)
|
||||
}
|
||||
return {
|
||||
"inbox": await get_inbox_counter(slug),
|
||||
"topics": [t.slug for t in get_followed_topics(0, slug)],
|
||||
"authors": [a.slug for a in get_followed_authors(0, slug)],
|
||||
"reactions": [r.shout for r in get_shout_reactions(0, slug)],
|
||||
"communities": [c.slug for c in get_followed_communities(0, slug)]
|
||||
}
|
||||
|
||||
|
||||
@query.field("getCurrentUser")
|
||||
@login_required
|
||||
async def get_current_user(_, info):
|
||||
user = info.context["request"].user
|
||||
return {
|
||||
"user": user,
|
||||
"info": await get_user_info(user.slug)
|
||||
}
|
||||
user = info.context["request"].user
|
||||
return {
|
||||
"user": user,
|
||||
"info": await get_user_info(user.slug)
|
||||
}
|
||||
|
||||
|
||||
@query.field("getUsersBySlugs")
|
||||
async def get_users_by_slugs(_, info, slugs):
|
||||
with local_session() as session:
|
||||
users = session.query(User).\
|
||||
options(selectinload(User.ratings)).\
|
||||
filter(User.slug.in_(slugs)).all()
|
||||
return users
|
||||
with local_session() as session:
|
||||
users = session.query(User).\
|
||||
options(selectinload(User.ratings)).\
|
||||
filter(User.slug.in_(slugs)).all()
|
||||
return users
|
||||
|
||||
|
||||
@query.field("getUserRoles")
|
||||
async def get_user_roles(_, info, slug):
|
||||
with local_session() as session:
|
||||
user = session.query(User).where(User.slug == slug).first()
|
||||
roles = session.query(Role).\
|
||||
options(selectinload(Role.permissions)).\
|
||||
join(UserRole).\
|
||||
where(UserRole.user_id == user.id).all()
|
||||
return roles
|
||||
|
||||
with local_session() as session:
|
||||
user = session.query(User).where(User.slug == slug).first()
|
||||
|
||||
roles = session.query(Role).\
|
||||
options(selectinload(Role.permissions)).\
|
||||
join(UserRole).\
|
||||
where(UserRole.user_id == user.id).all()
|
||||
|
||||
return roles
|
||||
|
||||
@mutation.field("updateProfile")
|
||||
@login_required
|
||||
async def update_profile(_, info, profile):
|
||||
auth = info.context["request"].auth
|
||||
user_id = auth.user_id
|
||||
auth = info.context["request"].auth
|
||||
user_id = auth.user_id
|
||||
with local_session() as session:
|
||||
user = session.query(User).filter(User.id == user_id).first()
|
||||
user.update(profile)
|
||||
session.commit()
|
||||
return {}
|
||||
|
||||
with local_session() as session:
|
||||
user = session.query(User).filter(User.id == user_id).first()
|
||||
user.update(profile)
|
||||
session.commit()
|
||||
|
||||
return {}
|
||||
|
||||
@query.field("userComments")
|
||||
async def user_comments(_, info, slug, page, size):
|
||||
user = await UserStorage.get_user_by_slug(slug)
|
||||
if not user:
|
||||
return
|
||||
|
||||
page = page - 1
|
||||
with local_session() as session:
|
||||
comments = session.query(Comment).\
|
||||
filter(Comment.createdBy == user.id).\
|
||||
order_by(desc(Comment.createdAt)).\
|
||||
limit(size).\
|
||||
offset(page * size)
|
||||
|
||||
return comments
|
||||
|
||||
@query.field("userSubscribedAuthors")
|
||||
async def user_subscriptions(_, info, slug):
|
||||
slugs = _get_user_subscribed_authors(slug)
|
||||
return slugs
|
||||
|
||||
@query.field("userSubscribers")
|
||||
async def user_subscribers(_, info, slug):
|
||||
with local_session() as session:
|
||||
slugs = session.query(User.slug).\
|
||||
join(AuthorSubscription, User.slug == AuthorSubscription.subscriber).\
|
||||
where(AuthorSubscription.author == slug)
|
||||
return slugs
|
||||
|
||||
@query.field("userSubscribedTopics")
|
||||
async def user_subscribed_topics(_, info, slug):
|
||||
return _get_user_subscribed_topic_slugs(slug)
|
||||
|
||||
@mutation.field("rateUser")
|
||||
@login_required
|
||||
async def rate_user(_, info, slug, value):
|
||||
user = info.context["request"].user
|
||||
user = info.context["request"].user
|
||||
with local_session() as session:
|
||||
rating = session.query(UserRating).\
|
||||
filter(and_(UserRating.rater == user.slug, UserRating.user == slug)).\
|
||||
first()
|
||||
if rating:
|
||||
rating.value = value
|
||||
session.commit()
|
||||
return {}
|
||||
try:
|
||||
UserRating.create(
|
||||
rater=user.slug,
|
||||
user=slug,
|
||||
value=value
|
||||
)
|
||||
except Exception as err:
|
||||
return {"error": err}
|
||||
return {}
|
||||
|
||||
with local_session() as session:
|
||||
rating = session.query(UserRating).\
|
||||
filter(and_(UserRating.rater == user.slug, UserRating.user == slug)).\
|
||||
first()
|
||||
|
||||
if rating:
|
||||
rating.value = value
|
||||
session.commit()
|
||||
return {}
|
||||
|
||||
UserRating.create(
|
||||
rater = user.slug,
|
||||
user = slug,
|
||||
value = value
|
||||
)
|
||||
|
||||
return {}
|
||||
|
||||
|
||||
def author_subscribe(user, slug):
|
||||
AuthorSubscription.create(
|
||||
subscriber = user.slug,
|
||||
author = slug
|
||||
)
|
||||
|
||||
def author_unsubscribe(user, slug):
|
||||
with local_session() as session:
|
||||
sub = session.query(AuthorSubscription).\
|
||||
filter(and_(AuthorSubscription.subscriber == user.slug, AuthorSubscription.author == slug)).\
|
||||
first()
|
||||
if not sub:
|
||||
raise Exception("subscription not exist")
|
||||
session.delete(sub)
|
||||
session.commit()
|
||||
|
||||
@query.field("shoutsRatedByUser")
|
||||
@login_required
|
||||
async def shouts_rated_by_user(_, info, page, size):
|
||||
user = info.context["request"].user
|
||||
|
||||
with local_session() as session:
|
||||
shouts = session.query(Shout).\
|
||||
join(ShoutRating).\
|
||||
where(ShoutRating.rater == user.slug).\
|
||||
order_by(desc(ShoutRating.ts)).\
|
||||
limit(size).\
|
||||
offset( (page - 1) * size)
|
||||
|
||||
return {
|
||||
"shouts" : shouts
|
||||
}
|
||||
|
||||
@query.field("userUnpublishedShouts")
|
||||
@login_required
|
||||
async def user_unpublished_shouts(_, info, page, size):
|
||||
user = info.context["request"].user
|
||||
|
||||
with local_session() as session:
|
||||
shouts = session.query(Shout).\
|
||||
join(ShoutAuthor).\
|
||||
where(and_(Shout.publishedAt == None, ShoutAuthor.user == user.slug)).\
|
||||
order_by(desc(Shout.createdAt)).\
|
||||
limit(size).\
|
||||
offset( (page - 1) * size)
|
||||
|
||||
return {
|
||||
"shouts" : shouts
|
||||
}
|
||||
|
||||
@query.field("shoutsReviewed")
|
||||
@login_required
|
||||
async def shouts_reviewed(_, info, page, size):
|
||||
user = info.context["request"].user
|
||||
with local_session() as session:
|
||||
shouts_by_rating = session.query(Shout).\
|
||||
join(ShoutRating).\
|
||||
where(and_(Shout.publishedAt != None, ShoutRating.rater == user.slug))
|
||||
shouts_by_comment = session.query(Shout).\
|
||||
join(Comment).\
|
||||
where(and_(Shout.publishedAt != None, Comment.createdBy == user.id))
|
||||
shouts = shouts_by_rating.union(shouts_by_comment).\
|
||||
order_by(desc(Shout.publishedAt)).\
|
||||
limit(size).\
|
||||
offset( (page - 1) * size)
|
||||
|
||||
return shouts
|
||||
|
||||
@query.field("shoutsCommentedByUser")
|
||||
async def shouts_commented_by_user(_, info, slug, page, size):
|
||||
user = await UserStorage.get_user_by_slug(slug)
|
||||
if not user:
|
||||
return {}
|
||||
|
||||
with local_session() as session:
|
||||
shouts = session.query(Shout).\
|
||||
join(Comment).\
|
||||
where(Comment.createdBy == user.id).\
|
||||
order_by(desc(Comment.createdAt)).\
|
||||
limit(size).\
|
||||
offset( (page - 1) * size)
|
||||
return shouts
|
||||
# for mutation.field("follow")
|
||||
def author_follow(user, slug):
|
||||
AuthorFollower.create(
|
||||
follower=user.slug,
|
||||
author=slug
|
||||
)
|
||||
|
||||
# for mutation.field("unfollow")
|
||||
def author_unfollow(user, slug):
|
||||
with local_session() as session:
|
||||
flw = session.query(AuthorFollower).\
|
||||
filter(and_(AuthorFollower.follower == user.slug, AuthorFollower.author == slug)).\
|
||||
first()
|
||||
if not flw:
|
||||
raise Exception("[resolvers.profile] follower not exist, cant unfollow")
|
||||
else:
|
||||
session.delete(flw)
|
||||
session.commit()
|
||||
|
150
resolvers/reactions.py
Normal file
150
resolvers/reactions.py
Normal file
@@ -0,0 +1,150 @@
|
||||
from sqlalchemy import and_
|
||||
from sqlalchemy.orm import selectinload, joinedload
|
||||
from orm.reaction import Reaction
|
||||
from orm.base import local_session
|
||||
from orm.shout import Shout, ShoutReactionsFollower
|
||||
from orm.user import User
|
||||
from resolvers.base import mutation, query
|
||||
from auth.authenticate import login_required
|
||||
from datetime import datetime
|
||||
from storages.reactions import ReactionsStorage
|
||||
from storages.viewed import ViewedStorage
|
||||
|
||||
|
||||
def reactions_follow(user, slug, auto=False):
|
||||
with local_session() as session:
|
||||
fw = session.query(ShoutReactionsFollower).\
|
||||
filter(ShoutReactionsFollower.follower == user.slug, ShoutReactionsFollower.shout == slug).\
|
||||
first()
|
||||
if auto and fw:
|
||||
return
|
||||
elif not auto and fw:
|
||||
if not fw.deletedAt is None:
|
||||
fw.deletedAt = None
|
||||
fw.auto = False
|
||||
session.commit()
|
||||
return
|
||||
# print("[resolvers.reactions] was followed before")
|
||||
|
||||
ShoutReactionsFollower.create(
|
||||
follower=user.slug,
|
||||
shout=slug,
|
||||
auto=auto)
|
||||
|
||||
|
||||
def reactions_unfollow(user, slug):
|
||||
with local_session() as session:
|
||||
following = session.query(ShoutReactionsFollower).\
|
||||
filter(ShoutReactionsFollower.follower == user.slug, ShoutReactionsFollower.shout == slug).\
|
||||
first()
|
||||
if not following:
|
||||
# print("[resolvers.reactions] was not followed", slug)
|
||||
return
|
||||
if following.auto:
|
||||
following.deletedAt = datetime.now()
|
||||
else:
|
||||
session.delete(following)
|
||||
session.commit()
|
||||
|
||||
|
||||
@mutation.field("createReaction")
|
||||
@login_required
|
||||
async def create_reaction(_, info, inp):
|
||||
user = info.context["request"].user
|
||||
|
||||
reaction = Reaction.create(**inp)
|
||||
|
||||
try:
|
||||
reactions_follow(user, inp['shout'], True)
|
||||
except Exception as e:
|
||||
print(f"[resolvers.reactions] error on reactions autofollowing: {e}")
|
||||
|
||||
return {"reaction": reaction}
|
||||
|
||||
|
||||
@mutation.field("updateReaction")
|
||||
@login_required
|
||||
async def update_reaction(_, info, inp):
|
||||
auth = info.context["request"].auth
|
||||
user_id = auth.user_id
|
||||
|
||||
with local_session() as session:
|
||||
user = session.query(User).filter(User.id == user_id).first()
|
||||
reaction = session.query(Reaction).filter(Reaction.id == id).first()
|
||||
if not reaction:
|
||||
return {"error": "invalid reaction id"}
|
||||
if reaction.createdBy != user.slug:
|
||||
return {"error": "access denied"}
|
||||
reaction.body = inp['body']
|
||||
reaction.updatedAt = datetime.now()
|
||||
if reaction.kind != inp['kind']:
|
||||
# TODO: change mind detection
|
||||
pass
|
||||
if inp.get('range'):
|
||||
reaction.range = inp.get('range')
|
||||
session.commit()
|
||||
|
||||
return {"reaction": reaction}
|
||||
|
||||
|
||||
@mutation.field("deleteReaction")
|
||||
@login_required
|
||||
async def delete_reaction(_, info, id):
|
||||
auth = info.context["request"].auth
|
||||
user_id = auth.user_id
|
||||
with local_session() as session:
|
||||
user = session.query(User).filter(User.id == user_id).first()
|
||||
reaction = session.query(Reaction).filter(Reaction.id == id).first()
|
||||
if not reaction:
|
||||
return {"error": "invalid reaction id"}
|
||||
if reaction.createdBy != user.slug:
|
||||
return {"error": "access denied"}
|
||||
reaction.deletedAt = datetime.now()
|
||||
session.commit()
|
||||
return {}
|
||||
|
||||
@query.field("reactionsByShout")
|
||||
def get_shout_reactions(_, info, slug) -> list[Shout]:
|
||||
shouts = []
|
||||
with local_session() as session:
|
||||
shoutslugs = session.query(ShoutReactionsFollower.shout).\
|
||||
join(User).where(Reaction.createdBy == User.slug).\
|
||||
filter(ShoutReactionsFollower.follower == slug,
|
||||
ShoutReactionsFollower.deletedAt == None).all()
|
||||
shoutslugs = list(set(shoutslugs))
|
||||
shouts = session.query(Shout).filter(Shout.slug in shoutslugs).all()
|
||||
return shouts
|
||||
|
||||
|
||||
@query.field("reactionsAll")
|
||||
def get_all_reactions(_, info, page=1, size=10) -> list[Reaction]:
|
||||
reactions = []
|
||||
with local_session() as session:
|
||||
q = session.query(Reaction).\
|
||||
options(
|
||||
joinedload(User),
|
||||
joinedload(Shout)
|
||||
).\
|
||||
join( User, Reaction.createdBy == User.slug ).\
|
||||
join( Shout, Reaction.shout == Shout.slug ).\
|
||||
filter( Reaction.deletedAt == None ).\
|
||||
limit(size).offset(page * size).all()
|
||||
# print(reactions[0].dict())
|
||||
return reactions
|
||||
|
||||
|
||||
@query.field("reactionsByAuthor")
|
||||
def get_reactions_by_author(_, info, slug, page=1, size=50) -> list[Reaction]:
|
||||
reactions = []
|
||||
with local_session() as session:
|
||||
reactions = session.query(Reaction).\
|
||||
join(Shout).where(Reaction.shout == Shout.slug).\
|
||||
filter(Reaction.deletedAt == None, Reaction.createdBy == slug).\
|
||||
limit(size).offset(page * size).all() # pagination
|
||||
return reactions
|
||||
|
||||
|
||||
@mutation.field("viewReaction")
|
||||
async def view_reaction(_, info, reaction):
|
||||
await ViewedStorage.inc_reaction(reaction)
|
||||
return {"error" : ""}
|
@@ -1,17 +1,16 @@
|
||||
from orm import Topic, TopicSubscription, TopicStorage, Shout, User
|
||||
from orm.shout import TopicStat, ShoutAuthorStorage
|
||||
from orm.user import UserStorage
|
||||
from orm.topic import Topic, TopicFollower
|
||||
from storages.topics import TopicStorage
|
||||
from orm.shout import Shout
|
||||
from orm.user import User
|
||||
from storages.topicstat import TopicStat
|
||||
from orm.base import local_session
|
||||
from resolvers.base import mutation, query
|
||||
from auth.authenticate import login_required
|
||||
import asyncio
|
||||
|
||||
from sqlalchemy import func, and_
|
||||
from sqlalchemy import and_
|
||||
|
||||
@query.field("topicsAll")
|
||||
async def topics_by_slugs(_, info, slugs = None):
|
||||
with local_session() as session:
|
||||
topics = await TopicStorage.get_topics(slugs)
|
||||
async def topics_by_slugs(_, info, page = 1, size = 50):
|
||||
topics = await TopicStorage.get_topics_all(page, size)
|
||||
all_fields = [node.name.value for node in info.field_nodes[0].selection_set.selections]
|
||||
if "stat" in all_fields:
|
||||
for topic in topics:
|
||||
@@ -20,8 +19,7 @@ async def topics_by_slugs(_, info, slugs = None):
|
||||
|
||||
@query.field("topicsByCommunity")
|
||||
async def topics_by_community(_, info, community):
|
||||
with local_session() as session:
|
||||
topics = await TopicStorage.get_topics_by_community(community)
|
||||
topics = await TopicStorage.get_topics_by_community(community)
|
||||
all_fields = [node.name.value for node in info.field_nodes[0].selection_set.selections]
|
||||
if "stat" in all_fields:
|
||||
for topic in topics:
|
||||
@@ -65,17 +63,17 @@ async def update_topic(_, info, input):
|
||||
|
||||
return { "topic" : topic }
|
||||
|
||||
def topic_subscribe(user, slug):
|
||||
TopicSubscription.create(
|
||||
subscriber = user.slug,
|
||||
def topic_follow(user, slug):
|
||||
TopicFollower.create(
|
||||
follower = user.slug,
|
||||
topic = slug)
|
||||
|
||||
def topic_unsubscribe(user, slug):
|
||||
def topic_unfollow(user, slug):
|
||||
with local_session() as session:
|
||||
sub = session.query(TopicSubscription).\
|
||||
filter(and_(TopicSubscription.subscriber == user.slug, TopicSubscription.topic == slug)).\
|
||||
sub = session.query(TopicFollower).\
|
||||
filter(and_(TopicFollower.follower == user.slug, TopicFollower.topic == slug)).\
|
||||
first()
|
||||
if not sub:
|
||||
raise Exception("subscription not exist")
|
||||
raise Exception("[resolvers.topics] follower not exist")
|
||||
session.delete(sub)
|
||||
session.commit()
|
||||
|
@@ -1,216 +1,18 @@
|
||||
from orm import Shout, ShoutAuthor, ShoutTopic, ShoutRating, ShoutViewByDay, \
|
||||
User, Community, Resource, ShoutRatingStorage, ShoutViewStorage, \
|
||||
Comment, CommentRating, Topic, ShoutCommentsSubscription
|
||||
from orm.community import CommunitySubscription
|
||||
from orm.shout import Shout, ShoutAuthor, ShoutTopic
|
||||
from orm.topic import Topic
|
||||
from orm.base import local_session
|
||||
from orm.user import UserStorage, AuthorSubscription
|
||||
from orm.topic import TopicSubscription
|
||||
|
||||
from resolvers.base import mutation, query
|
||||
from resolvers.profile import author_subscribe, author_unsubscribe
|
||||
from resolvers.topics import topic_subscribe, topic_unsubscribe
|
||||
from resolvers.community import community_subscribe, community_unsubscribe
|
||||
from resolvers.comments import comments_subscribe, comments_unsubscribe
|
||||
from storages.shoutscache import ShoutsCache
|
||||
from storages.viewed import ViewedStorage
|
||||
from resolvers.profile import author_follow, author_unfollow
|
||||
from resolvers.topics import topic_follow, topic_unfollow
|
||||
from resolvers.community import community_follow, community_unfollow
|
||||
from resolvers.reactions import reactions_follow, reactions_unfollow
|
||||
from auth.authenticate import login_required
|
||||
from settings import SHOUTS_REPO
|
||||
|
||||
import subprocess
|
||||
import asyncio
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from pathlib import Path
|
||||
from sqlalchemy import select, func, desc, and_
|
||||
from sqlalchemy import select, desc, and_
|
||||
from sqlalchemy.orm import selectinload
|
||||
|
||||
|
||||
class GitTask:
|
||||
|
||||
queue = asyncio.Queue()
|
||||
|
||||
def __init__(self, input, username, user_email, comment):
|
||||
self.slug = input["slug"]
|
||||
self.shout_body = input["body"]
|
||||
self.username = username
|
||||
self.user_email = user_email
|
||||
self.comment = comment
|
||||
|
||||
GitTask.queue.put_nowait(self)
|
||||
|
||||
def init_repo(self):
|
||||
repo_path = "%s" % (SHOUTS_REPO)
|
||||
|
||||
Path(repo_path).mkdir()
|
||||
|
||||
cmd = "cd %s && git init && " \
|
||||
"git config user.name 'discours' && " \
|
||||
"git config user.email 'discours@discours.io' && " \
|
||||
"touch initial && git add initial && " \
|
||||
"git commit -m 'init repo'" \
|
||||
% (repo_path)
|
||||
output = subprocess.check_output(cmd, shell=True)
|
||||
print(output)
|
||||
|
||||
def execute(self):
|
||||
repo_path = "%s" % (SHOUTS_REPO)
|
||||
|
||||
if not Path(repo_path).exists():
|
||||
self.init_repo()
|
||||
|
||||
#cmd = "cd %s && git checkout master" % (repo_path)
|
||||
#output = subprocess.check_output(cmd, shell=True)
|
||||
#print(output)
|
||||
|
||||
shout_filename = "%s.mdx" % (self.slug)
|
||||
shout_full_filename = "%s/%s" % (repo_path, shout_filename)
|
||||
with open(shout_full_filename, mode='w', encoding='utf-8') as shout_file:
|
||||
shout_file.write(bytes(self.shout_body,'utf-8').decode('utf-8','ignore'))
|
||||
|
||||
author = "%s <%s>" % (self.username, self.user_email)
|
||||
cmd = "cd %s && git add %s && git commit -m '%s' --author='%s'" % \
|
||||
(repo_path, shout_filename, self.comment, author)
|
||||
output = subprocess.check_output(cmd, shell=True)
|
||||
print(output)
|
||||
|
||||
@staticmethod
|
||||
async def git_task_worker():
|
||||
print("[git.task] worker start")
|
||||
while True:
|
||||
task = await GitTask.queue.get()
|
||||
try:
|
||||
task.execute()
|
||||
except Exception as err:
|
||||
print("[git.task] worker error = %s" % (err))
|
||||
|
||||
|
||||
class ShoutsCache:
|
||||
limit = 200
|
||||
period = 60*60 #1 hour
|
||||
lock = asyncio.Lock()
|
||||
|
||||
@staticmethod
|
||||
async def prepare_recent_published():
|
||||
with local_session() as session:
|
||||
stmt = select(Shout).\
|
||||
options(selectinload(Shout.authors), selectinload(Shout.topics)).\
|
||||
where(Shout.publishedAt != None).\
|
||||
order_by(desc("publishedAt")).\
|
||||
limit(ShoutsCache.limit)
|
||||
shouts = []
|
||||
for row in session.execute(stmt):
|
||||
shout = row.Shout
|
||||
shout.ratings = await ShoutRatingStorage.get_ratings(shout.slug)
|
||||
shouts.append(shout)
|
||||
async with ShoutsCache.lock:
|
||||
ShoutsCache.recent_published = shouts
|
||||
|
||||
@staticmethod
|
||||
async def prepare_recent_all():
|
||||
with local_session() as session:
|
||||
stmt = select(Shout).\
|
||||
options(selectinload(Shout.authors), selectinload(Shout.topics)).\
|
||||
order_by(desc("createdAt")).\
|
||||
limit(ShoutsCache.limit)
|
||||
shouts = []
|
||||
for row in session.execute(stmt):
|
||||
shout = row.Shout
|
||||
shout.ratings = await ShoutRatingStorage.get_ratings(shout.slug)
|
||||
shouts.append(shout)
|
||||
async with ShoutsCache.lock:
|
||||
ShoutsCache.recent_all = shouts
|
||||
|
||||
@staticmethod
|
||||
async def prepare_recent_commented():
|
||||
with local_session() as session:
|
||||
stmt = select(Shout, func.max(Comment.createdAt).label("commentCreatedAt")).\
|
||||
options(selectinload(Shout.authors), selectinload(Shout.topics)).\
|
||||
join(Comment).\
|
||||
where(and_(Shout.publishedAt != None, Comment.deletedAt == None)).\
|
||||
group_by(Shout.slug).\
|
||||
order_by(desc("commentCreatedAt")).\
|
||||
limit(ShoutsCache.limit)
|
||||
shouts = []
|
||||
for row in session.execute(stmt):
|
||||
shout = row.Shout
|
||||
shout.ratings = await ShoutRatingStorage.get_ratings(shout.slug)
|
||||
shouts.append(shout)
|
||||
async with ShoutsCache.lock:
|
||||
ShoutsCache.recent_commented = shouts
|
||||
|
||||
|
||||
@staticmethod
|
||||
async def prepare_top_overall():
|
||||
with local_session() as session:
|
||||
stmt = select(Shout, func.sum(ShoutRating.value).label("rating")).\
|
||||
options(selectinload(Shout.authors), selectinload(Shout.topics)).\
|
||||
join(ShoutRating).\
|
||||
where(Shout.publishedAt != None).\
|
||||
group_by(Shout.slug).\
|
||||
order_by(desc("rating")).\
|
||||
limit(ShoutsCache.limit)
|
||||
shouts = []
|
||||
for row in session.execute(stmt):
|
||||
shout = row.Shout
|
||||
shout.ratings = await ShoutRatingStorage.get_ratings(shout.slug)
|
||||
shouts.append(shout)
|
||||
async with ShoutsCache.lock:
|
||||
ShoutsCache.top_overall = shouts
|
||||
|
||||
@staticmethod
|
||||
async def prepare_top_month():
|
||||
month_ago = datetime.now() - timedelta(days = 30)
|
||||
with local_session() as session:
|
||||
stmt = select(Shout, func.sum(ShoutRating.value).label("rating")).\
|
||||
options(selectinload(Shout.authors), selectinload(Shout.topics)).\
|
||||
join(ShoutRating).\
|
||||
where(and_(Shout.createdAt > month_ago, Shout.publishedAt != None)).\
|
||||
group_by(Shout.slug).\
|
||||
order_by(desc("rating")).\
|
||||
limit(ShoutsCache.limit)
|
||||
shouts = []
|
||||
for row in session.execute(stmt):
|
||||
shout = row.Shout
|
||||
shout.ratings = await ShoutRatingStorage.get_ratings(shout.slug)
|
||||
shouts.append(shout)
|
||||
async with ShoutsCache.lock:
|
||||
ShoutsCache.top_month = shouts
|
||||
|
||||
@staticmethod
|
||||
async def prepare_top_viewed():
|
||||
month_ago = datetime.now() - timedelta(days = 30)
|
||||
with local_session() as session:
|
||||
stmt = select(Shout, func.sum(ShoutViewByDay.value).label("views")).\
|
||||
options(selectinload(Shout.authors), selectinload(Shout.topics)).\
|
||||
join(ShoutViewByDay).\
|
||||
where(and_(ShoutViewByDay.day > month_ago, Shout.publishedAt != None)).\
|
||||
group_by(Shout.slug).\
|
||||
order_by(desc("views")).\
|
||||
limit(ShoutsCache.limit)
|
||||
shouts = []
|
||||
for row in session.execute(stmt):
|
||||
shout = row.Shout
|
||||
shout.ratings = await ShoutRatingStorage.get_ratings(shout.slug)
|
||||
shout.views = row.views
|
||||
shouts.append(shout)
|
||||
async with ShoutsCache.lock:
|
||||
ShoutsCache.top_viewed = shouts
|
||||
|
||||
@staticmethod
|
||||
async def worker():
|
||||
print("[shouts.cache] worker start")
|
||||
while True:
|
||||
try:
|
||||
print("[shouts.cache] updating...")
|
||||
await ShoutsCache.prepare_top_month()
|
||||
await ShoutsCache.prepare_top_overall()
|
||||
await ShoutsCache.prepare_top_viewed()
|
||||
await ShoutsCache.prepare_recent_published()
|
||||
await ShoutsCache.prepare_recent_all()
|
||||
await ShoutsCache.prepare_recent_commented()
|
||||
print("[shouts.cache] update finished")
|
||||
except Exception as err:
|
||||
print("[shouts.cache] worker error: %s" % (err))
|
||||
await asyncio.sleep(ShoutsCache.period)
|
||||
|
||||
@query.field("topViewed")
|
||||
async def top_viewed(_, info, page, size):
|
||||
async with ShoutsCache.lock:
|
||||
@@ -236,20 +38,20 @@ async def recent_all(_, info, page, size):
|
||||
async with ShoutsCache.lock:
|
||||
return ShoutsCache.recent_all[(page - 1) * size : page * size]
|
||||
|
||||
@query.field("recentCommented")
|
||||
async def recent_commented(_, info, page, size):
|
||||
@query.field("recentReacted")
|
||||
async def recent_reacted(_, info, page, size):
|
||||
async with ShoutsCache.lock:
|
||||
return ShoutsCache.recent_commented[(page - 1) * size : page * size]
|
||||
return ShoutsCache.recent_reacted[(page - 1) * size : page * size]
|
||||
|
||||
@mutation.field("viewShout")
|
||||
async def view_shout(_, info, slug):
|
||||
await ShoutViewStorage.inc_view(slug)
|
||||
await ViewedStorage.inc_shout(slug)
|
||||
return {"error" : ""}
|
||||
|
||||
@query.field("getShoutBySlug")
|
||||
async def get_shout_by_slug(_, info, slug):
|
||||
all_fields = [node.name.value for node in info.field_nodes[0].selection_set.selections]
|
||||
selected_fields = set(["authors", "topics"]).intersection(all_fields)
|
||||
selected_fields = set(["authors", "topics", "reactions"]).intersection(all_fields)
|
||||
select_options = [selectinload(getattr(Shout, field)) for field in selected_fields]
|
||||
|
||||
with local_session() as session:
|
||||
@@ -258,23 +60,11 @@ async def get_shout_by_slug(_, info, slug):
|
||||
filter(Shout.slug == slug).first()
|
||||
|
||||
if not shout:
|
||||
print(f"shout with slug {slug} not exist")
|
||||
print(f"[resolvers.zine] error: shout with slug {slug} not exist")
|
||||
return {} #TODO return error field
|
||||
|
||||
shout.ratings = await ShoutRatingStorage.get_ratings(slug)
|
||||
|
||||
return shout
|
||||
|
||||
@query.field("getShoutComments")
|
||||
async def get_shout_comments(_, info, slug):
|
||||
with local_session() as session:
|
||||
comments = session.query(Comment).\
|
||||
options(selectinload(Comment.ratings)).\
|
||||
filter(Comment.shout == slug).\
|
||||
group_by(Comment.id).all()
|
||||
for comment in comments:
|
||||
comment.createdBy = await UserStorage.get_user(comment.createdBy)
|
||||
return comments
|
||||
|
||||
@query.field("shoutsByTopics")
|
||||
async def shouts_by_topics(_, info, slugs, page, size):
|
||||
page = page - 1
|
||||
@@ -316,65 +106,39 @@ async def shouts_by_communities(_, info, slugs, page, size):
|
||||
offset(page * size)
|
||||
return shouts
|
||||
|
||||
@mutation.field("subscribe")
|
||||
@mutation.field("follow")
|
||||
@login_required
|
||||
async def subscribe(_, info, what, slug):
|
||||
async def follow(_, info, what, slug):
|
||||
user = info.context["request"].user
|
||||
|
||||
try:
|
||||
if what == "AUTHOR":
|
||||
author_subscribe(user, slug)
|
||||
author_follow(user, slug)
|
||||
elif what == "TOPIC":
|
||||
topic_subscribe(user, slug)
|
||||
topic_follow(user, slug)
|
||||
elif what == "COMMUNITY":
|
||||
community_subscribe(user, slug)
|
||||
elif what == "COMMENTS":
|
||||
comments_subscribe(user, slug)
|
||||
community_follow(user, slug)
|
||||
elif what == "REACTIONS":
|
||||
reactions_follow(user, slug)
|
||||
except Exception as e:
|
||||
return {"error" : str(e)}
|
||||
|
||||
return {}
|
||||
|
||||
@mutation.field("unsubscribe")
|
||||
@mutation.field("unfollow")
|
||||
@login_required
|
||||
async def unsubscribe(_, info, what, slug):
|
||||
async def unfollow(_, info, what, slug):
|
||||
user = info.context["request"].user
|
||||
|
||||
try:
|
||||
if what == "AUTHOR":
|
||||
author_unsubscribe(user, slug)
|
||||
author_unfollow(user, slug)
|
||||
elif what == "TOPIC":
|
||||
topic_unsubscribe(user, slug)
|
||||
topic_unfollow(user, slug)
|
||||
elif what == "COMMUNITY":
|
||||
community_unsubscribe(user, slug)
|
||||
elif what == "COMMENTS":
|
||||
comments_unsubscribe(user, slug)
|
||||
community_unfollow(user, slug)
|
||||
elif what == "REACTIONS":
|
||||
reactions_unfollow(user, slug)
|
||||
except Exception as e:
|
||||
return {"error" : str(e)}
|
||||
|
||||
return {}
|
||||
|
||||
|
||||
@mutation.field("rateShout")
|
||||
@login_required
|
||||
async def rate_shout(_, info, slug, value):
|
||||
auth = info.context["request"].auth
|
||||
user = info.context["request"].user
|
||||
|
||||
with local_session() as session:
|
||||
rating = session.query(ShoutRating).\
|
||||
filter(and_(ShoutRating.rater == user.slug, ShoutRating.shout == slug)).first()
|
||||
if rating:
|
||||
rating.value = value;
|
||||
rating.ts = datetime.now()
|
||||
session.commit()
|
||||
else:
|
||||
rating = ShoutRating.create(
|
||||
rater = user.slug,
|
||||
shout = slug,
|
||||
value = value
|
||||
)
|
||||
|
||||
await ShoutRatingStorage.update_rating(rating)
|
||||
|
||||
return {"error" : ""}
|
||||
|
Reference in New Issue
Block a user