Merge branch 'prealpha' into main
This commit is contained in:
commit
50b2ae4154
|
@ -9,7 +9,7 @@ from starlette.requests import HTTPConnection
|
||||||
|
|
||||||
from auth.credentials import AuthCredentials, AuthUser
|
from auth.credentials import AuthCredentials, AuthUser
|
||||||
from base.orm import local_session
|
from base.orm import local_session
|
||||||
from orm.user import User, Role, UserRole
|
from orm.user import User, Role
|
||||||
|
|
||||||
from settings import SESSION_TOKEN_HEADER
|
from settings import SESSION_TOKEN_HEADER
|
||||||
from auth.tokenstorage import SessionToken
|
from auth.tokenstorage import SessionToken
|
||||||
|
@ -39,12 +39,14 @@ class JWTAuthenticate(AuthenticationBackend):
|
||||||
user = None
|
user = None
|
||||||
with local_session() as session:
|
with local_session() as session:
|
||||||
try:
|
try:
|
||||||
q = select(
|
user = (
|
||||||
User
|
session.query(User).options(
|
||||||
).filter(
|
joinedload(User.roles).options(joinedload(Role.permissions)),
|
||||||
User.id == payload.user_id
|
joinedload(User.ratings)
|
||||||
).select_from(User)
|
).filter(
|
||||||
user = session.execute(q).unique().one()
|
User.id == payload.user_id
|
||||||
|
).one()
|
||||||
|
)
|
||||||
except exc.NoResultFound:
|
except exc.NoResultFound:
|
||||||
user = None
|
user = None
|
||||||
|
|
||||||
|
@ -59,7 +61,7 @@ class JWTAuthenticate(AuthenticationBackend):
|
||||||
scopes=scopes,
|
scopes=scopes,
|
||||||
logged_in=True
|
logged_in=True
|
||||||
),
|
),
|
||||||
user,
|
AuthUser(user_id=user.id),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
InvalidToken("please try again")
|
InvalidToken("please try again")
|
||||||
|
|
|
@ -10,7 +10,7 @@ lang_subject = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def send_auth_email(user, token, template="email_confirmation", lang="ru"):
|
async def send_auth_email(user, token, lang="ru", template="email_confirmation"):
|
||||||
try:
|
try:
|
||||||
to = "%s <%s>" % (user.name, user.email)
|
to = "%s <%s>" % (user.name, user.email)
|
||||||
if lang not in ['ru', 'en']:
|
if lang not in ['ru', 'en']:
|
||||||
|
|
2
main.py
2
main.py
|
@ -41,6 +41,7 @@ async def start_up():
|
||||||
|
|
||||||
async def dev_start_up():
|
async def dev_start_up():
|
||||||
if exists(DEV_SERVER_STATUS_FILE_NAME):
|
if exists(DEV_SERVER_STATUS_FILE_NAME):
|
||||||
|
await redis.connect()
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
with open(DEV_SERVER_STATUS_FILE_NAME, 'w', encoding='utf-8') as f:
|
with open(DEV_SERVER_STATUS_FILE_NAME, 'w', encoding='utf-8') as f:
|
||||||
|
@ -71,6 +72,7 @@ app.mount("/", GraphQL(schema, debug=True))
|
||||||
dev_app = app = Starlette(
|
dev_app = app = Starlette(
|
||||||
debug=True,
|
debug=True,
|
||||||
on_startup=[dev_start_up],
|
on_startup=[dev_start_up],
|
||||||
|
on_shutdown=[shutdown],
|
||||||
middleware=middleware,
|
middleware=middleware,
|
||||||
routes=routes,
|
routes=routes,
|
||||||
)
|
)
|
||||||
|
|
|
@ -9,7 +9,6 @@ from orm.shout import ShoutReactionsFollower
|
||||||
from orm.topic import TopicFollower
|
from orm.topic import TopicFollower
|
||||||
from orm.user import User
|
from orm.user import User
|
||||||
from orm.shout import Shout
|
from orm.shout import Shout
|
||||||
# from services.stat.reacted import ReactedStorage
|
|
||||||
|
|
||||||
ts = datetime.now(tz=timezone.utc)
|
ts = datetime.now(tz=timezone.utc)
|
||||||
|
|
||||||
|
@ -84,7 +83,6 @@ def migrate_ratings(session, entry, reaction_dict):
|
||||||
)
|
)
|
||||||
session.add(following2)
|
session.add(following2)
|
||||||
session.add(rr)
|
session.add(rr)
|
||||||
# await ReactedStorage.react(rr)
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("[migration] comment rating error: %r" % re_reaction_dict)
|
print("[migration] comment rating error: %r" % re_reaction_dict)
|
||||||
|
|
|
@ -9,7 +9,6 @@ from orm.reaction import Reaction, ReactionKind
|
||||||
from orm.shout import Shout, ShoutTopic, ShoutReactionsFollower
|
from orm.shout import Shout, ShoutTopic, ShoutReactionsFollower
|
||||||
from orm.user import User
|
from orm.user import User
|
||||||
from orm.topic import TopicFollower, Topic
|
from orm.topic import TopicFollower, Topic
|
||||||
# from services.stat.reacted import ReactedStorage
|
|
||||||
from services.stat.viewed import ViewedStorage
|
from services.stat.viewed import ViewedStorage
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
@ -365,7 +364,6 @@ async def content_ratings_to_reactions(entry, slug):
|
||||||
else:
|
else:
|
||||||
rea = Reaction.create(**reaction_dict)
|
rea = Reaction.create(**reaction_dict)
|
||||||
session.add(rea)
|
session.add(rea)
|
||||||
# await ReactedStorage.react(rea)
|
|
||||||
# shout_dict['ratings'].append(reaction_dict)
|
# shout_dict['ratings'].append(reaction_dict)
|
||||||
|
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
|
@ -35,11 +35,12 @@ def migrate(entry):
|
||||||
slug = entry["profile"].get("path").lower()
|
slug = entry["profile"].get("path").lower()
|
||||||
slug = re.sub('[^0-9a-zA-Z]+', '-', slug).strip()
|
slug = re.sub('[^0-9a-zA-Z]+', '-', slug).strip()
|
||||||
user_dict["slug"] = slug
|
user_dict["slug"] = slug
|
||||||
bio = BeautifulSoup(entry.get("profile").get("bio") or "", features="lxml").text
|
bio = (entry.get("profile", {"bio": ""}).get("bio") or "").replace('\(', '(').replace('\)', ')')
|
||||||
if bio.startswith('<'):
|
bio_html = BeautifulSoup(bio, features="lxml").text
|
||||||
print('[migration] bio! ' + bio)
|
if bio == bio_html:
|
||||||
bio = BeautifulSoup(bio, features="lxml").text
|
user_dict["bio"] = bio
|
||||||
bio = bio.replace('\(', '(').replace('\)', ')')
|
else:
|
||||||
|
user_dict["about"] = bio
|
||||||
|
|
||||||
# userpic
|
# userpic
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -56,7 +56,8 @@ class User(Base):
|
||||||
email = Column(String, unique=True, nullable=False, comment="Email")
|
email = Column(String, unique=True, nullable=False, comment="Email")
|
||||||
username = Column(String, nullable=False, comment="Login")
|
username = Column(String, nullable=False, comment="Login")
|
||||||
password = Column(String, nullable=True, comment="Password")
|
password = Column(String, nullable=True, comment="Password")
|
||||||
bio = Column(String, nullable=True, comment="Bio")
|
bio = Column(String, nullable=True, comment="Bio") # status description
|
||||||
|
about = Column(String, nullable=True, comment="About") # long and formatted
|
||||||
userpic = Column(String, nullable=True, comment="Userpic")
|
userpic = Column(String, nullable=True, comment="Userpic")
|
||||||
name = Column(String, nullable=True, comment="Display name")
|
name = Column(String, nullable=True, comment="Display name")
|
||||||
slug = Column(String, unique=True, comment="User's slug")
|
slug = Column(String, unique=True, comment="User's slug")
|
||||||
|
@ -100,7 +101,7 @@ class User(Base):
|
||||||
session.commit()
|
session.commit()
|
||||||
User.default_user = default
|
User.default_user = default
|
||||||
|
|
||||||
async def get_permission(self):
|
def get_permission(self):
|
||||||
scope = {}
|
scope = {}
|
||||||
for role in self.roles:
|
for role in self.roles:
|
||||||
for p in role.permissions:
|
for p in role.permissions:
|
||||||
|
|
|
@ -8,6 +8,7 @@ from starlette.responses import RedirectResponse
|
||||||
from transliterate import translit
|
from transliterate import translit
|
||||||
|
|
||||||
from auth.authenticate import login_required
|
from auth.authenticate import login_required
|
||||||
|
from auth.credentials import AuthCredentials
|
||||||
from auth.email import send_auth_email
|
from auth.email import send_auth_email
|
||||||
from auth.identity import Identity, Password
|
from auth.identity import Identity, Password
|
||||||
from auth.jwtcodec import JWTCodec
|
from auth.jwtcodec import JWTCodec
|
||||||
|
@ -24,20 +25,19 @@ from settings import SESSION_TOKEN_HEADER, FRONTEND_URL
|
||||||
@mutation.field("getSession")
|
@mutation.field("getSession")
|
||||||
@login_required
|
@login_required
|
||||||
async def get_current_user(_, info):
|
async def get_current_user(_, info):
|
||||||
user = info.context["request"].user
|
auth: AuthCredentials = info.context["request"].auth
|
||||||
token = info.context["request"].headers.get("Authorization")
|
token = info.context["request"].headers.get(SESSION_TOKEN_HEADER)
|
||||||
if user and token:
|
|
||||||
|
with local_session() as session:
|
||||||
|
user = session.query(User).where(User.id == auth.user_id).one()
|
||||||
user.lastSeen = datetime.now(tz=timezone.utc)
|
user.lastSeen = datetime.now(tz=timezone.utc)
|
||||||
with local_session() as session:
|
session.commit()
|
||||||
session.add(user)
|
|
||||||
session.commit()
|
return {
|
||||||
return {
|
"token": token,
|
||||||
"token": token,
|
"user": user,
|
||||||
"user": user,
|
"news": await user_subscriptions(user.id),
|
||||||
"news": await user_subscriptions(user.slug),
|
}
|
||||||
}
|
|
||||||
else:
|
|
||||||
raise Unauthorized("No session token present in request, try to login")
|
|
||||||
|
|
||||||
|
|
||||||
@mutation.field("confirmEmail")
|
@mutation.field("confirmEmail")
|
||||||
|
@ -58,7 +58,7 @@ async def confirm_email(_, info, token):
|
||||||
return {
|
return {
|
||||||
"token": session_token,
|
"token": session_token,
|
||||||
"user": user,
|
"user": user,
|
||||||
"news": await user_subscriptions(user.slug)
|
"news": await user_subscriptions(user.id)
|
||||||
}
|
}
|
||||||
except InvalidToken as e:
|
except InvalidToken as e:
|
||||||
raise InvalidToken(e.message)
|
raise InvalidToken(e.message)
|
||||||
|
@ -174,7 +174,7 @@ async def login(_, info, email: str, password: str = "", lang: str = "ru"):
|
||||||
return {
|
return {
|
||||||
"token": session_token,
|
"token": session_token,
|
||||||
"user": user,
|
"user": user,
|
||||||
"news": await user_subscriptions(user.slug),
|
"news": await user_subscriptions(user.id),
|
||||||
}
|
}
|
||||||
except InvalidPassword:
|
except InvalidPassword:
|
||||||
print(f"[auth] {email}: invalid password")
|
print(f"[auth] {email}: invalid password")
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from auth.authenticate import login_required
|
from auth.authenticate import login_required
|
||||||
|
from auth.credentials import AuthCredentials
|
||||||
from base.orm import local_session
|
from base.orm import local_session
|
||||||
from base.resolvers import query, mutation
|
from base.resolvers import query, mutation
|
||||||
from base.exceptions import ObjectNotExist, BaseHttpException
|
from base.exceptions import ObjectNotExist, BaseHttpException
|
||||||
|
@ -10,26 +11,28 @@ from orm.user import User
|
||||||
@query.field("getCollabs")
|
@query.field("getCollabs")
|
||||||
@login_required
|
@login_required
|
||||||
async def get_collabs(_, info):
|
async def get_collabs(_, info):
|
||||||
user = info.context["request"].user
|
auth: AuthCredentials = info.context["request"].auth
|
||||||
|
|
||||||
with local_session() as session:
|
with local_session() as session:
|
||||||
collabs = session.query(Collab).filter(user.slug in Collab.authors)
|
collabs = session.query(Collab).filter(auth.user_id in Collab.authors)
|
||||||
return collabs
|
return collabs
|
||||||
|
|
||||||
|
|
||||||
@mutation.field("inviteCoauthor")
|
@mutation.field("inviteCoauthor")
|
||||||
@login_required
|
@login_required
|
||||||
async def invite_coauthor(_, info, author: str, shout: int):
|
async def invite_coauthor(_, info, author: str, shout: int):
|
||||||
user = info.context["request"].user
|
auth: AuthCredentials = info.context["request"].auth
|
||||||
|
|
||||||
with local_session() as session:
|
with local_session() as session:
|
||||||
s = session.query(Shout).where(Shout.id == shout).one()
|
s = session.query(Shout).where(Shout.id == shout).one()
|
||||||
if not s:
|
if not s:
|
||||||
raise ObjectNotExist("invalid shout id")
|
raise ObjectNotExist("invalid shout id")
|
||||||
else:
|
else:
|
||||||
c = session.query(Collab).where(Collab.shout == shout).one()
|
c = session.query(Collab).where(Collab.shout == shout).one()
|
||||||
if user.slug not in c.authors:
|
if auth.user_id not in c.authors:
|
||||||
raise BaseHttpException("you are not in authors list")
|
raise BaseHttpException("you are not in authors list")
|
||||||
else:
|
else:
|
||||||
invited_user = session.query(User).where(User.slug == author).one()
|
invited_user = session.query(User).where(User.id == author).one()
|
||||||
c.invites.append(invited_user)
|
c.invites.append(invited_user)
|
||||||
session.add(c)
|
session.add(c)
|
||||||
session.commit()
|
session.commit()
|
||||||
|
@ -41,16 +44,17 @@ async def invite_coauthor(_, info, author: str, shout: int):
|
||||||
@mutation.field("removeCoauthor")
|
@mutation.field("removeCoauthor")
|
||||||
@login_required
|
@login_required
|
||||||
async def remove_coauthor(_, info, author: str, shout: int):
|
async def remove_coauthor(_, info, author: str, shout: int):
|
||||||
user = info.context["request"].user
|
auth: AuthCredentials = info.context["request"].auth
|
||||||
|
|
||||||
with local_session() as session:
|
with local_session() as session:
|
||||||
s = session.query(Shout).where(Shout.id == shout).one()
|
s = session.query(Shout).where(Shout.id == shout).one()
|
||||||
if not s:
|
if not s:
|
||||||
raise ObjectNotExist("invalid shout id")
|
raise ObjectNotExist("invalid shout id")
|
||||||
if user.slug != s.createdBy.slug:
|
if auth.user_id != s.createdBy:
|
||||||
raise BaseHttpException("only onwer can remove coauthors")
|
raise BaseHttpException("only owner can remove coauthors")
|
||||||
else:
|
else:
|
||||||
c = session.query(Collab).where(Collab.shout == shout).one()
|
c = session.query(Collab).where(Collab.shout == shout).one()
|
||||||
ca = session.query(CollabAuthor).where(c.shout == shout, c.author == author).one()
|
ca = session.query(CollabAuthor).join(User).where(c.shout == shout, User.slug == author).one()
|
||||||
session.remve(ca)
|
session.remve(ca)
|
||||||
c.invites = filter(lambda x: x.slug == author, c.invites)
|
c.invites = filter(lambda x: x.slug == author, c.invites)
|
||||||
c.authors = filter(lambda x: x.slug == author, c.authors)
|
c.authors = filter(lambda x: x.slug == author, c.authors)
|
||||||
|
@ -64,14 +68,15 @@ async def remove_coauthor(_, info, author: str, shout: int):
|
||||||
@mutation.field("acceptCoauthor")
|
@mutation.field("acceptCoauthor")
|
||||||
@login_required
|
@login_required
|
||||||
async def accept_coauthor(_, info, shout: int):
|
async def accept_coauthor(_, info, shout: int):
|
||||||
user = info.context["request"].user
|
auth: AuthCredentials = info.context["request"].auth
|
||||||
|
|
||||||
with local_session() as session:
|
with local_session() as session:
|
||||||
s = session.query(Shout).where(Shout.id == shout).one()
|
s = session.query(Shout).where(Shout.id == shout).one()
|
||||||
if not s:
|
if not s:
|
||||||
raise ObjectNotExist("invalid shout id")
|
raise ObjectNotExist("invalid shout id")
|
||||||
else:
|
else:
|
||||||
c = session.query(Collab).where(Collab.shout == shout).one()
|
c = session.query(Collab).where(Collab.shout == shout).one()
|
||||||
accepted = filter(lambda x: x.slug == user.slug, c.invites).pop()
|
accepted = filter(lambda x: x.id == auth.user_id, c.invites).pop()
|
||||||
if accepted:
|
if accepted:
|
||||||
c.authors.append(accepted)
|
c.authors.append(accepted)
|
||||||
s.authors.append(accepted)
|
s.authors.append(accepted)
|
||||||
|
|
|
@ -3,6 +3,7 @@ from datetime import datetime, timezone
|
||||||
from sqlalchemy import and_
|
from sqlalchemy import and_
|
||||||
|
|
||||||
from auth.authenticate import login_required
|
from auth.authenticate import login_required
|
||||||
|
from auth.credentials import AuthCredentials
|
||||||
from base.orm import local_session
|
from base.orm import local_session
|
||||||
from base.resolvers import mutation
|
from base.resolvers import mutation
|
||||||
from orm.rbac import Resource
|
from orm.rbac import Resource
|
||||||
|
@ -19,7 +20,7 @@ from orm.collab import Collab
|
||||||
@mutation.field("createShout")
|
@mutation.field("createShout")
|
||||||
@login_required
|
@login_required
|
||||||
async def create_shout(_, info, inp):
|
async def create_shout(_, info, inp):
|
||||||
user = info.context["request"].user
|
auth: AuthCredentials = info.context["request"].auth
|
||||||
|
|
||||||
topic_slugs = inp.get("topic_slugs", [])
|
topic_slugs = inp.get("topic_slugs", [])
|
||||||
if topic_slugs:
|
if topic_slugs:
|
||||||
|
@ -37,24 +38,24 @@ async def create_shout(_, info, inp):
|
||||||
"mainTopic": inp.get("topics", []).pop(),
|
"mainTopic": inp.get("topics", []).pop(),
|
||||||
"visibility": "authors"
|
"visibility": "authors"
|
||||||
})
|
})
|
||||||
authors.remove(user.slug)
|
authors.remove(auth.user_id)
|
||||||
if authors:
|
if authors:
|
||||||
chat = create_chat(None, info, new_shout.title, members=authors)
|
chat = create_chat(None, info, new_shout.title, members=authors)
|
||||||
# create a cooperative chatroom
|
# create a cooperative chatroom
|
||||||
MessagesStorage.register_chat(chat)
|
await MessagesStorage.register_chat(chat)
|
||||||
# now we should create a collab
|
# now we should create a collab
|
||||||
new_collab = Collab.create({
|
new_collab = Collab.create({
|
||||||
"shout": new_shout.id,
|
"shout": new_shout.id,
|
||||||
"authors": [user.slug, ],
|
"authors": [auth.user_id, ],
|
||||||
"invites": authors
|
"invites": authors
|
||||||
})
|
})
|
||||||
session.add(new_collab)
|
session.add(new_collab)
|
||||||
|
|
||||||
# NOTE: shout made by one first author
|
# NOTE: shout made by one first author
|
||||||
sa = ShoutAuthor.create(shout=new_shout.id, user=user.id)
|
sa = ShoutAuthor.create(shout=new_shout.id, user=auth.user_id)
|
||||||
session.add(sa)
|
session.add(sa)
|
||||||
|
|
||||||
reactions_follow(user, new_shout.slug, True)
|
reactions_follow(auth.user_id, new_shout.slug, True)
|
||||||
|
|
||||||
if "mainTopic" in inp:
|
if "mainTopic" in inp:
|
||||||
topic_slugs.append(inp["mainTopic"])
|
topic_slugs.append(inp["mainTopic"])
|
||||||
|
@ -65,11 +66,11 @@ async def create_shout(_, info, inp):
|
||||||
st = ShoutTopic.create(shout=new_shout.id, topic=topic.id)
|
st = ShoutTopic.create(shout=new_shout.id, topic=topic.id)
|
||||||
session.add(st)
|
session.add(st)
|
||||||
tf = session.query(TopicFollower).where(
|
tf = session.query(TopicFollower).where(
|
||||||
and_(TopicFollower.follower == user.id, TopicFollower.topic == topic.id)
|
and_(TopicFollower.follower == auth.user_id, TopicFollower.topic == topic.id)
|
||||||
)
|
)
|
||||||
|
|
||||||
if not tf:
|
if not tf:
|
||||||
tf = TopicFollower.create(follower=user.id, topic=topic.id, auto=True)
|
tf = TopicFollower.create(follower=auth.user_id, topic=topic.id, auto=True)
|
||||||
session.add(tf)
|
session.add(tf)
|
||||||
|
|
||||||
new_shout.topic_slugs = topic_slugs
|
new_shout.topic_slugs = topic_slugs
|
||||||
|
@ -77,7 +78,8 @@ async def create_shout(_, info, inp):
|
||||||
|
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
GitTask(inp, user.username, user.email, "new shout %s" % new_shout.slug)
|
# TODO
|
||||||
|
# GitTask(inp, user.username, user.email, "new shout %s" % new_shout.slug)
|
||||||
|
|
||||||
return {"shout": new_shout}
|
return {"shout": new_shout}
|
||||||
|
|
||||||
|
@ -85,18 +87,17 @@ async def create_shout(_, info, inp):
|
||||||
@mutation.field("updateShout")
|
@mutation.field("updateShout")
|
||||||
@login_required
|
@login_required
|
||||||
async def update_shout(_, info, inp):
|
async def update_shout(_, info, inp):
|
||||||
auth = info.context["request"].auth
|
auth: AuthCredentials = info.context["request"].auth
|
||||||
user_id = auth.user_id
|
|
||||||
slug = inp["slug"]
|
slug = inp["slug"]
|
||||||
|
|
||||||
with local_session() as session:
|
with local_session() as session:
|
||||||
user = session.query(User).filter(User.id == user_id).first()
|
user = session.query(User).filter(User.id == auth.user_id).first()
|
||||||
shout = session.query(Shout).filter(Shout.slug == slug).first()
|
shout = session.query(Shout).filter(Shout.slug == slug).first()
|
||||||
if not shout:
|
if not shout:
|
||||||
return {"error": "shout not found"}
|
return {"error": "shout not found"}
|
||||||
|
|
||||||
authors = [author.id for author in shout.authors]
|
authors = [author.id for author in shout.authors]
|
||||||
if user_id not in authors:
|
if auth.user_id not in authors:
|
||||||
scopes = auth.scopes
|
scopes = auth.scopes
|
||||||
print(scopes)
|
print(scopes)
|
||||||
if Resource.shout not in scopes:
|
if Resource.shout not in scopes:
|
||||||
|
@ -115,7 +116,7 @@ async def update_shout(_, info, inp):
|
||||||
ShoutTopic.create(shout=slug, topic=topic)
|
ShoutTopic.create(shout=slug, topic=topic)
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
GitTask(inp, user.username, user.email, "update shout %s" % (slug))
|
GitTask(inp, user.username, user.email, "update shout %s" % slug)
|
||||||
|
|
||||||
return {"shout": shout}
|
return {"shout": shout}
|
||||||
|
|
||||||
|
@ -123,18 +124,17 @@ async def update_shout(_, info, inp):
|
||||||
@mutation.field("deleteShout")
|
@mutation.field("deleteShout")
|
||||||
@login_required
|
@login_required
|
||||||
async def delete_shout(_, info, slug):
|
async def delete_shout(_, info, slug):
|
||||||
auth = info.context["request"].auth
|
auth: AuthCredentials = info.context["request"].auth
|
||||||
user_id = auth.user_id
|
|
||||||
|
|
||||||
with local_session() as session:
|
with local_session() as session:
|
||||||
shout = session.query(Shout).filter(Shout.slug == slug).first()
|
shout = session.query(Shout).filter(Shout.slug == slug).first()
|
||||||
authors = [a.id for a in shout.authors]
|
authors = [a.id for a in shout.authors]
|
||||||
if not shout:
|
if not shout:
|
||||||
return {"error": "invalid shout slug"}
|
return {"error": "invalid shout slug"}
|
||||||
if user_id not in authors:
|
if auth.user_id not in authors:
|
||||||
return {"error": "access denied"}
|
return {"error": "access denied"}
|
||||||
for a in authors:
|
for a in authors:
|
||||||
reactions_unfollow(a.slug, slug, True)
|
reactions_unfollow(a.id, slug)
|
||||||
shout.deletedAt = datetime.now(tz=timezone.utc)
|
shout.deletedAt = datetime.now(tz=timezone.utc)
|
||||||
session.add(shout)
|
session.add(shout)
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
|
@ -3,6 +3,7 @@ import uuid
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
from auth.authenticate import login_required
|
from auth.authenticate import login_required
|
||||||
|
from auth.credentials import AuthCredentials
|
||||||
from base.redis import redis
|
from base.redis import redis
|
||||||
from base.resolvers import mutation
|
from base.resolvers import mutation
|
||||||
|
|
||||||
|
@ -18,7 +19,7 @@ async def update_chat(_, info, chat_new: dict):
|
||||||
:param chat_new: dict with chat data
|
:param chat_new: dict with chat data
|
||||||
:return: Result { error chat }
|
:return: Result { error chat }
|
||||||
"""
|
"""
|
||||||
user = info.context["request"].user
|
auth: AuthCredentials = info.context["request"].auth
|
||||||
chat_id = chat_new["id"]
|
chat_id = chat_new["id"]
|
||||||
chat = await redis.execute("GET", f"chats/{chat_id}")
|
chat = await redis.execute("GET", f"chats/{chat_id}")
|
||||||
if not chat:
|
if not chat:
|
||||||
|
@ -26,7 +27,9 @@ async def update_chat(_, info, chat_new: dict):
|
||||||
"error": "chat not exist"
|
"error": "chat not exist"
|
||||||
}
|
}
|
||||||
chat = dict(json.loads(chat))
|
chat = dict(json.loads(chat))
|
||||||
if user.slug in chat["admins"]:
|
|
||||||
|
# TODO
|
||||||
|
if auth.user_id in chat["admins"]:
|
||||||
chat.update({
|
chat.update({
|
||||||
"title": chat_new.get("title", chat["title"]),
|
"title": chat_new.get("title", chat["title"]),
|
||||||
"description": chat_new.get("description", chat["description"]),
|
"description": chat_new.get("description", chat["description"]),
|
||||||
|
@ -46,10 +49,11 @@ async def update_chat(_, info, chat_new: dict):
|
||||||
@mutation.field("createChat")
|
@mutation.field("createChat")
|
||||||
@login_required
|
@login_required
|
||||||
async def create_chat(_, info, title="", members=[]):
|
async def create_chat(_, info, title="", members=[]):
|
||||||
user = info.context["request"].user
|
auth: AuthCredentials = info.context["request"].auth
|
||||||
chat = {}
|
chat = {}
|
||||||
if user.slug not in members:
|
|
||||||
members.append(user.slug)
|
if auth.user_id not in members:
|
||||||
|
members.append(auth.user_id)
|
||||||
|
|
||||||
# reuse chat craeted before if exists
|
# reuse chat craeted before if exists
|
||||||
if len(members) == 2 and title == "":
|
if len(members) == 2 and title == "":
|
||||||
|
@ -73,7 +77,7 @@ async def create_chat(_, info, title="", members=[]):
|
||||||
"id": chat_id,
|
"id": chat_id,
|
||||||
"users": members,
|
"users": members,
|
||||||
"title": title,
|
"title": title,
|
||||||
"createdBy": user.slug,
|
"createdBy": auth.user_id,
|
||||||
"createdAt": int(datetime.now(tz=timezone.utc).timestamp()),
|
"createdAt": int(datetime.now(tz=timezone.utc).timestamp()),
|
||||||
"updatedAt": int(datetime.now(tz=timezone.utc).timestamp()),
|
"updatedAt": int(datetime.now(tz=timezone.utc).timestamp()),
|
||||||
"admins": []
|
"admins": []
|
||||||
|
@ -93,13 +97,14 @@ async def create_chat(_, info, title="", members=[]):
|
||||||
@mutation.field("deleteChat")
|
@mutation.field("deleteChat")
|
||||||
@login_required
|
@login_required
|
||||||
async def delete_chat(_, info, chat_id: str):
|
async def delete_chat(_, info, chat_id: str):
|
||||||
user = info.context["request"].user
|
auth: AuthCredentials = info.context["request"].auth
|
||||||
|
|
||||||
chat = await redis.execute("GET", f"/chats/{chat_id}")
|
chat = await redis.execute("GET", f"/chats/{chat_id}")
|
||||||
if chat:
|
if chat:
|
||||||
chat = dict(json.loads(chat))
|
chat = dict(json.loads(chat))
|
||||||
if user.slug in chat['admins']:
|
if auth.user_id in chat['admins']:
|
||||||
await redis.execute("DEL", f"chats/{chat_id}")
|
await redis.execute("DEL", f"chats/{chat_id}")
|
||||||
await redis.execute("SREM", "chats_by_user/" + user, chat_id)
|
await redis.execute("SREM", "chats_by_user/" + str(auth.user_id), chat_id)
|
||||||
await redis.execute("COMMIT")
|
await redis.execute("COMMIT")
|
||||||
else:
|
else:
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -2,6 +2,7 @@ import json
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
|
|
||||||
from auth.authenticate import login_required
|
from auth.authenticate import login_required
|
||||||
|
from auth.credentials import AuthCredentials
|
||||||
from base.redis import redis
|
from base.redis import redis
|
||||||
from base.orm import local_session
|
from base.orm import local_session
|
||||||
from base.resolvers import query
|
from base.resolvers import query
|
||||||
|
@ -30,12 +31,9 @@ async def load_messages(chat_id: str, limit: int, offset: int):
|
||||||
@login_required
|
@login_required
|
||||||
async def load_chats(_, info, limit: int = 50, offset: int = 0):
|
async def load_chats(_, info, limit: int = 50, offset: int = 0):
|
||||||
""" load :limit chats of current user with :offset """
|
""" load :limit chats of current user with :offset """
|
||||||
user = info.context["request"].user
|
auth: AuthCredentials = info.context["request"].auth
|
||||||
if user:
|
|
||||||
print('[inbox] load user\'s chats %s' % user.slug)
|
cids = await redis.execute("SMEMBERS", "chats_by_user/" + str(auth.user_id))
|
||||||
else:
|
|
||||||
raise Unauthorized("Please login to load chats")
|
|
||||||
cids = await redis.execute("SMEMBERS", "chats_by_user/" + user.slug)
|
|
||||||
if cids:
|
if cids:
|
||||||
cids = list(cids)[offset:offset + limit]
|
cids = list(cids)[offset:offset + limit]
|
||||||
if not cids:
|
if not cids:
|
||||||
|
@ -47,7 +45,7 @@ async def load_chats(_, info, limit: int = 50, offset: int = 0):
|
||||||
if c:
|
if c:
|
||||||
c = dict(json.loads(c))
|
c = dict(json.loads(c))
|
||||||
c['messages'] = await load_messages(cid, 5, 0)
|
c['messages'] = await load_messages(cid, 5, 0)
|
||||||
c['unread'] = await get_unread_counter(cid, user.slug)
|
c['unread'] = await get_unread_counter(cid, auth.user_id)
|
||||||
with local_session() as session:
|
with local_session() as session:
|
||||||
c['members'] = []
|
c['members'] = []
|
||||||
for userslug in c["users"]:
|
for userslug in c["users"]:
|
||||||
|
@ -65,11 +63,11 @@ async def load_chats(_, info, limit: int = 50, offset: int = 0):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def search_user_chats(by, messages: set, slug: str, limit, offset):
|
async def search_user_chats(by, messages: set, user_id: int, limit, offset):
|
||||||
cids = set([])
|
cids = set([])
|
||||||
by_author = by.get('author')
|
by_author = by.get('author')
|
||||||
body_like = by.get('body')
|
body_like = by.get('body')
|
||||||
cids.unioin(set(await redis.execute("SMEMBERS", "chats_by_user/" + slug)))
|
cids.unioin(set(await redis.execute("SMEMBERS", "chats_by_user/" + str(user_id))))
|
||||||
if by_author:
|
if by_author:
|
||||||
# all author's messages
|
# all author's messages
|
||||||
cids.union(set(await redis.execute("SMEMBERS", f"chats_by_user/{by_author}")))
|
cids.union(set(await redis.execute("SMEMBERS", f"chats_by_user/{by_author}")))
|
||||||
|
@ -104,9 +102,11 @@ async def load_messages_by(_, info, by, limit: int = 10, offset: int = 0):
|
||||||
# everyone's messages in filtered chat
|
# everyone's messages in filtered chat
|
||||||
messages.union(set(await load_messages(by_chat, limit, offset)))
|
messages.union(set(await load_messages(by_chat, limit, offset)))
|
||||||
|
|
||||||
user = info.context["request"].user
|
auth: AuthCredentials = info.context["request"].auth
|
||||||
if user and len(messages) == 0:
|
|
||||||
messages.union(search_user_chats(by, messages, user.slug, limit, offset))
|
if len(messages) == 0:
|
||||||
|
# FIXME
|
||||||
|
messages.union(search_user_chats(by, messages, auth.user_id, limit, offset))
|
||||||
|
|
||||||
days = by.get("days")
|
days = by.get("days")
|
||||||
if days:
|
if days:
|
||||||
|
@ -126,9 +126,10 @@ async def load_messages_by(_, info, by, limit: int = 10, offset: int = 0):
|
||||||
@query.field("loadRecipients")
|
@query.field("loadRecipients")
|
||||||
async def load_recipients(_, info, limit=50, offset=0):
|
async def load_recipients(_, info, limit=50, offset=0):
|
||||||
chat_users = []
|
chat_users = []
|
||||||
user = info.context["request"].user
|
auth: AuthCredentials = info.context["request"].auth
|
||||||
|
|
||||||
try:
|
try:
|
||||||
chat_users += await followed_authors(user.slug)
|
chat_users += await followed_authors(auth.user_id)
|
||||||
limit = limit - len(chat_users)
|
limit = limit - len(chat_users)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -3,6 +3,7 @@ import json
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
from auth.authenticate import login_required
|
from auth.authenticate import login_required
|
||||||
|
from auth.credentials import AuthCredentials
|
||||||
from base.redis import redis
|
from base.redis import redis
|
||||||
from base.resolvers import mutation, subscription
|
from base.resolvers import mutation, subscription
|
||||||
from services.inbox import ChatFollowing, MessageResult, MessagesStorage
|
from services.inbox import ChatFollowing, MessageResult, MessagesStorage
|
||||||
|
@ -12,7 +13,8 @@ from services.inbox import ChatFollowing, MessageResult, MessagesStorage
|
||||||
@login_required
|
@login_required
|
||||||
async def create_message(_, info, chat: str, body: str, replyTo=None):
|
async def create_message(_, info, chat: str, body: str, replyTo=None):
|
||||||
""" create message with :body for :chat_id replying to :replyTo optionally """
|
""" create message with :body for :chat_id replying to :replyTo optionally """
|
||||||
user = info.context["request"].user
|
auth: AuthCredentials = info.context["request"].auth
|
||||||
|
|
||||||
chat = await redis.execute("GET", f"chats/{chat}")
|
chat = await redis.execute("GET", f"chats/{chat}")
|
||||||
if not chat:
|
if not chat:
|
||||||
return {
|
return {
|
||||||
|
@ -25,7 +27,7 @@ async def create_message(_, info, chat: str, body: str, replyTo=None):
|
||||||
new_message = {
|
new_message = {
|
||||||
"chatId": chat['id'],
|
"chatId": chat['id'],
|
||||||
"id": message_id,
|
"id": message_id,
|
||||||
"author": user.slug,
|
"author": auth.user_id,
|
||||||
"body": body,
|
"body": body,
|
||||||
"replyTo": replyTo,
|
"replyTo": replyTo,
|
||||||
"createdAt": int(datetime.now(tz=timezone.utc).timestamp()),
|
"createdAt": int(datetime.now(tz=timezone.utc).timestamp()),
|
||||||
|
@ -55,7 +57,7 @@ async def create_message(_, info, chat: str, body: str, replyTo=None):
|
||||||
@mutation.field("updateMessage")
|
@mutation.field("updateMessage")
|
||||||
@login_required
|
@login_required
|
||||||
async def update_message(_, info, chat_id: str, message_id: int, body: str):
|
async def update_message(_, info, chat_id: str, message_id: int, body: str):
|
||||||
user = info.context["request"].user
|
auth: AuthCredentials = info.context["request"].auth
|
||||||
|
|
||||||
chat = await redis.execute("GET", f"chats/{chat_id}")
|
chat = await redis.execute("GET", f"chats/{chat_id}")
|
||||||
if not chat:
|
if not chat:
|
||||||
|
@ -66,7 +68,7 @@ async def update_message(_, info, chat_id: str, message_id: int, body: str):
|
||||||
return {"error": "message not exist"}
|
return {"error": "message not exist"}
|
||||||
|
|
||||||
message = json.loads(message)
|
message = json.loads(message)
|
||||||
if message["author"] != user.slug:
|
if message["author"] != auth.user_id:
|
||||||
return {"error": "access denied"}
|
return {"error": "access denied"}
|
||||||
|
|
||||||
message["body"] = body
|
message["body"] = body
|
||||||
|
@ -86,7 +88,7 @@ async def update_message(_, info, chat_id: str, message_id: int, body: str):
|
||||||
@mutation.field("deleteMessage")
|
@mutation.field("deleteMessage")
|
||||||
@login_required
|
@login_required
|
||||||
async def delete_message(_, info, chat_id: str, message_id: int):
|
async def delete_message(_, info, chat_id: str, message_id: int):
|
||||||
user = info.context["request"].user
|
auth: AuthCredentials = info.context["request"].auth
|
||||||
|
|
||||||
chat = await redis.execute("GET", f"chats/{chat_id}")
|
chat = await redis.execute("GET", f"chats/{chat_id}")
|
||||||
if not chat:
|
if not chat:
|
||||||
|
@ -97,15 +99,15 @@ async def delete_message(_, info, chat_id: str, message_id: int):
|
||||||
if not message:
|
if not message:
|
||||||
return {"error": "message not exist"}
|
return {"error": "message not exist"}
|
||||||
message = json.loads(message)
|
message = json.loads(message)
|
||||||
if message["author"] != user.slug:
|
if message["author"] != auth.user_id:
|
||||||
return {"error": "access denied"}
|
return {"error": "access denied"}
|
||||||
|
|
||||||
await redis.execute("LREM", f"chats/{chat_id}/message_ids", 0, str(message_id))
|
await redis.execute("LREM", f"chats/{chat_id}/message_ids", 0, str(message_id))
|
||||||
await redis.execute("DEL", f"chats/{chat_id}/messages/{str(message_id)}")
|
await redis.execute("DEL", f"chats/{chat_id}/messages/{str(message_id)}")
|
||||||
|
|
||||||
users = chat["users"]
|
users = chat["users"]
|
||||||
for user_slug in users:
|
for user_id in users:
|
||||||
await redis.execute("LREM", f"chats/{chat_id}/unread/{user_slug}", 0, str(message_id))
|
await redis.execute("LREM", f"chats/{chat_id}/unread/{user_id}", 0, str(message_id))
|
||||||
|
|
||||||
result = MessageResult("DELETED", message)
|
result = MessageResult("DELETED", message)
|
||||||
await MessagesStorage.put(result)
|
await MessagesStorage.put(result)
|
||||||
|
@ -116,7 +118,7 @@ async def delete_message(_, info, chat_id: str, message_id: int):
|
||||||
@mutation.field("markAsRead")
|
@mutation.field("markAsRead")
|
||||||
@login_required
|
@login_required
|
||||||
async def mark_as_read(_, info, chat_id: str, messages: [int]):
|
async def mark_as_read(_, info, chat_id: str, messages: [int]):
|
||||||
user = info.context["request"].user
|
auth: AuthCredentials = info.context["request"].auth
|
||||||
|
|
||||||
chat = await redis.execute("GET", f"chats/{chat_id}")
|
chat = await redis.execute("GET", f"chats/{chat_id}")
|
||||||
if not chat:
|
if not chat:
|
||||||
|
@ -124,11 +126,11 @@ async def mark_as_read(_, info, chat_id: str, messages: [int]):
|
||||||
|
|
||||||
chat = json.loads(chat)
|
chat = json.loads(chat)
|
||||||
users = set(chat["users"])
|
users = set(chat["users"])
|
||||||
if user.slug not in users:
|
if auth.user_id not in users:
|
||||||
return {"error": "access denied"}
|
return {"error": "access denied"}
|
||||||
|
|
||||||
for message_id in messages:
|
for message_id in messages:
|
||||||
await redis.execute("LREM", f"chats/{chat_id}/unread/{user.slug}", 0, str(message_id))
|
await redis.execute("LREM", f"chats/{chat_id}/unread/{auth.user_id}", 0, str(message_id))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"error": None
|
"error": None
|
||||||
|
@ -139,8 +141,9 @@ async def mark_as_read(_, info, chat_id: str, messages: [int]):
|
||||||
@login_required
|
@login_required
|
||||||
async def message_generator(obj, info):
|
async def message_generator(obj, info):
|
||||||
try:
|
try:
|
||||||
user = info.context["request"].user
|
auth: AuthCredentials = info.context["request"].auth
|
||||||
user_following_chats = await redis.execute("GET", f"chats_by_user/{user.slug}")
|
|
||||||
|
user_following_chats = await redis.execute("GET", f"chats_by_user/{auth.user_id}")
|
||||||
if user_following_chats:
|
if user_following_chats:
|
||||||
user_following_chats = list(json.loads(user_following_chats)) # chat ids
|
user_following_chats = list(json.loads(user_following_chats)) # chat ids
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from auth.authenticate import login_required
|
from auth.authenticate import login_required
|
||||||
|
from auth.credentials import AuthCredentials
|
||||||
from base.redis import redis
|
from base.redis import redis
|
||||||
from base.resolvers import query
|
from base.resolvers import query
|
||||||
from base.orm import local_session
|
from base.orm import local_session
|
||||||
|
@ -12,8 +13,8 @@ from orm.user import AuthorFollower, User
|
||||||
async def search_recipients(_, info, query: str, limit: int = 50, offset: int = 0):
|
async def search_recipients(_, info, query: str, limit: int = 50, offset: int = 0):
|
||||||
result = []
|
result = []
|
||||||
# TODO: maybe redis scan?
|
# TODO: maybe redis scan?
|
||||||
user = info.context["request"].user
|
auth: AuthCredentials = info.context["request"].auth
|
||||||
talk_before = await redis.execute("GET", f"/chats_by_user/{user.slug}")
|
talk_before = await redis.execute("GET", f"/chats_by_user/{auth.user_id}")
|
||||||
if talk_before:
|
if talk_before:
|
||||||
talk_before = list(json.loads(talk_before))[offset:offset + limit]
|
talk_before = list(json.loads(talk_before))[offset:offset + limit]
|
||||||
for chat_id in talk_before:
|
for chat_id in talk_before:
|
||||||
|
@ -24,7 +25,6 @@ async def search_recipients(_, info, query: str, limit: int = 50, offset: int =
|
||||||
if member.startswith(query):
|
if member.startswith(query):
|
||||||
if member not in result:
|
if member not in result:
|
||||||
result.append(member)
|
result.append(member)
|
||||||
user = info.context["request"].user
|
|
||||||
|
|
||||||
more_amount = limit - len(result)
|
more_amount = limit - len(result)
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from auth.authenticate import login_required
|
from auth.authenticate import login_required
|
||||||
|
from auth.credentials import AuthCredentials
|
||||||
from base.resolvers import mutation
|
from base.resolvers import mutation
|
||||||
# from resolvers.community import community_follow, community_unfollow
|
# from resolvers.community import community_follow, community_unfollow
|
||||||
from resolvers.zine.profile import author_follow, author_unfollow
|
from resolvers.zine.profile import author_follow, author_unfollow
|
||||||
|
@ -9,17 +10,18 @@ from resolvers.zine.topics import topic_follow, topic_unfollow
|
||||||
@mutation.field("follow")
|
@mutation.field("follow")
|
||||||
@login_required
|
@login_required
|
||||||
async def follow(_, info, what, slug):
|
async def follow(_, info, what, slug):
|
||||||
user = info.context["request"].user
|
auth: AuthCredentials = info.context["request"].auth
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if what == "AUTHOR":
|
if what == "AUTHOR":
|
||||||
author_follow(user, slug)
|
author_follow(auth.user_id, slug)
|
||||||
elif what == "TOPIC":
|
elif what == "TOPIC":
|
||||||
topic_follow(user, slug)
|
topic_follow(auth.user_id, slug)
|
||||||
elif what == "COMMUNITY":
|
elif what == "COMMUNITY":
|
||||||
# community_follow(user, slug)
|
# community_follow(user, slug)
|
||||||
pass
|
pass
|
||||||
elif what == "REACTIONS":
|
elif what == "REACTIONS":
|
||||||
reactions_follow(user, slug)
|
reactions_follow(auth.user_id, slug)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return {"error": str(e)}
|
return {"error": str(e)}
|
||||||
|
|
||||||
|
@ -29,18 +31,18 @@ async def follow(_, info, what, slug):
|
||||||
@mutation.field("unfollow")
|
@mutation.field("unfollow")
|
||||||
@login_required
|
@login_required
|
||||||
async def unfollow(_, info, what, slug):
|
async def unfollow(_, info, what, slug):
|
||||||
user = info.context["request"].user
|
auth: AuthCredentials = info.context["request"].auth
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if what == "AUTHOR":
|
if what == "AUTHOR":
|
||||||
author_unfollow(user, slug)
|
author_unfollow(auth.user_id, slug)
|
||||||
elif what == "TOPIC":
|
elif what == "TOPIC":
|
||||||
topic_unfollow(user, slug)
|
topic_unfollow(auth.user_id, slug)
|
||||||
elif what == "COMMUNITY":
|
elif what == "COMMUNITY":
|
||||||
# community_unfollow(user, slug)
|
# community_unfollow(user, slug)
|
||||||
pass
|
pass
|
||||||
elif what == "REACTIONS":
|
elif what == "REACTIONS":
|
||||||
reactions_unfollow(user, slug)
|
reactions_unfollow(auth.user_id, slug)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return {"error": str(e)}
|
return {"error": str(e)}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
from sqlalchemy.orm import joinedload, aliased
|
from sqlalchemy.orm import joinedload, aliased
|
||||||
from sqlalchemy.sql.expression import desc, asc, select, func
|
from sqlalchemy.sql.expression import desc, asc, select, func
|
||||||
|
|
||||||
|
from auth.credentials import AuthCredentials
|
||||||
from base.orm import local_session
|
from base.orm import local_session
|
||||||
from base.resolvers import query
|
from base.resolvers import query
|
||||||
from orm import ViewedEntry
|
from orm import ViewedEntry
|
||||||
|
@ -15,10 +17,10 @@ def add_stat_columns(q):
|
||||||
return add_common_stat_columns(q)
|
return add_common_stat_columns(q)
|
||||||
|
|
||||||
|
|
||||||
def apply_filters(q, filters, user=None):
|
def apply_filters(q, filters, user_id=None):
|
||||||
|
|
||||||
if filters.get("reacted") and user:
|
if filters.get("reacted") and user_id:
|
||||||
q.join(Reaction, Reaction.createdBy == user.id)
|
q.join(Reaction, Reaction.createdBy == user_id)
|
||||||
|
|
||||||
v = filters.get("visibility")
|
v = filters.get("visibility")
|
||||||
if v == "public":
|
if v == "public":
|
||||||
|
@ -105,17 +107,15 @@ async def load_shouts_by(_, info, options):
|
||||||
|
|
||||||
q = add_stat_columns(q)
|
q = add_stat_columns(q)
|
||||||
|
|
||||||
user = info.context["request"].user
|
auth: AuthCredentials = info.context["request"].auth
|
||||||
q = apply_filters(q, options.get("filters", {}), user)
|
q = apply_filters(q, options.get("filters", {}), auth.user_id)
|
||||||
|
|
||||||
order_by = options.get("order_by", Shout.createdAt)
|
order_by = options.get("order_by", Shout.createdAt)
|
||||||
if order_by == 'reacted':
|
if order_by == 'reacted':
|
||||||
aliased_reaction = aliased(Reaction)
|
aliased_reaction = aliased(Reaction)
|
||||||
q.outerjoin(aliased_reaction).add_columns(func.max(aliased_reaction.createdAt).label('reacted'))
|
q.outerjoin(aliased_reaction).add_columns(func.max(aliased_reaction.createdAt).label('reacted'))
|
||||||
|
|
||||||
order_by_desc = options.get('order_by_desc', True)
|
query_order_by = desc(order_by) if options.get('order_by_desc', True) else asc(order_by)
|
||||||
|
|
||||||
query_order_by = desc(order_by) if order_by_desc else asc(order_by)
|
|
||||||
offset = options.get("offset", 0)
|
offset = options.get("offset", 0)
|
||||||
limit = options.get("limit", 10)
|
limit = options.get("limit", 10)
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ from sqlalchemy import and_, func, distinct, select, literal
|
||||||
from sqlalchemy.orm import aliased, joinedload
|
from sqlalchemy.orm import aliased, joinedload
|
||||||
|
|
||||||
from auth.authenticate import login_required
|
from auth.authenticate import login_required
|
||||||
|
from auth.credentials import AuthCredentials
|
||||||
from base.orm import local_session
|
from base.orm import local_session
|
||||||
from base.resolvers import mutation, query
|
from base.resolvers import mutation, query
|
||||||
from orm.reaction import Reaction
|
from orm.reaction import Reaction
|
||||||
|
@ -40,11 +41,10 @@ def add_author_stat_columns(q):
|
||||||
# func.sum(user_rating_aliased.value).label('rating_stat')
|
# func.sum(user_rating_aliased.value).label('rating_stat')
|
||||||
# )
|
# )
|
||||||
|
|
||||||
# q = q.add_columns(literal(0).label('commented_stat'))
|
q = q.add_columns(literal(0).label('commented_stat'))
|
||||||
|
# q = q.outerjoin(Reaction, and_(Reaction.createdBy == User.id, Reaction.body.is_not(None))).add_columns(
|
||||||
q = q.outerjoin(Reaction, and_(Reaction.createdBy == User.id, Reaction.body.is_not(None))).add_columns(
|
# func.count(distinct(Reaction.id)).label('commented_stat')
|
||||||
func.count(distinct(Reaction.id)).label('commented_stat')
|
# )
|
||||||
)
|
|
||||||
|
|
||||||
q = q.group_by(User.id)
|
q = q.group_by(User.id)
|
||||||
|
|
||||||
|
@ -74,25 +74,25 @@ def get_authors_from_query(q):
|
||||||
return authors
|
return authors
|
||||||
|
|
||||||
|
|
||||||
async def user_subscriptions(slug: str):
|
async def user_subscriptions(user_id: int):
|
||||||
return {
|
return {
|
||||||
"unread": await get_total_unread_counter(slug), # unread inbox messages counter
|
"unread": await get_total_unread_counter(user_id), # unread inbox messages counter
|
||||||
"topics": [t.slug for t in await followed_topics(slug)], # followed topics slugs
|
"topics": [t.slug for t in await followed_topics(user_id)], # followed topics slugs
|
||||||
"authors": [a.slug for a in await followed_authors(slug)], # followed authors slugs
|
"authors": [a.slug for a in await followed_authors(user_id)], # followed authors slugs
|
||||||
"reactions": await followed_reactions(slug)
|
"reactions": await followed_reactions(user_id)
|
||||||
# "communities": [c.slug for c in followed_communities(slug)], # communities
|
# "communities": [c.slug for c in followed_communities(slug)], # communities
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# @query.field("userFollowedDiscussions")
|
# @query.field("userFollowedDiscussions")
|
||||||
@login_required
|
# @login_required
|
||||||
async def followed_discussions(_, info, slug) -> List[Topic]:
|
async def followed_discussions(_, info, user_id) -> List[Topic]:
|
||||||
return await followed_reactions(slug)
|
return await followed_reactions(user_id)
|
||||||
|
|
||||||
|
|
||||||
async def followed_reactions(slug):
|
async def followed_reactions(user_id):
|
||||||
with local_session() as session:
|
with local_session() as session:
|
||||||
user = session.query(User).where(User.slug == slug).first()
|
user = session.query(User).where(User.id == user_id).first()
|
||||||
return session.query(
|
return session.query(
|
||||||
Reaction.shout
|
Reaction.shout
|
||||||
).where(
|
).where(
|
||||||
|
@ -104,31 +104,26 @@ async def followed_reactions(slug):
|
||||||
|
|
||||||
@query.field("userFollowedTopics")
|
@query.field("userFollowedTopics")
|
||||||
@login_required
|
@login_required
|
||||||
async def get_followed_topics(_, info, slug) -> List[Topic]:
|
async def get_followed_topics(_, info, user_id) -> List[Topic]:
|
||||||
return await followed_topics(slug)
|
return await followed_topics(user_id)
|
||||||
|
|
||||||
|
|
||||||
async def followed_topics(slug):
|
async def followed_topics(user_id):
|
||||||
return followed_by_user(slug)
|
return followed_by_user(user_id)
|
||||||
|
|
||||||
|
|
||||||
@query.field("userFollowedAuthors")
|
@query.field("userFollowedAuthors")
|
||||||
async def get_followed_authors(_, _info, slug) -> List[User]:
|
async def get_followed_authors(_, _info, user_id: int) -> List[User]:
|
||||||
return await followed_authors(slug)
|
return await followed_authors(user_id)
|
||||||
|
|
||||||
|
|
||||||
async def followed_authors(slug):
|
async def followed_authors(user_id):
|
||||||
with local_session() as session:
|
q = select(User)
|
||||||
user = session.query(User).where(User.slug == slug).first()
|
q = add_author_stat_columns(q)
|
||||||
q = select(User)
|
q = q.join(AuthorFollower, AuthorFollower.author == User.id).where(
|
||||||
q = add_author_stat_columns(q)
|
AuthorFollower.follower == user_id
|
||||||
aliased_user = aliased(User)
|
)
|
||||||
q = q.join(AuthorFollower, AuthorFollower.author == user.id).join(
|
return get_authors_from_query(q)
|
||||||
aliased_user, aliased_user.id == AuthorFollower.follower
|
|
||||||
).where(
|
|
||||||
aliased_user.slug == slug
|
|
||||||
)
|
|
||||||
return get_authors_from_query(q)
|
|
||||||
|
|
||||||
|
|
||||||
@query.field("userFollowers")
|
@query.field("userFollowers")
|
||||||
|
@ -157,25 +152,16 @@ async def get_user_roles(slug):
|
||||||
.all()
|
.all()
|
||||||
)
|
)
|
||||||
|
|
||||||
return roles
|
return [] # roles
|
||||||
|
|
||||||
|
|
||||||
@mutation.field("updateProfile")
|
@mutation.field("updateProfile")
|
||||||
@login_required
|
@login_required
|
||||||
async def update_profile(_, info, profile):
|
async def update_profile(_, info, profile):
|
||||||
print('[zine] update_profile')
|
|
||||||
print(profile)
|
|
||||||
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:
|
||||||
session.query(User).filter(User.id == user_id).update({
|
session.query(User).filter(User.id == user_id).update(profile)
|
||||||
"name": profile['name'],
|
|
||||||
"slug": profile['slug'],
|
|
||||||
"bio": profile['bio'],
|
|
||||||
"userpic": profile['userpic'],
|
|
||||||
"about": profile['about'],
|
|
||||||
"links": profile['links']
|
|
||||||
})
|
|
||||||
session.commit()
|
session.commit()
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
@ -183,11 +169,12 @@ async def update_profile(_, info, profile):
|
||||||
@mutation.field("rateUser")
|
@mutation.field("rateUser")
|
||||||
@login_required
|
@login_required
|
||||||
async def rate_user(_, info, rated_userslug, value):
|
async def rate_user(_, info, rated_userslug, value):
|
||||||
user = info.context["request"].user
|
auth: AuthCredentials = info.context["request"].auth
|
||||||
|
|
||||||
with local_session() as session:
|
with local_session() as session:
|
||||||
rating = (
|
rating = (
|
||||||
session.query(UserRating)
|
session.query(UserRating)
|
||||||
.filter(and_(UserRating.rater == user.slug, UserRating.user == rated_userslug))
|
.filter(and_(UserRating.rater == auth.user_id, UserRating.user == rated_userslug))
|
||||||
.first()
|
.first()
|
||||||
)
|
)
|
||||||
if rating:
|
if rating:
|
||||||
|
@ -195,30 +182,30 @@ async def rate_user(_, info, rated_userslug, value):
|
||||||
session.commit()
|
session.commit()
|
||||||
return {}
|
return {}
|
||||||
try:
|
try:
|
||||||
UserRating.create(rater=user.slug, user=rated_userslug, value=value)
|
UserRating.create(rater=auth.user_id, user=rated_userslug, value=value)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
return {"error": err}
|
return {"error": err}
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
# for mutation.field("follow")
|
# for mutation.field("follow")
|
||||||
def author_follow(user, slug):
|
def author_follow(user_id, slug):
|
||||||
with local_session() as session:
|
with local_session() as session:
|
||||||
author = session.query(User).where(User.slug == slug).one()
|
author = session.query(User).where(User.slug == slug).one()
|
||||||
af = AuthorFollower.create(follower=user.id, author=author.id)
|
af = AuthorFollower.create(follower=user_id, author=author.id)
|
||||||
session.add(af)
|
session.add(af)
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
|
|
||||||
# for mutation.field("unfollow")
|
# for mutation.field("unfollow")
|
||||||
def author_unfollow(user, slug):
|
def author_unfollow(user_id, slug):
|
||||||
with local_session() as session:
|
with local_session() as session:
|
||||||
flw = (
|
flw = (
|
||||||
session.query(
|
session.query(
|
||||||
AuthorFollower
|
AuthorFollower
|
||||||
).join(User, User.id == AuthorFollower.author).filter(
|
).join(User, User.id == AuthorFollower.author).filter(
|
||||||
and_(
|
and_(
|
||||||
AuthorFollower.follower == user.id, User.slug == slug
|
AuthorFollower.follower == user_id, User.slug == slug
|
||||||
)
|
)
|
||||||
).first()
|
).first()
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,6 +2,7 @@ from datetime import datetime, timedelta, timezone
|
||||||
from sqlalchemy import and_, asc, desc, select, text, func
|
from sqlalchemy import and_, asc, desc, select, text, func
|
||||||
from sqlalchemy.orm import aliased
|
from sqlalchemy.orm import aliased
|
||||||
from auth.authenticate import login_required
|
from auth.authenticate import login_required
|
||||||
|
from auth.credentials import AuthCredentials
|
||||||
from base.orm import local_session
|
from base.orm import local_session
|
||||||
from base.resolvers import mutation, query
|
from base.resolvers import mutation, query
|
||||||
from orm.reaction import Reaction, ReactionKind
|
from orm.reaction import Reaction, ReactionKind
|
||||||
|
@ -14,20 +15,20 @@ def add_reaction_stat_columns(q):
|
||||||
return add_common_stat_columns(q)
|
return add_common_stat_columns(q)
|
||||||
|
|
||||||
|
|
||||||
def reactions_follow(user: User, slug: str, auto=False):
|
def reactions_follow(user_id, slug: str, auto=False):
|
||||||
with local_session() as session:
|
with local_session() as session:
|
||||||
shout = session.query(Shout).where(Shout.slug == slug).one()
|
shout = session.query(Shout).where(Shout.slug == slug).one()
|
||||||
|
|
||||||
following = (
|
following = (
|
||||||
session.query(ShoutReactionsFollower).where(and_(
|
session.query(ShoutReactionsFollower).where(and_(
|
||||||
ShoutReactionsFollower.follower == user.id,
|
ShoutReactionsFollower.follower == user_id,
|
||||||
ShoutReactionsFollower.shout == shout.id,
|
ShoutReactionsFollower.shout == shout.id,
|
||||||
)).first()
|
)).first()
|
||||||
)
|
)
|
||||||
|
|
||||||
if not following:
|
if not following:
|
||||||
following = ShoutReactionsFollower.create(
|
following = ShoutReactionsFollower.create(
|
||||||
follower=user.id,
|
follower=user_id,
|
||||||
shout=shout.id,
|
shout=shout.id,
|
||||||
auto=auto
|
auto=auto
|
||||||
)
|
)
|
||||||
|
@ -35,13 +36,13 @@ def reactions_follow(user: User, slug: str, auto=False):
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
|
|
||||||
def reactions_unfollow(user, slug):
|
def reactions_unfollow(user_id, slug):
|
||||||
with local_session() as session:
|
with local_session() as session:
|
||||||
shout = session.query(Shout).where(Shout.slug == slug).one()
|
shout = session.query(Shout).where(Shout.slug == slug).one()
|
||||||
|
|
||||||
following = (
|
following = (
|
||||||
session.query(ShoutReactionsFollower).where(and_(
|
session.query(ShoutReactionsFollower).where(and_(
|
||||||
ShoutReactionsFollower.follower == user.id,
|
ShoutReactionsFollower.follower == user_id,
|
||||||
ShoutReactionsFollower.shout == shout.id
|
ShoutReactionsFollower.shout == shout.id
|
||||||
)).first()
|
)).first()
|
||||||
)
|
)
|
||||||
|
@ -51,12 +52,12 @@ def reactions_unfollow(user, slug):
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
|
|
||||||
def is_published_author(session, userslug):
|
def is_published_author(session, user_id):
|
||||||
''' checks if user has at least one publication '''
|
''' checks if user has at least one publication '''
|
||||||
return session.query(
|
return session.query(
|
||||||
Shout
|
Shout
|
||||||
).where(
|
).where(
|
||||||
Shout.authors.contains(userslug)
|
Shout.authors.contains(user_id)
|
||||||
).filter(
|
).filter(
|
||||||
and_(
|
and_(
|
||||||
Shout.publishedAt.is_not(None),
|
Shout.publishedAt.is_not(None),
|
||||||
|
@ -65,17 +66,17 @@ def is_published_author(session, userslug):
|
||||||
).count() > 0
|
).count() > 0
|
||||||
|
|
||||||
|
|
||||||
def check_to_publish(session, user, reaction):
|
def check_to_publish(session, user_id, reaction):
|
||||||
''' set shout to public if publicated approvers amount > 4 '''
|
''' set shout to public if publicated approvers amount > 4 '''
|
||||||
if not reaction.replyTo and reaction.kind in [
|
if not reaction.replyTo and reaction.kind in [
|
||||||
ReactionKind.ACCEPT,
|
ReactionKind.ACCEPT,
|
||||||
ReactionKind.LIKE,
|
ReactionKind.LIKE,
|
||||||
ReactionKind.PROOF
|
ReactionKind.PROOF
|
||||||
]:
|
]:
|
||||||
if is_published_author(user):
|
if is_published_author(user_id):
|
||||||
# now count how many approvers are voted already
|
# now count how many approvers are voted already
|
||||||
approvers_reactions = session.query(Reaction).where(Reaction.shout == reaction.shout).all()
|
approvers_reactions = session.query(Reaction).where(Reaction.shout == reaction.shout).all()
|
||||||
approvers = [user.slug, ]
|
approvers = [user_id, ]
|
||||||
for ar in approvers_reactions:
|
for ar in approvers_reactions:
|
||||||
a = ar.createdBy
|
a = ar.createdBy
|
||||||
if is_published_author(session, a):
|
if is_published_author(session, a):
|
||||||
|
@ -85,7 +86,7 @@ def check_to_publish(session, user, reaction):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def check_to_hide(session, user, reaction):
|
def check_to_hide(session, user_id, reaction):
|
||||||
''' hides any shout if 20% of reactions are negative '''
|
''' hides any shout if 20% of reactions are negative '''
|
||||||
if not reaction.replyTo and reaction.kind in [
|
if not reaction.replyTo and reaction.kind in [
|
||||||
ReactionKind.DECLINE,
|
ReactionKind.DECLINE,
|
||||||
|
@ -107,8 +108,8 @@ def check_to_hide(session, user, reaction):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def set_published(session, slug, publisher):
|
def set_published(session, shout_id, publisher):
|
||||||
s = session.query(Shout).where(Shout.slug == slug).first()
|
s = session.query(Shout).where(Shout.id == shout_id).first()
|
||||||
s.publishedAt = datetime.now(tz=timezone.utc)
|
s.publishedAt = datetime.now(tz=timezone.utc)
|
||||||
s.publishedBy = publisher
|
s.publishedBy = publisher
|
||||||
s.visibility = text('public')
|
s.visibility = text('public')
|
||||||
|
@ -116,8 +117,8 @@ def set_published(session, slug, publisher):
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
|
|
||||||
def set_hidden(session, slug):
|
def set_hidden(session, shout_id):
|
||||||
s = session.query(Shout).where(Shout.slug == slug).first()
|
s = session.query(Shout).where(Shout.id == shout_id).first()
|
||||||
s.visibility = text('authors')
|
s.visibility = text('authors')
|
||||||
s.publishedAt = None # TODO: discuss
|
s.publishedAt = None # TODO: discuss
|
||||||
s.publishedBy = None # TODO: store changes history in git
|
s.publishedBy = None # TODO: store changes history in git
|
||||||
|
@ -128,7 +129,7 @@ def set_hidden(session, slug):
|
||||||
@mutation.field("createReaction")
|
@mutation.field("createReaction")
|
||||||
@login_required
|
@login_required
|
||||||
async def create_reaction(_, info, inp):
|
async def create_reaction(_, info, inp):
|
||||||
user = info.context["request"].user
|
auth: AuthCredentials = info.context["request"].auth
|
||||||
|
|
||||||
with local_session() as session:
|
with local_session() as session:
|
||||||
reaction = Reaction.create(**inp)
|
reaction = Reaction.create(**inp)
|
||||||
|
@ -137,13 +138,13 @@ async def create_reaction(_, info, inp):
|
||||||
|
|
||||||
# self-regulation mechanics
|
# self-regulation mechanics
|
||||||
|
|
||||||
if check_to_hide(session, user, reaction):
|
if check_to_hide(session, auth.user_id, reaction):
|
||||||
set_hidden(session, reaction.shout)
|
set_hidden(session, reaction.shout)
|
||||||
elif check_to_publish(session, user, reaction):
|
elif check_to_publish(session, auth.user_id, reaction):
|
||||||
set_published(session, reaction.shout, reaction.createdBy)
|
set_published(session, reaction.shout, reaction.createdBy)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
reactions_follow(user, inp["shout"], True)
|
reactions_follow(auth.user_id, inp["shout"], True)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[resolvers.reactions] error on reactions autofollowing: {e}")
|
print(f"[resolvers.reactions] error on reactions autofollowing: {e}")
|
||||||
|
|
||||||
|
@ -158,11 +159,10 @@ async def create_reaction(_, info, inp):
|
||||||
@mutation.field("updateReaction")
|
@mutation.field("updateReaction")
|
||||||
@login_required
|
@login_required
|
||||||
async def update_reaction(_, info, inp):
|
async def update_reaction(_, info, inp):
|
||||||
auth = info.context["request"].auth
|
auth: AuthCredentials = info.context["request"].auth
|
||||||
user_id = auth.user_id
|
|
||||||
|
|
||||||
with local_session() as session:
|
with local_session() as session:
|
||||||
user = session.query(User).where(User.id == user_id).first()
|
user = session.query(User).where(User.id == auth.user_id).first()
|
||||||
q = select(Reaction).filter(Reaction.id == inp.id)
|
q = select(Reaction).filter(Reaction.id == inp.id)
|
||||||
q = add_reaction_stat_columns(q)
|
q = add_reaction_stat_columns(q)
|
||||||
|
|
||||||
|
@ -193,10 +193,10 @@ async def update_reaction(_, info, inp):
|
||||||
@mutation.field("deleteReaction")
|
@mutation.field("deleteReaction")
|
||||||
@login_required
|
@login_required
|
||||||
async def delete_reaction(_, info, rid):
|
async def delete_reaction(_, info, rid):
|
||||||
auth = info.context["request"].auth
|
auth: AuthCredentials = info.context["request"].auth
|
||||||
user_id = auth.user_id
|
|
||||||
with local_session() as session:
|
with local_session() as session:
|
||||||
user = session.query(User).where(User.id == user_id).first()
|
user = session.query(User).where(User.id == auth.user_id).first()
|
||||||
reaction = session.query(Reaction).filter(Reaction.id == rid).first()
|
reaction = session.query(Reaction).filter(Reaction.id == rid).first()
|
||||||
if not reaction:
|
if not reaction:
|
||||||
return {"error": "invalid reaction id"}
|
return {"error": "invalid reaction id"}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
from sqlalchemy import and_, select, distinct, func
|
from sqlalchemy import and_, select, distinct, func
|
||||||
|
from sqlalchemy.orm import aliased
|
||||||
|
|
||||||
from auth.authenticate import login_required
|
from auth.authenticate import login_required
|
||||||
from base.orm import local_session
|
from base.orm import local_session
|
||||||
from base.resolvers import mutation, query
|
from base.resolvers import mutation, query
|
||||||
|
@ -8,16 +10,20 @@ from orm import Shout, User
|
||||||
|
|
||||||
|
|
||||||
def add_topic_stat_columns(q):
|
def add_topic_stat_columns(q):
|
||||||
q = q.outerjoin(ShoutTopic, Topic.id == ShoutTopic.topic).add_columns(
|
aliased_shout_topic = aliased(ShoutTopic)
|
||||||
func.count(distinct(ShoutTopic.shout)).label('shouts_stat')
|
aliased_shout_author = aliased(ShoutAuthor)
|
||||||
).outerjoin(ShoutAuthor, ShoutTopic.shout == ShoutAuthor.shout).add_columns(
|
aliased_topic_follower = aliased(TopicFollower)
|
||||||
func.count(distinct(ShoutAuthor.user)).label('authors_stat')
|
|
||||||
).outerjoin(TopicFollower,
|
q = q.outerjoin(aliased_shout_topic, Topic.id == aliased_shout_topic.topic).add_columns(
|
||||||
|
func.count(distinct(aliased_shout_topic.shout)).label('shouts_stat')
|
||||||
|
).outerjoin(aliased_shout_author, aliased_shout_topic.shout == aliased_shout_author.shout).add_columns(
|
||||||
|
func.count(distinct(aliased_shout_author.user)).label('authors_stat')
|
||||||
|
).outerjoin(aliased_topic_follower,
|
||||||
and_(
|
and_(
|
||||||
TopicFollower.topic == Topic.id,
|
aliased_topic_follower.topic == Topic.id,
|
||||||
TopicFollower.follower == ShoutAuthor.id
|
aliased_topic_follower.follower == aliased_shout_author.id
|
||||||
)).add_columns(
|
)).add_columns(
|
||||||
func.count(distinct(TopicFollower.follower)).label('followers_stat')
|
func.count(distinct(aliased_topic_follower.follower)).label('followers_stat')
|
||||||
)
|
)
|
||||||
|
|
||||||
q = q.group_by(Topic.id)
|
q = q.group_by(Topic.id)
|
||||||
|
@ -46,10 +52,10 @@ def get_topics_from_query(q):
|
||||||
return topics
|
return topics
|
||||||
|
|
||||||
|
|
||||||
def followed_by_user(user_slug):
|
def followed_by_user(user_id):
|
||||||
q = select(Topic)
|
q = select(Topic)
|
||||||
q = add_topic_stat_columns(q)
|
q = add_topic_stat_columns(q)
|
||||||
q = q.join(User).where(User.slug == user_slug)
|
q = q.join(TopicFollower).where(TopicFollower.follower == user_id)
|
||||||
|
|
||||||
return get_topics_from_query(q)
|
return get_topics_from_query(q)
|
||||||
|
|
||||||
|
@ -115,21 +121,21 @@ async def update_topic(_, _info, inp):
|
||||||
return {"topic": topic}
|
return {"topic": topic}
|
||||||
|
|
||||||
|
|
||||||
async def topic_follow(user, slug):
|
def topic_follow(user_id, slug):
|
||||||
with local_session() as session:
|
with local_session() as session:
|
||||||
topic = session.query(Topic).where(Topic.slug == slug).one()
|
topic = session.query(Topic).where(Topic.slug == slug).one()
|
||||||
|
|
||||||
following = TopicFollower.create(topic=topic.id, follower=user.id)
|
following = TopicFollower.create(topic=topic.id, follower=user_id)
|
||||||
session.add(following)
|
session.add(following)
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
|
|
||||||
async def topic_unfollow(user, slug):
|
def topic_unfollow(user_id, slug):
|
||||||
with local_session() as session:
|
with local_session() as session:
|
||||||
sub = (
|
sub = (
|
||||||
session.query(TopicFollower).join(Topic).filter(
|
session.query(TopicFollower).join(Topic).filter(
|
||||||
and_(
|
and_(
|
||||||
TopicFollower.follower == user.id,
|
TopicFollower.follower == user_id,
|
||||||
Topic.slug == slug
|
Topic.slug == slug
|
||||||
)
|
)
|
||||||
).first()
|
).first()
|
||||||
|
@ -145,7 +151,7 @@ async def topic_unfollow(user, slug):
|
||||||
async def topics_random(_, info, amount=12):
|
async def topics_random(_, info, amount=12):
|
||||||
q = select(Topic)
|
q = select(Topic)
|
||||||
q = add_topic_stat_columns(q)
|
q = add_topic_stat_columns(q)
|
||||||
q = q.join(Shout, ShoutTopic.shout == Shout.id).group_by(Topic.id).having(func.count(Shout.id) > 2)
|
q = q.join(ShoutTopic).join(Shout, ShoutTopic.shout == Shout.id).group_by(Topic.id).having(func.count(Shout.id) > 2)
|
||||||
q = q.order_by(func.random()).limit(amount)
|
q = q.order_by(func.random()).limit(amount)
|
||||||
|
|
||||||
return get_topics_from_query(q)
|
return get_topics_from_query(q)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user