Merge branch 'main' of github.com:Discours/discours-backend into main

This commit is contained in:
tonyrewin 2022-11-18 07:47:34 +03:00
commit 0d24193c3d
6 changed files with 144 additions and 99 deletions

View File

@ -12,6 +12,7 @@ from orm.user import AuthorFollower, Role, User, UserRating, UserRole
from services.auth.users import UserStorage from services.auth.users import UserStorage
from services.stat.reacted import ReactedStorage from services.stat.reacted import ReactedStorage
from services.stat.topicstat import TopicStat from services.stat.topicstat import TopicStat
from services.zine.authors import AuthorsStorage
from services.zine.shoutauthor import ShoutAuthorStorage from services.zine.shoutauthor import ShoutAuthorStorage
# from .community import followed_communities # from .community import followed_communities
@ -174,12 +175,9 @@ def author_unfollow(user, slug):
@query.field("authorsAll") @query.field("authorsAll")
async def get_authors_all(_, _info): async def get_authors_all(_, _info):
users = await UserStorage.get_all_users() authors = await AuthorsStorage.get_all_authors()
authors = [] for author in authors:
for author in users: author.stat = await get_author_stat(author.slug)
if ShoutAuthorStorage.shouts_by_author.get(author.slug):
author.stat = await get_author_stat(author.slug)
authors.append(author)
return authors return authors
@ -212,5 +210,5 @@ async def load_authors_by(_, info, by, limit, offset):
for a in authors: for a in authors:
a.stat = await get_author_stat(a.slug) a.stat = await get_author_stat(a.slug)
authors = list(set(authors)) authors = list(set(authors))
authors = sorted(authors, key=lambda a: a["stat"].get(by.get("stat"))) # authors = sorted(authors, key=lambda a: a["stat"].get(by.get("stat")))
return authors return authors

View File

@ -217,13 +217,10 @@ async def load_reactions_by(_, info, by, limit=50, offset=0):
:return: Reaction[] :return: Reaction[]
""" """
q = select(Reaction).options( q = select(Reaction).join(
selectinload(Reaction.shout), Shout
).where( ).where(
Reaction.deletedAt.is_(None) Reaction.deletedAt.is_(None)
).join(
Shout,
Shout.slug == Reaction.shout
) )
if by.get("slug"): if by.get("slug"):
q = q.filter(Shout.slug == by["slug"]) q = q.filter(Shout.slug == by["slug"])
@ -243,8 +240,9 @@ async def load_reactions_by(_, info, by, limit=50, offset=0):
if by.get("days"): if by.get("days"):
before = datetime.now() - timedelta(days=int(by["days"]) or 30) before = datetime.now() - timedelta(days=int(by["days"]) or 30)
q = q.filter(Reaction.createdAt > before) q = q.filter(Reaction.createdAt > before)
q = q.group_by(Shout.id).order_by(
desc(by.get("order") or "createdAt") q = q.group_by(Reaction.id).order_by(
desc(by.get("order") or Reaction.createdAt)
).limit(limit).offset(offset) ).limit(limit).offset(offset)
rrr = [] rrr = []

View File

@ -6,8 +6,8 @@ from timeit import default_timer as timer
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
from orm.shout import Shout from orm.shout import Shout, ShoutAuthor
from orm.reaction import Reaction, ReactionsWeights, ReactionKind from orm.reaction import Reaction, ReactionKind
# from resolvers.community import community_follow, community_unfollow # from resolvers.community import community_follow, community_unfollow
from resolvers.profile import author_follow, author_unfollow from resolvers.profile import author_follow, author_unfollow
from resolvers.reactions import reactions_follow, reactions_unfollow from resolvers.reactions import reactions_follow, reactions_unfollow
@ -16,22 +16,41 @@ from services.zine.shoutauthor import ShoutAuthorStorage
from services.stat.reacted import ReactedStorage from services.stat.reacted import ReactedStorage
@query.field("loadShoutsBy") @query.field("loadShout")
async def load_shouts_by(_, info, filter_by, limit, offset, order_by="createdAt", order_by_desc=True): async def load_shout(_, info, slug):
with local_session() as session:
shout = session.query(Shout).options(
# TODO add cation
selectinload(Shout.authors),
selectinload(Shout.topics),
).filter(
Shout.slug == slug
).filter(
Shout.deletedAt.is_(None)
).one()
return shout
@query.field("loadShouts")
async def load_shouts_by(_, info, options):
""" """
:param filterBy: { :param options: {
layout: 'audio', filters: {
visibility: "public", layout: 'audio',
author: 'discours', visibility: "public",
topic: 'culture', author: 'discours',
title: 'something', topic: 'culture',
body: 'something else', title: 'something',
days: 30 body: 'something else',
days: 30
}
offset: 0
limit: 50
order_by: 'createdAt'
order_by_desc: tr
} }
:param order_by: 'rating' | 'comments' | 'reacted' | 'views' | 'createdAt
:param order_by_desc: order be desc/ask (desc by default)
:param limit: int amount of shouts
:param offset: int offset in this order
:return: Shout[] :return: Shout[]
""" """
@ -43,68 +62,70 @@ async def load_shouts_by(_, info, filter_by, limit, offset, order_by="createdAt"
Shout.deletedAt.is_(None) Shout.deletedAt.is_(None)
) )
if filter_by.get("slug"): if options.get("filters"):
q = q.filter(Shout.slug == filter_by["slug"]) if options.get("filters").get("reacted"):
else:
if filter_by.get("reacted"):
user = info.context["request"].user user = info.context["request"].user
q.join(Reaction, Reaction.createdBy == user.slug) q.join(Reaction, Reaction.createdBy == user.slug)
if filter_by.get("visibility"): if options.get("filters").get("visibility"):
q = q.filter(or_( q = q.filter(or_(
Shout.visibility.ilike(f"%{filter_by.get('visibility')}%"), Shout.visibility.ilike(f"%{options.get('filters').get('visibility')}%"),
Shout.visibility.ilike(f"%{'public'}%"), Shout.visibility.ilike(f"%{'public'}%"),
)) ))
if filter_by.get("layout"): if options.get("filters").get("layout"):
q = q.filter(Shout.layout == filter_by["layout"]) q = q.filter(Shout.layout == options.get("filters").get("layout"))
if filter_by.get("author"): if options.get("filters").get("author"):
q = q.filter(Shout.authors.any(slug=filter_by["author"])) q = q.filter(Shout.authors.any(slug=options.get("filters").get("author")))
if filter_by.get("topic"): if options.get("filters").get("topic"):
q = q.filter(Shout.topics.any(slug=filter_by["topic"])) q = q.filter(Shout.topics.any(slug=options.get("filters").get("topic")))
if filter_by.get("title"): if options.get("filters").get("title"):
q = q.filter(Shout.title.ilike(f'%{filter_by["title"]}%')) q = q.filter(Shout.title.ilike(f'%{options.get("filters").get("title")}%'))
if filter_by.get("body"): if options.get("filters").get("body"):
q = q.filter(Shout.body.ilike(f'%{filter_by["body"]}%')) q = q.filter(Shout.body.ilike(f'%{options.get("filters").get("body")}%s'))
if filter_by.get("days"): if options.get("filters").get("days"):
before = datetime.now() - timedelta(days=int(filter_by["days"]) or 30) before = datetime.now() - timedelta(days=int(options.get("filter").get("days")) or 30)
q = q.filter(Shout.createdAt > before) q = q.filter(Shout.createdAt > before)
if order_by == 'comments':
q = q.join(Reaction).add_columns(sa.func.count(Reaction.id).label(order_by))
if order_by == 'reacted':
# TODO ?
q = q.join(Reaction).add_columns(sa.func.count(Reaction.id).label(order_by))
if order_by == "rating":
q = q.join(Reaction).add_columns(sa.func.sum(case(
(Reaction.kind == ReactionKind.AGREE, 1),
(Reaction.kind == ReactionKind.DISAGREE, -1),
(Reaction.kind == ReactionKind.PROOF, 1),
(Reaction.kind == ReactionKind.DISPROOF, -1),
(Reaction.kind == ReactionKind.ACCEPT, 1),
(Reaction.kind == ReactionKind.REJECT, -1),
(Reaction.kind == ReactionKind.LIKE, 1),
(Reaction.kind == ReactionKind.DISLIKE, -1),
else_=0
)).label(order_by))
# if order_by == 'views':
# TODO dump ackee data to db periodically
query_order_by = desc(order_by) if order_by_desc else asc(order_by) if options.get("order_by") == 'comments':
q = q.join(Reaction, Shout.slug == Reaction.shout and Reaction.body.is_not(None)).add_columns(
sa.func.count(Reaction.id).label(options.get("order_by")))
if options.get("order_by") == 'reacted':
q = q.join(Reaction).add_columns(sa.func.max(Reaction.createdAt).label(options.get("order_by")))
if options.get("order_by") == "rating":
q = q.join(Reaction).add_columns(sa.func.sum(case(
(Reaction.kind == ReactionKind.AGREE, 1),
(Reaction.kind == ReactionKind.DISAGREE, -1),
(Reaction.kind == ReactionKind.PROOF, 1),
(Reaction.kind == ReactionKind.DISPROOF, -1),
(Reaction.kind == ReactionKind.ACCEPT, 1),
(Reaction.kind == ReactionKind.REJECT, -1),
(Reaction.kind == ReactionKind.LIKE, 1),
(Reaction.kind == ReactionKind.DISLIKE, -1),
else_=0
)).label(options.get("order_by")))
# if order_by == 'views':
# TODO dump ackee data to db periodically
q = q.group_by(Shout.id).order_by(query_order_by).limit(limit).offset(offset) order_by = options.get("order_by") if options.get("order_by") else 'createdAt'
with local_session() as session: query_order_by = desc(order_by) if options.get('order_by_desc') else asc(order_by)
# post query stats and author's captions
# start = timer()
shouts = list(map(lambda r: r.Shout, session.execute(q)))
for s in shouts:
s.stat = await ReactedStorage.get_shout_stat(s.slug)
for a in s.authors:
a.caption = await ShoutAuthorStorage.get_author_caption(s.slug, a.slug)
# end = timer() q = q.group_by(Shout.id).order_by(query_order_by).limit(options.get("limit")).offset(
# print(end - start) options.get("offset") if options.get("offset") else 0)
# print(q)
return shouts with local_session() as session:
# post query stats and author's captions
# start = timer()
shouts = list(map(lambda r: r.Shout, session.execute(q)))
for s in shouts:
s.stat = await ReactedStorage.get_shout_stat(s.slug)
for a in s.authors:
a.caption = await ShoutAuthorStorage.get_author_caption(s.slug, a.slug)
# end = timer()
# print(end - start)
# print(q)
return shouts
@mutation.field("follow") @mutation.field("follow")

View File

@ -212,17 +212,36 @@ input AuthorsBy {
} }
input ShoutsFilterBy { input ShoutsFilterBy {
slug: String slug: String
title: String title: String
body: String body: String
topic: String topic: String
topics: [String] topics: [String]
author: String author: String
authors: [String] authors: [String]
layout: String layout: String
visibility: String visibility: String
days: Int days: Int
stat: String stat: String
}
input LoadShoutsFilters {
title: String
body: String
topic: String
author: String
layout: String
visibility: String
days: Int
reacted: Boolean
}
input LoadShoutsOptions {
filters: LoadShoutsFilters
limit: Int!
offset: Int
order_by: String
order_by_desc: Boolean
} }
input ReactionBy { input ReactionBy {
@ -251,13 +270,14 @@ type Query {
# zine # zine
loadAuthorsBy(by: AuthorsBy, limit: Int, offset: Int): [Author]! loadAuthorsBy(by: AuthorsBy, limit: Int, offset: Int): [Author]!
loadShoutsBy(filter_by: ShoutsFilterBy!, limit: Int!, offset: Int!, order_by: String, order_by_desc: Boolean): [Shout]! loadShout(slug: String!): Shout
loadShouts(options: LoadShoutsOptions): [Shout]!
loadReactionsBy(by: ReactionBy!, limit: Int, offset: Int): [Reaction]! loadReactionsBy(by: ReactionBy!, limit: Int, offset: Int): [Reaction]!
userFollowers(slug: String!): [Author]! userFollowers(slug: String!): [Author]!
userFollowedAuthors(slug: String!): [Author]! userFollowedAuthors(slug: String!): [Author]!
userFollowedTopics(slug: String!): [Topic]! userFollowedTopics(slug: String!): [Topic]!
authorsAll: [Author]! authorsAll: [Author]!
getAuthor(slug: String!): User! getAuthor(slug: String!): User
# collab # collab
getCollabs: [Collab]! getCollabs: [Collab]!
@ -266,7 +286,7 @@ type Query {
markdownBody(body: String!): String! markdownBody(body: String!): String!
# topics # topics
getTopic(slug: String!): Topic! getTopic(slug: String!): Topic
topicsAll: [Topic]! topicsAll: [Topic]!
topicsRandom(amount: Int): [Topic]! topicsRandom(amount: Int): [Topic]!
topicsByCommunity(community: String!): [Topic]! topicsByCommunity(community: String!): [Topic]!

12
services/zine/authors.py Normal file
View File

@ -0,0 +1,12 @@
from base.orm import local_session
from orm.user import User
from orm.shout import ShoutAuthor
class AuthorsStorage:
@staticmethod
async def get_all_authors():
with local_session() as session:
query = session.query(User).join(ShoutAuthor)
result = query.all()
return result

View File

@ -6,7 +6,6 @@ from orm.shout import ShoutAuthor, Shout
class ShoutAuthorStorage: class ShoutAuthorStorage:
authors_by_shout = {} authors_by_shout = {}
shouts_by_author = {}
lock = asyncio.Lock() lock = asyncio.Lock()
period = 30 * 60 # sec period = 30 * 60 # sec
@ -17,10 +16,7 @@ class ShoutAuthorStorage:
for sa in sas: for sa in sas:
self.authors_by_shout[sa.shout] = self.authors_by_shout.get(sa.shout, []) self.authors_by_shout[sa.shout] = self.authors_by_shout.get(sa.shout, [])
self.authors_by_shout[sa.shout].append([sa.user, sa.caption]) self.authors_by_shout[sa.shout].append([sa.user, sa.caption])
self.shouts_by_author[sa.user] = self.shouts_by_author.get(sa.user, [])
self.shouts_by_author[sa.user].append(sa.shout)
print("[zine.authors] %d shouts indexed by authors" % len(self.authors_by_shout)) print("[zine.authors] %d shouts indexed by authors" % len(self.authors_by_shout))
print("[zine.authors] %d authors indexed by shouts" % len(self.shouts_by_author))
@staticmethod @staticmethod
async def get_authors(shout): async def get_authors(shout):