proposals and refactoring

This commit is contained in:
tonyrewin 2022-06-19 20:54:39 +03:00
parent 30c51ecac1
commit 11f81d46ce
9 changed files with 545 additions and 370 deletions

View File

@ -6,9 +6,13 @@ from orm.notification import Notification
from orm.shout import Shout, ShoutAuthor, ShoutTopic, ShoutRating, ShoutViewByDay,\ from orm.shout import Shout, ShoutAuthor, ShoutTopic, ShoutRating, ShoutViewByDay,\
ShoutRatingStorage, ShoutViewStorage ShoutRatingStorage, ShoutViewStorage
from orm.base import Base, engine, local_session from orm.base import Base, engine, local_session
from orm.comment import Comment, CommentRating from orm.comment import Comment, CommentRating #, CommentRatingStorage
from orm.proposal import Proposal, ProposalRating #, ProposalRatingStorage
__all__ = ["User", "Role", "Community", "Operation", "Permission", "Shout", "Topic", "TopicSubscription", "Notification", "ShoutRating", "Comment", "CommentRating", "UserRating"] __all__ = ["User", "Role", "Community", "Operation", \
"Permission", "Shout", "Topic", "TopicSubscription", \
"Notification", "ShoutRating", "Comment", "CommentRating", \
"UserRating", "Proposal", "ProposalRating"]
Base.metadata.create_all(engine) Base.metadata.create_all(engine)
Operation.init_table() Operation.init_table()
@ -19,6 +23,8 @@ Role.init_table()
with local_session() as session: with local_session() as session:
ShoutRatingStorage.init(session) ShoutRatingStorage.init(session)
# CommentRatingStorage.init(session)
# ProposalRatingStorage.init(session)
ShoutViewStorage.init(session) ShoutViewStorage.init(session)
RoleStorage.init(session) RoleStorage.init(session)
UserStorage.init(session) UserStorage.init(session)

View File

@ -1,23 +1,33 @@
from typing import List from typing import List
from datetime import datetime from datetime import datetime
from sqlalchemy import Column, Integer, String, ForeignKey, Datetime from sqlalchemy import Column, Integer, String, ForeignKey, DateTime
from sqlalchemy.orm import relationship
from orm import Permission from orm import Permission
from orm.base import Base from orm.base import Base
class Proposal(Base): class ProposalRating(Base):
__tablename__ = 'proposal' __tablename__ = "comment_rating"
shout: int = Column(Integer, ForeignKey("shout.id"), nullable=False, comment="Shout") id = None
range: str = Column(String, nullable=True, comment="Range in format <start index>:<end>") proposal_id = Column(ForeignKey('proposal.id'), primary_key = True)
body: str = Column(String, nullable=False, comment="Body") createdBy = Column(ForeignKey('user.slug'), primary_key = True)
createdBy: int = Column(Integer, ForeignKey("user.id"), nullable=False, comment="Author") createdAt: str = Column(DateTime, nullable=False, default = datetime.now, comment="Timestamp")
createdAt: str = Column(datetime, nullable=False, comment="Created at") value = Column(Integer)
updatedAt: str = Column(datetime, nullable=True, comment="Updated at")
acceptedAt: str = Column(datetime, nullable=True, comment="Accepted at") class Proposal(Base):
acceptedBy: str = Column(datetime, nullable=True, comment="Accepted by") __tablename__ = 'proposal'
deletedAt: str = Column(datetime, nullable=True, comment="Deleted at")
declinedAt: str = Column(datetime, nullable=True, comment="Declined at) shout: str = Column(String, ForeignKey("shout.slug"), nullable=False, comment="Shout")
declinedBy: str = Column(datetime, nullable=True, comment="Declined by") range: str = Column(String, nullable=True, comment="Range in format <start index>:<end>")
# TODO: debug, logix body: str = Column(String, nullable=False, comment="Body")
createdBy: int = Column(Integer, ForeignKey("user.id"), nullable=False, comment="Author")
createdAt: str = Column(DateTime, nullable=False, comment="Created at")
updatedAt: str = Column(DateTime, nullable=True, comment="Updated at")
acceptedAt: str = Column(DateTime, nullable=True, comment="Accepted at")
acceptedBy: str = Column(Integer, ForeignKey("user.id"), nullable=True, comment="Accepted by")
declinedAt: str = Column(DateTime, nullable=True, comment="Declined at")
declinedBy: str = Column(Integer, ForeignKey("user.id"), nullable=True, comment="Declined by")
ratings = relationship(ProposalRating, foreign_keys=ProposalRating.proposal_id)
deletedAt: str = Column(DateTime, nullable=True, comment="Deleted at")
# TODO: debug, logix

View File

@ -1,42 +1,74 @@
from resolvers.auth import login, sign_out, is_email_used, register, confirm from resolvers.auth import login, sign_out, is_email_used, register, confirm, auth_forget, auth_reset
from resolvers.zine import create_shout, get_shout_by_slug, \ from resolvers.zine import get_shout_by_slug, subscribe, unsubscribe, view_shout, rate_shout, \
top_month, top_overall, recent_published, recent_all, top_viewed, \ top_month, top_overall, recent_published, recent_all, top_viewed, \
shouts_by_authors, shouts_by_topics, shouts_by_communities, \ shouts_by_authors, shouts_by_topics, shouts_by_communities
shouts_reviewed, shouts_subscribed from resolvers.profile import get_users_by_slugs, get_current_user, shouts_reviewed, shouts_subscribed
from resolvers.profile import get_users_by_slugs, get_current_user
from resolvers.topics import topic_subscribe, topic_unsubscribe, topics_by_author, \ from resolvers.topics import topic_subscribe, topic_unsubscribe, topics_by_author, \
topics_by_community, topics_by_slugs topics_by_community, topics_by_slugs
from resolvers.comments import create_comment 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
from resolvers.editor import create_shout, delete_shout, update_shout
from resolvers.community import create_community, delete_community, get_community, get_communities from resolvers.community import create_community, delete_community, get_community, get_communities
__all__ = [ __all__ = [
"login", # auth
"register", "login",
"is_email_used", "register",
"confirm", "is_email_used",
# TODO: "reset_password_code", "confirm",
# TODO: "reset_password_confirm", "auth_forget",
"create_shout", "auth_reset"
"get_current_user",
"get_users_by_slugs", # profile
"get_shout_by_slug", "get_current_user",
"recent_published", "get_users_by_slugs",
"recent_all",
"shouts_by_topics", # zine
"shouts_by_authors", "recent_published",
"shouts_by_communities", "recent_all",
"shouts_subscribed", "shouts_by_topics",
"shouts_reviewed", "shouts_by_authors",
"top_month", "shouts_by_communities",
"top_overall", "shouts_subscribed",
"top_viewed", "shouts_reviewed",
"topics_by_slugs", "top_month",
"topics_by_community", "top_overall",
"topics_by_author", "top_viewed",
"topic_subscribe", "rate_shout",
"topic_unsubscribe", "view_shout",
"create_community", "get_shout_by_slug",
"delete_community",
"get_community", # editor
"get_communities" "create_shout",
] "update_shout",
"delete_shout",
# topics
"topics_by_slugs",
"topics_by_community",
"topics_by_author",
"topic_subscribe",
"topic_unsubscribe",
# communities
"get_community",
"get_communities",
"create_community",
"delete_community",
# comments
"get_shout_comments",
"create_comment",
"update_comment",
"delete_comment",
# collab
"get_shout_proposals",
"create_proposal",
"update_proposal",
"disable_proposal",
"accept_proposal",
"decline_proposal",
"delete_proposal"
]

View File

@ -18,6 +18,7 @@ from settings import JWT_AUTH_HEADER
@mutation.field("confirmEmail") @mutation.field("confirmEmail")
async def confirm(*_, confirm_token): async def confirm(*_, confirm_token):
''' confirm owning email address '''
auth_token, user = await Authorize.confirm(confirm_token) auth_token, user = await Authorize.confirm(confirm_token)
if auth_token: if auth_token:
user.emailConfirmed = True user.emailConfirmed = True
@ -29,6 +30,7 @@ async def confirm(*_, confirm_token):
@mutation.field("registerUser") @mutation.field("registerUser")
async def register(*_, email: str, password: str = ""): async def register(*_, email: str, password: str = ""):
''' creates new user account '''
with local_session() as session: with local_session() as session:
user = session.query(User).filter(User.email == email).first() user = session.query(User).filter(User.email == email).first()
if user: if user:
@ -51,7 +53,8 @@ async def register(*_, email: str, password: str = ""):
return { "user": user } return { "user": user }
@mutation.field("requestPasswordUpdate") @mutation.field("requestPasswordUpdate")
async def request_password_update(_, info, email): async def auth_forget(_, info, email):
''' send email to recover account '''
with local_session() as session: with local_session() as session:
user = session.query(User).filter(User.email == email).first() user = session.query(User).filter(User.email == email).first()
if not user: if not user:
@ -62,9 +65,10 @@ async def request_password_update(_, info, email):
return {} return {}
@mutation.field("updatePassword") @mutation.field("updatePassword")
async def update_password(_, info, password, token): async def auth_reset(_, info, password, resetToken):
''' set the new password '''
try: try:
user_id = await ResetPassword.verify(token) user_id = await ResetPassword.verify(resetToken)
except InvalidToken as e: except InvalidToken as e:
return {"error" : e.message} return {"error" : e.message}
@ -79,6 +83,7 @@ async def update_password(_, info, password, token):
@query.field("signIn") @query.field("signIn")
async def login(_, info: GraphQLResolveInfo, email: str, password: str = ""): async def login(_, info: GraphQLResolveInfo, email: str, password: str = ""):
with local_session() as session: with local_session() as session:
orm_user = session.query(User).filter(User.email == email).first() orm_user = session.query(User).filter(User.email == email).first()
if orm_user is None: if orm_user is None:
@ -126,4 +131,4 @@ async def sign_out(_, info: GraphQLResolveInfo):
async def is_email_used(_, info, email): async def is_email_used(_, info, email):
with local_session() as session: with local_session() as session:
user = session.query(User).filter(User.email == email).first() user = session.query(User).filter(User.email == email).first()
return not user is None return not user is None

191
resolvers/collab.py Normal file
View File

@ -0,0 +1,191 @@
from orm import Proposal, ProposalRating, UserStorage
from orm.base import local_session
from resolvers.base import mutation, query, subscription
from auth.authenticate import login_required
import asyncio
from datetime import datetime
class ProposalResult:
def __init__(self, status, proposal):
self.status = status
self.proposal = proposal
@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 ProposalSubscriptions.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 ProposalSubscriptions.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 ProposalSubscriptions.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 ProposalSubscriptions.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 ProposalSubscriptions.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 ProposalSubscriptions.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 ProposalSubscriptions.put(result)
return {}

View File

@ -1,123 +1,109 @@
from orm import Proposal, ProposalRating from orm import Shout, ShoutRating, ShoutRatingStorage
from orm.base import local_session from orm.base import local_session
from resolvers.base import mutation, query, subscription from resolvers.base import mutation, query, subscription
from auth.authenticate import login_required from auth.authenticate import login_required
import asyncio import asyncio
from datetime import datetime from datetime import datetime
class ProposalResult:
def __init__(self, status, proposal):
self.status = status
self.proposal = proposal
@mutation.field("createProposal") @mutation.field("createShout")
@login_required @login_required
async def create_proposal(_, info, body, shout, range = None): async def create_shout(_, info, input):
user = info.context["request"].user
topic_slugs = input.get("topic_slugs", [])
if topic_slugs:
del input["topic_slugs"]
new_shout = Shout.create(**input)
ShoutAuthor.create(
shout = new_shout.slug,
user = user.slug)
if "mainTopic" in input:
topic_slugs.append(input["mainTopic"])
for slug in topic_slugs:
topic = ShoutTopic.create(
shout = new_shout.slug,
topic = slug)
new_shout.topic_slugs = topic_slugs
task = GitTask(
input,
user.username,
user.email,
"new shout %s" % (new_shout.slug)
)
await ShoutSubscriptions.send_shout(new_shout)
return {
"shout" : new_shout
}
@mutation.field("updateShout")
@login_required
async def update_shout(_, info, input):
auth = info.context["request"].auth auth = info.context["request"].auth
user_id = auth.user_id user_id = auth.user_id
proposal = Proposal.create( slug = input["slug"]
createdBy = user_id,
body = body, session = local_session()
shout = shout, user = session.query(User).filter(User.id == user_id).first()
range = range shout = session.query(Shout).filter(Shout.slug == slug).first()
if not shout:
return {
"error" : "shout not found"
}
authors = [author.id for author in shout.authors]
if not user_id in authors:
scopes = auth.scopes
print(scopes)
if not Resource.shout_id in scopes:
return {
"error" : "access denied"
}
shout.update(input)
shout.updatedAt = datetime.now()
session.commit()
session.close()
for topic in input.get("topic_slugs", []):
ShoutTopic.create(
shout = slug,
topic = topic)
task = GitTask(
input,
user.username,
user.email,
"update shout %s" % (slug)
) )
result = ProposalResult("NEW", proposal) return {
await ProposalSubscriptions.put(result) "shout" : shout
}
return {"proposal": proposal} @mutation.field("deleteShout")
@mutation.field("updateProposal")
@login_required @login_required
async def update_proposal(_, info, id, body): async def delete_shout(_, info, slug):
auth = info.context["request"].auth auth = info.context["request"].auth
user_id = auth.user_id user_id = auth.user_id
with local_session() as session: with local_session() as session:
proposal = session.query(Proposal).filter(Proposal.id == id).first() shout = session.query(Shout).filter(Shout.slug == slug).first()
shout = session.query(Shout.slug === proposal.shout) authors = [author.id for author in shout.authors]
if not proposal: if not comment:
return {"error": "invalid proposal id"} return {"error": "invalid shout slug"}
if proposal.author != user_id: if user_id not in authors:
return {"error": "access denied"}
proposal.body = body
proposal.updatedAt = datetime.now()
session.commit()
result = ProposalResult("UPDATED", proposal)
await ProposalSubscriptions.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"} return {"error": "access denied"}
proposal.deletedAt = datetime.now() shout.deletedAt = datetime.now()
session.commit() session.commit()
result = ProposalResult("DELETED", proposal) return {}
await ProposalSubscriptions.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 ProposalSubscriptions.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()
if not proposal:
return {"error": "invalid proposal id"}
if proposal.acceptedBy == user_id: # TODO: manage ACL here to give access all editors
return {"error": "access denied"}
proposal.acceptedAt = datetime.now()
proposal.acceptedBy = user_id
session.commit()
result = ProposalResult("ACCEPTED", proposal)
await ProposalSubscriptions.put(result)
return {}

View File

@ -153,36 +153,95 @@ def author_unsubscribe(user, slug):
session.delete(sub) session.delete(sub)
session.commit() session.commit()
@mutation.field("subscribe") @query.field("shoutsRatedByUser")
@login_required @login_required
async def subscribe(_, info, subscription, slug): async def shouts_rated_by_user(_, info, page, size):
user = info.context["request"].user user = info.context["request"].user
try: with local_session() as session:
if subscription == "AUTHOR": shouts = session.query(Shout).\
author_subscribe(user, slug) join(ShoutRating).\
elif subscription == "TOPIC": where(ShoutRating.rater == user.slug).\
topic_subscribe(user, slug) order_by(desc(ShoutRating.ts)).\
elif subscription == "COMMUNITY": limit(size).\
community_subscribe(user, slug) offset( (page - 1) * size)
except Exception as e:
return {"error" : e}
return {} return {
"shouts" : shouts
}
@mutation.field("unsubscribe") @query.field("userUnpublishedShouts")
@login_required @login_required
async def unsubscribe(_, info, subscription, slug): async def user_unpublished_shouts(_, info, page, size):
user = info.context["request"].user user = info.context["request"].user
try: with local_session() as session:
if subscription == "AUTHOR": shouts = session.query(Shout).\
author_unsubscribe(user, slug) join(ShoutAuthor).\
elif subscription == "TOPIC": where(and_(Shout.publishedAt == None, ShoutAuthor.user == user.slug)).\
topic_unsubscribe(user, slug) order_by(desc(Shout.createdAt)).\
elif subscription == "COMMUNITY": limit(size).\
community_unsubscribe(user, slug) offset( (page - 1) * size)
except Exception as e:
return {"error" : e} 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.author == 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("shoutsSubscribed")
@login_required
async def shouts_subscribed(_, info, page, size):
user = info.context["request"].user
with local_session() as session:
shouts_by_topic = session.query(Shout).\
join(ShoutTopic).\
join(TopicSubscription, ShoutTopic.topic == TopicSubscription.topic).\
where(TopicSubscription.subscriber == user.slug)
shouts_by_author = session.query(Shout).\
join(ShoutAuthor).\
join(AuthorSubscription, ShoutAuthor.user == AuthorSubscription.author).\
where(AuthorSubscription.subscriber == user.slug)
shouts_by_community = session.query(Shout).\
join(Community).\
join(CommunitySubscription).\
where(CommunitySubscription.subscriber == user.slug)
shouts = shouts_by_topic.union(shouts_by_author).\
union(shouts_by_community).\
order_by(desc(Shout.createdAt)).\
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.author == user.id).\
order_by(desc(Comment.createdAt)).\
limit(size).\
offset( (page - 1) * size)
return shouts
return {}

View File

@ -257,113 +257,6 @@ async def recent_commented(_, info, page, size):
async with ShoutsCache.lock: async with ShoutsCache.lock:
return ShoutsCache.recent_commented[(page - 1) * size : page * size] return ShoutsCache.recent_commented[(page - 1) * size : page * size]
@mutation.field("createShout")
@login_required
async def create_shout(_, info, input):
user = info.context["request"].user
topic_slugs = input.get("topic_slugs", [])
if topic_slugs:
del input["topic_slugs"]
new_shout = Shout.create(**input)
ShoutAuthor.create(
shout = new_shout.slug,
user = user.slug)
if "mainTopic" in input:
topic_slugs.append(input["mainTopic"])
for slug in topic_slugs:
topic = ShoutTopic.create(
shout = new_shout.slug,
topic = slug)
new_shout.topic_slugs = topic_slugs
task = GitTask(
input,
user.username,
user.email,
"new shout %s" % (new_shout.slug)
)
await ShoutSubscriptions.send_shout(new_shout)
return {
"shout" : new_shout
}
@mutation.field("updateShout")
@login_required
async def update_shout(_, info, input):
auth = info.context["request"].auth
user_id = auth.user_id
slug = input["slug"]
session = local_session()
user = session.query(User).filter(User.id == user_id).first()
shout = session.query(Shout).filter(Shout.slug == slug).first()
if not shout:
return {
"error" : "shout not found"
}
authors = [author.id for author in shout.authors]
if not user_id in authors:
scopes = auth.scopes
print(scopes)
if not Resource.shout_id in scopes:
return {
"error" : "access denied"
}
shout.update(input)
shout.updatedAt = datetime.now()
session.commit()
session.close()
for topic in input.get("topic_slugs", []):
ShoutTopic.create(
shout = slug,
topic = topic)
task = GitTask(
input,
user.username,
user.email,
"update shout %s" % (slug)
)
return {
"shout" : shout
}
@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" : ""}
@mutation.field("viewShout") @mutation.field("viewShout")
async def view_shout(_, info, slug): async def view_shout(_, info, slug):
await ShoutViewStorage.inc_view(slug) await ShoutViewStorage.inc_view(slug)
@ -439,94 +332,61 @@ async def shouts_by_communities(_, info, slugs, page, size):
offset(page * size) offset(page * size)
return shouts return shouts
@query.field("shoutsSubscribed") @mutation.field("subscribe")
@login_required @login_required
async def shouts_subscribed(_, info, page, size): async def subscribe(_, info, subscription, slug):
user = info.context["request"].user user = info.context["request"].user
with local_session() as session:
shouts_by_topic = session.query(Shout).\
join(ShoutTopic).\
join(TopicSubscription, ShoutTopic.topic == TopicSubscription.topic).\
where(TopicSubscription.subscriber == user.slug)
shouts_by_author = session.query(Shout).\
join(ShoutAuthor).\
join(AuthorSubscription, ShoutAuthor.user == AuthorSubscription.author).\
where(AuthorSubscription.subscriber == user.slug)
shouts_by_community = session.query(Shout).\
join(Community).\
join(CommunitySubscription).\
where(CommunitySubscription.subscriber == user.slug)
shouts = shouts_by_topic.union(shouts_by_author).\
union(shouts_by_community).\
order_by(desc(Shout.createdAt)).\
limit(size).\
offset( (page - 1) * size)
return shouts try:
if subscription == "AUTHOR":
author_subscribe(user, slug)
elif subscription == "TOPIC":
topic_subscribe(user, slug)
elif subscription == "COMMUNITY":
community_subscribe(user, slug)
except Exception as e:
return {"error" : e}
@query.field("shoutsReviewed") return {}
@mutation.field("unsubscribe")
@login_required @login_required
async def shouts_reviewed(_, info, page, size): async def unsubscribe(_, info, subscription, slug):
user = info.context["request"].user 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.author == user.id))
shouts = shouts_by_rating.union(shouts_by_comment).\
order_by(desc(Shout.publishedAt)).\
limit(size).\
offset( (page - 1) * size)
return shouts try:
if subscription == "AUTHOR":
author_unsubscribe(user, slug)
elif subscription == "TOPIC":
topic_unsubscribe(user, slug)
elif subscription == "COMMUNITY":
community_unsubscribe(user, slug)
except Exception as e:
return {"error" : e}
@query.field("shoutsCommentedByUser") return {}
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.author == user.id).\
order_by(desc(Comment.createdAt)).\
limit(size).\
offset( (page - 1) * size)
return shouts
@query.field("shoutsRatedByUser") @mutation.field("rateShout")
@login_required @login_required
async def shouts_rated_by_user(_, info, page, size): async def rate_shout(_, info, slug, value):
auth = info.context["request"].auth
user = info.context["request"].user user = info.context["request"].user
with local_session() as session: with local_session() as session:
shouts = session.query(Shout).\ rating = session.query(ShoutRating).\
join(ShoutRating).\ filter(and_(ShoutRating.rater == user.slug, ShoutRating.shout == slug)).first()
where(ShoutRating.rater == user.slug).\ if rating:
order_by(desc(ShoutRating.ts)).\ rating.value = value;
limit(size).\ rating.ts = datetime.now()
offset( (page - 1) * size) session.commit()
else:
rating = ShoutRating.create(
rater = user.slug,
shout = slug,
value = value
)
return { await ShoutRatingStorage.update_rating(rating)
"shouts" : shouts
}
@query.field("userUnpublishedShouts") return {"error" : ""}
@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
}

View File

@ -126,15 +126,26 @@ type Mutation {
createTopic(input: TopicInput!): TopicResult! createTopic(input: TopicInput!): TopicResult!
updateTopic(input: TopicInput!): TopicResult! updateTopic(input: TopicInput!): TopicResult!
# comments
createComment(body: String!, shout: String!, replyTo: Int): CommentResult! createComment(body: String!, shout: String!, replyTo: Int): CommentResult!
updateComment(id: Int!, body: String!): CommentResult! updateComment(id: Int!, body: String!): CommentResult!
deleteComment(id: Int!): Result! deleteComment(id: Int!): Result!
rateComment(id: Int!, value: Int!): Result! rateComment(id: Int!, value: Int!): Result!
# community
createCommunity(title: String!, desc: String!): Community! createCommunity(title: String!, desc: String!): Community!
updateCommunity(community: CommunityInput!): Community! updateCommunity(community: CommunityInput!): Community!
deleteCommunity(id: Int!): Result! deleteCommunity(id: Int!): Result!
# proposal
createProposal(body: String!, range: String): Proposal!
updateProposal(body: String!, range: String): Proposal!
acceptProposal(id: Int!): Result!
declineProposal(id: Int!): Result!
disableProposal(id: Int!): Result!
deleteProposal(id: Int!): Result!
rateProposal(id: Int!): Result!
subscribe(what: SubscriptionType!, slug: String!): Result! subscribe(what: SubscriptionType!, slug: String!): Result!
unsubscribe(what: SubscriptionType!, slug: String!): Result! unsubscribe(what: SubscriptionType!, slug: String!): Result!
} }
@ -167,6 +178,9 @@ type Query {
shoutsByCommunities(slugs: [String]!, page: Int!, size: Int!): [Shout]! shoutsByCommunities(slugs: [String]!, page: Int!, size: Int!): [Shout]!
getShoutComments(slug: String!): [Comment]! getShoutComments(slug: String!): [Comment]!
# collab
getShoutProposals(slug: String!): [Proposal]!
# mainpage # mainpage
topViewed(page: Int!, size: Int!): [Shout]! topViewed(page: Int!, size: Int!): [Shout]!
topMonth(page: Int!, size: Int!): [Shout]! topMonth(page: Int!, size: Int!): [Shout]!
@ -353,17 +367,29 @@ type Topic {
topicStat: TopicStat topicStat: TopicStat
} }
enum ProposalStatus {
NEW
UPDATED
UPDATED_RATING
ACCEPTED
DECLINED
DISABLED
DELETED
}
type Proposal { type Proposal {
createdBy: String! shout: String!
shout: string!
range: String # full / 0:2340 range: String # full / 0:2340
body: String! body: String!
createdAt: DateTime! createdAt: DateTime!
createdBy: String!
updatedAt: DateTime updatedAt: DateTime
acceptedAt: DateTime acceptedAt: DateTime
acceptedBy: string acceptedBy: Int
declinedAt: DateTime declinedAt: DateTime
declinedBy: string declinedBy: Int
disabledAt: DateTime
disabledBy: Int
} }
type Token { type Token {