Merge remote-tracking branch 'origin/main' into storages-to-qeuries
This commit is contained in:
@@ -49,8 +49,8 @@ from resolvers.zine.load import (
|
||||
from resolvers.inbox.chats import (
|
||||
create_chat,
|
||||
delete_chat,
|
||||
update_chat,
|
||||
invite_to_chat
|
||||
update_chat
|
||||
|
||||
)
|
||||
from resolvers.inbox.messages import (
|
||||
create_message,
|
||||
@@ -112,7 +112,6 @@ __all__ = [
|
||||
# inbox
|
||||
"load_chats",
|
||||
"load_messages_by",
|
||||
"invite_to_chat",
|
||||
"create_chat",
|
||||
"delete_chat",
|
||||
"update_chat",
|
||||
|
@@ -13,12 +13,12 @@ from auth.identity import Identity, Password
|
||||
from auth.jwtcodec import JWTCodec
|
||||
from auth.tokenstorage import TokenStorage
|
||||
from base.exceptions import (BaseHttpException, InvalidPassword, InvalidToken,
|
||||
ObjectNotExist, OperationNotAllowed)
|
||||
ObjectNotExist, OperationNotAllowed, Unauthorized)
|
||||
from base.orm import local_session
|
||||
from base.resolvers import mutation, query
|
||||
from orm import Role, User
|
||||
from resolvers.zine.profile import user_subscriptions
|
||||
from settings import SESSION_TOKEN_HEADER
|
||||
from settings import SESSION_TOKEN_HEADER, FRONTEND_URL
|
||||
|
||||
|
||||
@mutation.field("getSession")
|
||||
@@ -37,7 +37,7 @@ async def get_current_user(_, info):
|
||||
"news": await user_subscriptions(user.slug),
|
||||
}
|
||||
else:
|
||||
raise OperationNotAllowed("No session token present in request, try to login")
|
||||
raise Unauthorized("No session token present in request, try to login")
|
||||
|
||||
|
||||
@mutation.field("confirmEmail")
|
||||
@@ -75,7 +75,7 @@ async def confirm_email_handler(request):
|
||||
if "error" in res:
|
||||
raise BaseHttpException(res['error'])
|
||||
else:
|
||||
response = RedirectResponse(url="https://new.discours.io")
|
||||
response = RedirectResponse(url=FRONTEND_URL)
|
||||
response.set_cookie("token", res["token"]) # session token
|
||||
return response
|
||||
|
||||
@@ -133,7 +133,7 @@ async def register_by_email(_, _info, email: str, password: str = "", name: str
|
||||
|
||||
|
||||
@mutation.field("sendLink")
|
||||
async def auth_send_link(_, _info, email, lang="ru"):
|
||||
async def auth_send_link(_, _info, email, lang="ru", template="email_confirmation"):
|
||||
"""send link with confirm code to email"""
|
||||
with local_session() as session:
|
||||
user = session.query(User).filter(User.email == email).first()
|
||||
@@ -141,7 +141,7 @@ async def auth_send_link(_, _info, email, lang="ru"):
|
||||
raise ObjectNotExist("User not found")
|
||||
else:
|
||||
token = await TokenStorage.create_onetime(user)
|
||||
await send_auth_email(user, token, lang)
|
||||
await send_auth_email(user, token, lang, template)
|
||||
return user
|
||||
|
||||
|
||||
|
@@ -7,43 +7,6 @@ from base.redis import redis
|
||||
from base.resolvers import mutation
|
||||
|
||||
|
||||
async def add_user_to_chat(user_slug: str, chat_id: str, chat=None):
|
||||
for member in chat["users"]:
|
||||
chats_ids = await redis.execute("GET", f"chats_by_user/{member}")
|
||||
if chats_ids:
|
||||
chats_ids = list(json.loads(chats_ids))
|
||||
else:
|
||||
chats_ids = []
|
||||
if chat_id not in chats_ids:
|
||||
chats_ids.append(chat_id)
|
||||
await redis.execute("SET", f"chats_by_user/{member}", json.dumps(chats_ids))
|
||||
|
||||
|
||||
@mutation.field("inviteChat")
|
||||
async def invite_to_chat(_, info, invited: str, chat_id: str):
|
||||
''' invite user with :slug to chat with :chat_id '''
|
||||
user = info.context["request"].user
|
||||
chat = await redis.execute("GET", f"chats/{chat_id}")
|
||||
if not chat:
|
||||
return {
|
||||
"error": "chat not exist"
|
||||
}
|
||||
chat = dict(json.loads(chat))
|
||||
if not chat['private'] and user.slug not in chat['admins']:
|
||||
return {
|
||||
"error": "only admins can invite to private chat",
|
||||
"chat": chat
|
||||
}
|
||||
else:
|
||||
chat["users"].append(invited)
|
||||
await add_user_to_chat(user.slug, chat_id, chat)
|
||||
await redis.execute("SET", f"chats/{chat_id}", json.dumps(chat))
|
||||
return {
|
||||
"error": None,
|
||||
"chat": chat
|
||||
}
|
||||
|
||||
|
||||
@mutation.field("updateChat")
|
||||
@login_required
|
||||
async def update_chat(_, info, chat_new: dict):
|
||||
@@ -68,12 +31,11 @@ async def update_chat(_, info, chat_new: dict):
|
||||
"title": chat_new.get("title", chat["title"]),
|
||||
"description": chat_new.get("description", chat["description"]),
|
||||
"updatedAt": int(datetime.now(tz=timezone.utc).timestamp()),
|
||||
"admins": chat_new.get("admins", chat["admins"]),
|
||||
"admins": chat_new.get("admins", chat.get("admins") or []),
|
||||
"users": chat_new.get("users", chat["users"])
|
||||
})
|
||||
await add_user_to_chat(user.slug, chat_id, chat)
|
||||
await redis.execute("SET", f"chats/{chat.id}", json.dumps(chat))
|
||||
await redis.execute("SET", f"chats/{chat.id}/next_message_id", 0)
|
||||
await redis.execute("COMMIT")
|
||||
|
||||
return {
|
||||
"error": None,
|
||||
@@ -85,23 +47,43 @@ async def update_chat(_, info, chat_new: dict):
|
||||
@login_required
|
||||
async def create_chat(_, info, title="", members=[]):
|
||||
user = info.context["request"].user
|
||||
chat_id = str(uuid.uuid4())
|
||||
chat = {}
|
||||
if user.slug not in members:
|
||||
members.append(user.slug)
|
||||
|
||||
# reuse chat craeted before if exists
|
||||
if len(members) == 2 and title == "":
|
||||
chats1 = await redis.execute("SMEMBERS", f"chats_by_user/{members[0].slug}")
|
||||
chats2 = await redis.execute("SMEMBERS", f"chats_by_user/{members[1].slug}")
|
||||
chat = None
|
||||
for c in chats1.intersection(chats2):
|
||||
chat = await redis.execute("GET", f"chats/{c.decode('utf-8')}")
|
||||
if chat:
|
||||
chat = json.loads(chat)
|
||||
if chat.title == "":
|
||||
break
|
||||
if chat:
|
||||
return {
|
||||
"chat": chat,
|
||||
"error": "existed"
|
||||
}
|
||||
|
||||
chat_id = str(uuid.uuid4())
|
||||
chat = {
|
||||
"title": title,
|
||||
"createdAt": int(datetime.now(tz=timezone.utc).timestamp()),
|
||||
"updatedAt": int(datetime.now(tz=timezone.utc).timestamp()),
|
||||
"createdBy": user.slug,
|
||||
"id": chat_id,
|
||||
"users": members,
|
||||
"admins": [user.slug, ]
|
||||
"title": title,
|
||||
"createdBy": user.slug,
|
||||
"createdAt": int(datetime.now(tz=timezone.utc).timestamp()),
|
||||
"updatedAt": int(datetime.now(tz=timezone.utc).timestamp()),
|
||||
"admins": []
|
||||
}
|
||||
|
||||
await add_user_to_chat(user.slug, chat_id, chat)
|
||||
for m in members:
|
||||
await redis.execute("SADD", f"chats_by_user/{m}", chat_id)
|
||||
await redis.execute("SET", f"chats/{chat_id}", json.dumps(chat))
|
||||
await redis.execute("SET", f"chats/{chat_id}/next_message_id", str(0))
|
||||
|
||||
await redis.execute("COMMIT")
|
||||
return {
|
||||
"error": None,
|
||||
"chat": chat
|
||||
@@ -117,6 +99,8 @@ async def delete_chat(_, info, chat_id: str):
|
||||
chat = dict(json.loads(chat))
|
||||
if user.slug in chat['admins']:
|
||||
await redis.execute("DEL", f"chats/{chat_id}")
|
||||
await redis.execute("SREM", "chats_by_user/" + user, chat_id)
|
||||
await redis.execute("COMMIT")
|
||||
else:
|
||||
return {
|
||||
"error": "chat not exist"
|
||||
|
@@ -5,89 +5,120 @@ from auth.authenticate import login_required
|
||||
from base.redis import redis
|
||||
from base.orm import local_session
|
||||
from base.resolvers import query
|
||||
from base.exceptions import ObjectNotExist, Unauthorized
|
||||
from orm.user import User
|
||||
from resolvers.zine.profile import followed_authors
|
||||
from .unread import get_unread_counter
|
||||
|
||||
|
||||
async def load_messages(chatId: str, limit: int, offset: int):
|
||||
''' load :limit messages for :chatId with :offset '''
|
||||
async def load_messages(chat_id: str, limit: int, offset: int):
|
||||
''' load :limit messages for :chat_id with :offset '''
|
||||
messages = []
|
||||
message_ids = await redis.lrange(
|
||||
f"chats/{chatId}/message_ids", 0 - offset - limit, 0 - offset
|
||||
f"chats/{chat_id}/message_ids", offset + limit, offset
|
||||
)
|
||||
if message_ids:
|
||||
message_keys = [
|
||||
f"chats/{chatId}/messages/{mid}" for mid in message_ids
|
||||
f"chats/{chat_id}/messages/{mid}" for mid in message_ids
|
||||
]
|
||||
messages = await redis.mget(*message_keys)
|
||||
messages = [json.loads(msg) for msg in messages]
|
||||
return {
|
||||
"messages": messages,
|
||||
"error": None
|
||||
}
|
||||
return messages
|
||||
|
||||
|
||||
@query.field("loadChats")
|
||||
@login_required
|
||||
async def load_chats(_, info, limit: int, offset: int):
|
||||
async def load_chats(_, info, limit: int = 50, offset: int = 0):
|
||||
""" load :limit chats of current user with :offset """
|
||||
user = info.context["request"].user
|
||||
if user:
|
||||
chats = await redis.execute("GET", f"chats_by_user/{user.slug}")
|
||||
if chats:
|
||||
chats = list(json.loads(chats))[offset:offset + limit]
|
||||
if not chats:
|
||||
chats = []
|
||||
for c in chats:
|
||||
c['messages'] = await load_messages(c['id'], limit, offset)
|
||||
c['unread'] = await get_unread_counter(c['id'], user.slug)
|
||||
return {
|
||||
"chats": chats,
|
||||
"error": None
|
||||
}
|
||||
print('[inbox] load user\'s chats %s' % user.slug)
|
||||
else:
|
||||
return {
|
||||
"error": "please login",
|
||||
"chats": []
|
||||
}
|
||||
raise Unauthorized("Please login to load chats")
|
||||
cids = await redis.execute("SMEMBERS", "chats_by_user/" + user.slug)
|
||||
if cids:
|
||||
cids = list(cids)[offset:offset + limit]
|
||||
if not cids:
|
||||
print('[inbox.load] no chats were found')
|
||||
cids = []
|
||||
chats = []
|
||||
for cid in cids:
|
||||
c = await redis.execute("GET", "chats/" + cid.decode("utf-8"))
|
||||
if c:
|
||||
c = dict(json.loads(c))
|
||||
c['messages'] = await load_messages(cid, 5, 0)
|
||||
c['unread'] = await get_unread_counter(cid, user.slug)
|
||||
with local_session() as session:
|
||||
c['members'] = []
|
||||
for userslug in c["users"]:
|
||||
a = session.query(User).where(User.slug == userslug).first().dict()
|
||||
c['members'].append({
|
||||
"slug": userslug,
|
||||
"userpic": a["userpic"],
|
||||
"name": a["name"],
|
||||
"lastSeen": a["lastSeen"],
|
||||
})
|
||||
chats.append(c)
|
||||
return {
|
||||
"chats": chats,
|
||||
"error": None
|
||||
}
|
||||
|
||||
|
||||
async def search_user_chats(by, messages: set, slug: str, limit, offset):
|
||||
cids = set([])
|
||||
by_author = by.get('author')
|
||||
body_like = by.get('body')
|
||||
cids.unioin(set(await redis.execute("SMEMBERS", "chats_by_user/" + slug)))
|
||||
if by_author:
|
||||
# all author's messages
|
||||
cids.union(set(await redis.execute("SMEMBERS", f"chats_by_user/{by_author}")))
|
||||
# author's messages in filtered chat
|
||||
messages.union(set(filter(lambda m: m["author"] == by_author, list(messages))))
|
||||
for c in cids:
|
||||
messages.union(set(await load_messages(c, limit, offset)))
|
||||
if body_like:
|
||||
# search in all messages in all user's chats
|
||||
for c in cids:
|
||||
# FIXME: user redis scan here
|
||||
mmm = set(await load_messages(c, limit, offset))
|
||||
for m in mmm:
|
||||
if body_like in m["body"]:
|
||||
messages.add(m)
|
||||
else:
|
||||
# search in chat's messages
|
||||
messages.union(set(filter(lambda m: body_like in m["body"], list(messages))))
|
||||
return messages
|
||||
|
||||
|
||||
@query.field("loadMessagesBy")
|
||||
@login_required
|
||||
async def load_messages_by(_, info, by, limit: int = 50, offset: int = 0):
|
||||
''' load :amolimitunt messages of :chat_id with :offset '''
|
||||
user = info.context["request"].user
|
||||
my_chats = await redis.execute("GET", f"chats_by_user/{user.slug}")
|
||||
chat_id = by.get('chat')
|
||||
if chat_id:
|
||||
chat = await redis.execute("GET", f"chats/{chat_id}")
|
||||
async def load_messages_by(_, info, by, limit: int = 10, offset: int = 0):
|
||||
''' load :limit messages of :chat_id with :offset '''
|
||||
messages = set([])
|
||||
by_chat = by.get('chat')
|
||||
if by_chat:
|
||||
chat = await redis.execute("GET", f"chats/{by_chat}")
|
||||
if not chat:
|
||||
return {
|
||||
"error": "chat not exist"
|
||||
}
|
||||
messages = await load_messages(chat_id, limit, offset)
|
||||
user_id = by.get('author')
|
||||
if user_id:
|
||||
chats = await redis.execute("GET", f"chats_by_user/{user_id}")
|
||||
our_chats = list(set(chats) & set(my_chats))
|
||||
for c in our_chats:
|
||||
messages += await load_messages(c, limit, offset)
|
||||
body_like = by.get('body')
|
||||
if body_like:
|
||||
for c in my_chats:
|
||||
mmm = await load_messages(c, limit, offset)
|
||||
for m in mmm:
|
||||
if body_like in m["body"]:
|
||||
messages.append(m)
|
||||
raise ObjectNotExist("Chat not exists")
|
||||
# everyone's messages in filtered chat
|
||||
messages.union(set(await load_messages(by_chat, limit, offset)))
|
||||
|
||||
user = info.context["request"].user
|
||||
if user and len(messages) == 0:
|
||||
messages.union(search_user_chats(by, messages, user.slug, limit, offset))
|
||||
|
||||
days = by.get("days")
|
||||
if days:
|
||||
messages = filter(
|
||||
messages.union(set(filter(
|
||||
lambda m: datetime.now(tz=timezone.utc) - int(m["createdAt"]) < timedelta(days=by.get("days")),
|
||||
messages
|
||||
)
|
||||
list(messages)
|
||||
)))
|
||||
return {
|
||||
"messages": messages,
|
||||
"messages": sorted(
|
||||
lambda m: m.createdAt,
|
||||
list(messages)
|
||||
),
|
||||
"error": None
|
||||
}
|
||||
|
||||
|
@@ -17,6 +17,6 @@ async def get_total_unread_counter(user_slug: str):
|
||||
if chats:
|
||||
chats = json.loads(chats)
|
||||
for chat_id in chats:
|
||||
n = await get_unread_counter(chat_id, user_slug)
|
||||
n = await get_unread_counter(chat_id.decode('utf-8'), user_slug)
|
||||
unread += n
|
||||
return unread
|
||||
|
@@ -44,8 +44,11 @@ def apply_filters(q, filters, user=None):
|
||||
filters = {} if filters is None else filters
|
||||
if filters.get("reacted") and user:
|
||||
q.join(Reaction, Reaction.createdBy == user.slug)
|
||||
if filters.get("visibility"):
|
||||
v = filters.get("visibility")
|
||||
if v == "public":
|
||||
q = q.filter(Shout.visibility == filters.get("visibility"))
|
||||
if v == "community":
|
||||
q = q.filter(Shout.visibility.in_(["public", "community"]))
|
||||
if filters.get("layout"):
|
||||
q = q.filter(Shout.layout == filters.get("layout"))
|
||||
if filters.get("author"):
|
||||
@@ -74,7 +77,6 @@ def add_stat_columns(q):
|
||||
async def load_shout(_, info, slug):
|
||||
with local_session() as session:
|
||||
q = select(Shout).options(
|
||||
# TODO add cation
|
||||
joinedload(Shout.authors),
|
||||
joinedload(Shout.topics),
|
||||
)
|
||||
|
@@ -13,21 +13,18 @@ from orm.user import AuthorFollower, Role, User, UserRating, UserRole
|
||||
|
||||
# from .community import followed_communities
|
||||
from resolvers.inbox.unread import get_total_unread_counter
|
||||
from .topics import get_topic_stat
|
||||
|
||||
|
||||
async def user_subscriptions(slug: str):
|
||||
return {
|
||||
"unread": await get_total_unread_counter(slug), # unread inbox messages counter
|
||||
"topics": [t.slug for t in await followed_topics(slug)], # followed topics slugs
|
||||
"authors": [a.slug for a in await followed_authors(slug)], # followed authors slugs
|
||||
"reactions": await ReactedStorage.get_shouts_by_author(slug),
|
||||
"reactions": await followed_reactions(slug)
|
||||
# "communities": [c.slug for c in followed_communities(slug)], # communities
|
||||
}
|
||||
|
||||
|
||||
async def get_author_stat(slug):
|
||||
# TODO: implement author stat
|
||||
with local_session() as session:
|
||||
return {
|
||||
"shouts": session.query(ShoutAuthor).where(ShoutAuthor.user == slug).count(),
|
||||
@@ -39,11 +36,29 @@ async def get_author_stat(slug):
|
||||
).where(
|
||||
Reaction.createdBy == slug
|
||||
).filter(
|
||||
func.length(Reaction.body) > 0
|
||||
Reaction.body.is_not(None)
|
||||
).count()
|
||||
}
|
||||
|
||||
|
||||
# @query.field("userFollowedDiscussions")
|
||||
@login_required
|
||||
async def followed_discussions(_, info, slug) -> List[Topic]:
|
||||
return await followed_reactions(slug)
|
||||
|
||||
|
||||
async def followed_reactions(slug):
|
||||
with local_session() as session:
|
||||
user = session.query(User).where(User.slug == slug).first()
|
||||
return session.query(
|
||||
Reaction.shout
|
||||
).where(
|
||||
Reaction.createdBy == slug
|
||||
).filter(
|
||||
Reaction.createdAt > user.lastSeen
|
||||
).all()
|
||||
|
||||
|
||||
@query.field("userFollowedTopics")
|
||||
@login_required
|
||||
async def get_followed_topics(_, info, slug) -> List[Topic]:
|
||||
|
@@ -146,7 +146,11 @@ async def create_reaction(_, info, inp):
|
||||
except Exception as e:
|
||||
print(f"[resolvers.reactions] error on reactions autofollowing: {e}")
|
||||
|
||||
reaction.stat = await get_reaction_stat(reaction.id)
|
||||
reaction.stat = {
|
||||
"commented": 0,
|
||||
"reacted": 0,
|
||||
"rating": 0
|
||||
}
|
||||
return {"reaction": reaction}
|
||||
|
||||
|
||||
@@ -158,11 +162,16 @@ async def update_reaction(_, info, inp):
|
||||
|
||||
with local_session() as session:
|
||||
user = session.query(User).where(User.id == user_id).first()
|
||||
reaction = session.query(Reaction).filter(Reaction.id == inp.id).first()
|
||||
q = select(Reaction).filter(Reaction.id == inp.id)
|
||||
q = calc_reactions(q)
|
||||
|
||||
[reaction, rating, commented, reacted] = session.execute(q).unique().one()
|
||||
|
||||
if not reaction:
|
||||
return {"error": "invalid reaction id"}
|
||||
if reaction.createdBy != user.slug:
|
||||
return {"error": "access denied"}
|
||||
|
||||
reaction.body = inp["body"]
|
||||
reaction.updatedAt = datetime.now(tz=timezone.utc)
|
||||
if reaction.kind != inp["kind"]:
|
||||
@@ -171,8 +180,11 @@ async def update_reaction(_, info, inp):
|
||||
if inp.get("range"):
|
||||
reaction.range = inp.get("range")
|
||||
session.commit()
|
||||
|
||||
reaction.stat = await get_reaction_stat(reaction.id)
|
||||
reaction.stat = {
|
||||
"commented": commented,
|
||||
"reacted": reacted,
|
||||
"rating": rating
|
||||
}
|
||||
|
||||
return {"reaction": reaction}
|
||||
|
||||
@@ -195,9 +207,11 @@ async def delete_reaction(_, info, rid):
|
||||
|
||||
|
||||
def map_result_item(result_item):
|
||||
reaction = result_item[0]
|
||||
user = result_item[1]
|
||||
[user, shout, reaction] = result_item
|
||||
print(reaction)
|
||||
reaction.createdBy = user
|
||||
reaction.shout = shout
|
||||
reaction.replyTo = reaction
|
||||
return reaction
|
||||
|
||||
|
||||
@@ -220,10 +234,17 @@ async def load_reactions_by(_, _info, by, limit=50, offset=0):
|
||||
"""
|
||||
|
||||
CreatedByUser = aliased(User)
|
||||
|
||||
ReactedShout = aliased(Shout)
|
||||
RepliedReaction = aliased(Reaction)
|
||||
q = select(
|
||||
Reaction, CreatedByUser
|
||||
).join(CreatedByUser, Reaction.createdBy == CreatedByUser.slug)
|
||||
Reaction, CreatedByUser, ReactedShout, RepliedReaction
|
||||
).join(
|
||||
CreatedByUser, Reaction.createdBy == CreatedByUser.slug
|
||||
).join(
|
||||
ReactedShout, Reaction.shout == ReactedShout.slug
|
||||
).join(
|
||||
RepliedReaction, Reaction.replyTo == RepliedReaction.id
|
||||
)
|
||||
|
||||
if by.get("shout"):
|
||||
q = q.filter(Reaction.shout == by["shout"])
|
||||
@@ -243,20 +264,28 @@ async def load_reactions_by(_, _info, by, limit=50, offset=0):
|
||||
order_way = asc if by.get("sort", "").startswith("-") else desc
|
||||
order_field = by.get("sort") or Reaction.createdAt
|
||||
q = q.group_by(
|
||||
Reaction.id, CreatedByUser.id
|
||||
Reaction.id, CreatedByUser.id, ReactedShout.id
|
||||
).order_by(
|
||||
order_way(order_field)
|
||||
)
|
||||
|
||||
q = calc_reactions(q)
|
||||
q = q.where(Reaction.deletedAt.is_(None))
|
||||
q = q.limit(limit).offset(offset)
|
||||
|
||||
reactions = []
|
||||
with local_session() as session:
|
||||
reactions = list(map(map_result_item, session.execute(q)))
|
||||
for reaction in reactions:
|
||||
reaction.stat = await get_reaction_stat(reaction.id)
|
||||
for [
|
||||
[reaction, rating, commented, reacted], shout, reply
|
||||
] in list(map(map_result_item, session.execute(q))):
|
||||
reaction.shout = shout
|
||||
reaction.replyTo = reply
|
||||
reaction.stat = {
|
||||
"rating": rating,
|
||||
"commented": commented,
|
||||
"reacted": reacted
|
||||
}
|
||||
reactions.append(reaction)
|
||||
|
||||
if by.get("stat"):
|
||||
reactions.sort(lambda r: r.stat.get(by["stat"]) or r.createdAt)
|
||||
if by.get("stat"):
|
||||
reactions.sort(lambda r: r.stat.get(by["stat"]) or r.createdAt)
|
||||
|
||||
return reactions
|
||||
|
@@ -3,23 +3,14 @@ from sqlalchemy import and_, select
|
||||
from auth.authenticate import login_required
|
||||
from base.orm import local_session
|
||||
from base.resolvers import mutation, query
|
||||
from orm import Shout
|
||||
from orm.topic import Topic, TopicFollower
|
||||
# from services.stat.reacted import ReactedStorage
|
||||
|
||||
|
||||
# from services.stat.viewed import ViewedStorage
|
||||
|
||||
from orm import Shout
|
||||
|
||||
async def get_topic_stat(slug):
|
||||
return {
|
||||
"shouts": len(TopicStat.shouts_by_topic.get(slug, {}).keys()),
|
||||
"authors": len(TopicStat.authors_by_topic.get(slug, {}).keys()),
|
||||
"followers": len(TopicStat.followers_by_topic.get(slug, {}).keys()),
|
||||
# "viewed": await ViewedStorage.get_topic(slug),
|
||||
# "reacted": len(await ReactedStorage.get_topic(slug)),
|
||||
# "commented": len(await ReactedStorage.get_topic_comments(slug)),
|
||||
# "rating": await ReactedStorage.get_topic_rating(slug)
|
||||
"followers": len(TopicStat.followers_by_topic.get(slug, {}).keys())
|
||||
}
|
||||
|
||||
|
||||
@@ -96,11 +87,12 @@ async def topic_follow(user, slug):
|
||||
async def topic_unfollow(user, slug):
|
||||
with local_session() as session:
|
||||
sub = (
|
||||
session.query(TopicFollower)
|
||||
.filter(
|
||||
and_(TopicFollower.follower == user.slug, TopicFollower.topic == slug)
|
||||
)
|
||||
.first()
|
||||
session.query(TopicFollower).filter(
|
||||
and_(
|
||||
TopicFollower.follower == user.slug,
|
||||
TopicFollower.topic == slug
|
||||
)
|
||||
).first()
|
||||
)
|
||||
if not sub:
|
||||
raise Exception("[resolvers.topics] follower not exist")
|
||||
|
Reference in New Issue
Block a user