fmt
All checks were successful
Deploy on push / deploy (push) Successful in 3m45s

This commit is contained in:
2024-02-24 21:45:38 +03:00
parent 12137eccda
commit eaaace4d28
6 changed files with 263 additions and 215 deletions

View File

@@ -7,9 +7,9 @@ from resolvers.author import (
get_author_id,
get_authors_all,
load_authors_by,
rate_author,
update_author,
)
from resolvers.rating import rate_author
from resolvers.community import get_communities_all, get_community
from resolvers.editor import create_shout, delete_shout, update_shout
from resolvers.follower import (

View File

@@ -1,14 +1,19 @@
import json
import time
from sqlalchemy import and_, desc, select, or_, distinct, func
from sqlalchemy import desc, select, or_, distinct, func
from sqlalchemy.orm import aliased
from orm.author import Author, AuthorFollower, AuthorRating
from orm.author import Author, AuthorFollower
from orm.shout import ShoutAuthor, ShoutTopic
from orm.topic import Topic
from resolvers.follower import query_follows
from resolvers.stat import get_authors_with_stat, execute_with_ministat, author_follows_authors, author_follows_topics
from resolvers.stat import (
get_authors_with_stat,
execute_with_ministat,
author_follows_authors,
author_follows_topics,
)
from services.auth import login_required
from services.db import local_session
from services.rediscache import redis
@@ -47,7 +52,7 @@ def get_author(_, _info, slug='', author_id=None):
if author_id:
q = select(Author).where(Author.id == author_id)
[author, ] = get_authors_with_stat(q, ratings=True)
[author] = get_authors_with_stat(q, ratings=True)
except Exception as exc:
logger.error(exc)
return author
@@ -67,7 +72,7 @@ async def get_author_by_user_id(user_id: str, ratings=False):
logger.info(f'getting author id for {user_id}')
q = select(Author).filter(Author.user == user_id)
[author, ] = get_authors_with_stat(q, ratings)
[author] = get_authors_with_stat(q, ratings)
except Exception as exc:
logger.error(exc)
return author
@@ -115,7 +120,11 @@ def load_authors_by(_, _info, by, limit, offset):
def get_author_follows(_, _info, slug='', user=None, author_id=None):
with local_session() as session:
if user or slug:
author_id_result = session.query(Author.id).filter(or_(Author.user == user, Author.slug == slug)).first()
author_id_result = (
session.query(Author.id)
.filter(or_(Author.user == user, Author.slug == slug))
.first()
)
author_id = author_id_result[0] if author_id_result else None
if author_id:
follows = query_follows(author_id)
@@ -128,7 +137,11 @@ def get_author_follows(_, _info, slug='', user=None, author_id=None):
def get_author_follows_topics(_, _info, slug='', user=None, author_id=None):
with local_session() as session:
if user or slug:
author_id_result = session.query(Author.id).filter(or_(Author.user == user, Author.slug == slug)).first()
author_id_result = (
session.query(Author.id)
.filter(or_(Author.user == user, Author.slug == slug))
.first()
)
author_id = author_id_result[0] if author_id_result else None
if author_id:
follows = author_follows_authors(author_id)
@@ -141,7 +154,11 @@ def get_author_follows_topics(_, _info, slug='', user=None, author_id=None):
def get_author_follows_authors(_, _info, slug='', user=None, author_id=None):
with local_session() as session:
if user or slug:
author_id_result = session.query(Author.id).filter(or_(Author.user == user, Author.slug == slug)).first()
author_id_result = (
session.query(Author.id)
.filter(or_(Author.user == user, Author.slug == slug))
.first()
)
author_id = author_id_result[0] if author_id_result else None
if author_id:
follows = author_follows_topics(author_id)
@@ -150,42 +167,6 @@ def get_author_follows_authors(_, _info, slug='', user=None, author_id=None):
raise ValueError('Author not found')
@mutation.field('rate_author')
@login_required
def rate_author(_, info, rated_slug, value):
user_id = info.context['user_id']
with local_session() as session:
rated_author = session.query(Author).filter(Author.slug == rated_slug).first()
rater = session.query(Author).filter(Author.slug == user_id).first()
if rater and rated_author:
rating: AuthorRating = (
session.query(AuthorRating)
.filter(
and_(
AuthorRating.rater == rater.id,
AuthorRating.author == rated_author.id,
)
)
.first()
)
if rating:
rating.plus = value > 0
session.add(rating)
session.commit()
return {}
else:
try:
rating = AuthorRating(
rater=rater.id, author=rated_author.id, plus=value > 0
)
session.add(rating)
session.commit()
except Exception as err:
return {'error': err}
return {}
def create_author(user_id: str, slug: str, name: str = ''):
with local_session() as session:
new_author = Author(user=user_id, slug=slug, name=name)

137
resolvers/rating.py Normal file
View File

@@ -0,0 +1,137 @@
from sqlalchemy import and_
from sqlalchemy.orm import aliased
from orm.author import AuthorRating, Author
from orm.reaction import Reaction, ReactionKind
from orm.shout import Shout
from services.auth import login_required
from services.db import local_session
from services.schema import mutation
@mutation.field('rate_author')
@login_required
def rate_author(_, info, rated_slug, value):
user_id = info.context['user_id']
with local_session() as session:
rated_author = session.query(Author).filter(Author.slug == rated_slug).first()
rater = session.query(Author).filter(Author.slug == user_id).first()
if rater and rated_author:
rating: AuthorRating = (
session.query(AuthorRating)
.filter(
and_(
AuthorRating.rater == rater.id,
AuthorRating.author == rated_author.id,
)
)
.first()
)
if rating:
rating.plus = value > 0
session.add(rating)
session.commit()
return {}
else:
try:
rating = AuthorRating(
rater=rater.id, author=rated_author.id, plus=value > 0
)
session.add(rating)
session.commit()
except Exception as err:
return {'error': err}
return {}
def count_author_comments_rating(session, author_id) -> int:
replied_alias = aliased(Reaction)
replies_likes = (
session.query(replied_alias)
.join(Reaction, replied_alias.id == Reaction.reply_to)
.where(
and_(
replied_alias.created_by == author_id,
replied_alias.kind == ReactionKind.COMMENT.value,
)
)
.filter(replied_alias.kind == ReactionKind.LIKE.value)
.count()
) or 0
replies_dislikes = (
session.query(replied_alias)
.join(Reaction, replied_alias.id == Reaction.reply_to)
.where(
and_(
replied_alias.created_by == author_id,
replied_alias.kind == ReactionKind.COMMENT.value,
)
)
.filter(replied_alias.kind == ReactionKind.DISLIKE.value)
.count()
) or 0
return replies_likes - replies_dislikes
def count_author_shouts_rating(session, author_id) -> int:
shouts_likes = (
session.query(Reaction, Shout)
.join(Shout, Shout.id == Reaction.shout)
.filter(
and_(
Shout.authors.any(id=author_id),
Reaction.kind == ReactionKind.LIKE.value,
)
)
.count()
or 0
)
shouts_dislikes = (
session.query(Reaction, Shout)
.join(Shout, Shout.id == Reaction.shout)
.filter(
and_(
Shout.authors.any(id=author_id),
Reaction.kind == ReactionKind.DISLIKE.value,
)
)
.count()
or 0
)
return shouts_likes - shouts_dislikes
def load_author_ratings(author: Author):
with local_session() as session:
comments_count = (
session.query(Reaction)
.filter(
and_(
Reaction.created_by == author.id,
Reaction.kind == ReactionKind.COMMENT.value,
Reaction.deleted_at.is_(None),
)
)
.count()
)
likes_count = (
session.query(AuthorRating)
.filter(and_(AuthorRating.author == author.id, AuthorRating.plus.is_(True)))
.count()
)
dislikes_count = (
session.query(AuthorRating)
.filter(
and_(AuthorRating.author == author.id, AuthorRating.plus.is_not(True))
)
.count()
)
author.stat['rating'] = likes_count - dislikes_count
author.stat['rating_shouts'] = count_author_shouts_rating(session, author.id)
author.stat['rating_comments'] = count_author_comments_rating(
session, author.id
)
author.stat['commented'] = comments_count
return author

View File

@@ -57,7 +57,7 @@ def check_to_feature(session, approver_id, reaction):
"""set shout to public if publicated approvers amount > 4"""
if not reaction.reply_to and is_positive(reaction.kind):
if is_featured_author(session, approver_id):
approvers = [approver_id, ]
approvers = [approver_id]
# now count how many approvers are voted already
reacted_readers = (
session.query(Reaction).where(Reaction.shout == reaction.shout).all()
@@ -148,6 +148,40 @@ async def _create_reaction(session, shout, author, reaction):
return rdict
def check_rating(reaction: dict, shout_id: int, session, author: Author):
kind = reaction.get('kind')
opposite_kind = (
ReactionKind.DISLIKE.value if is_positive(kind) else ReactionKind.LIKE.value
)
q = select(Reaction).filter(
and_(
Reaction.shout == shout_id,
Reaction.created_by == author.id,
Reaction.kind.in_(RATING_REACTIONS),
)
)
reply_to = reaction.get('reply_to')
if reply_to and isinstance(reply_to, int):
q = q.filter(Reaction.reply_to == reply_to)
rating_reactions = session.execute(q).all()
same_rating = filter(
lambda r: r.created_by == author.id and r.kind == opposite_kind,
rating_reactions,
)
opposite_rating = filter(
lambda r: r.created_by == author.id and r.kind == opposite_kind,
rating_reactions,
)
if same_rating:
return {'error': "You can't rate the same thing twice"}
elif opposite_rating:
return {'error': 'Remove opposite vote first'}
elif filter(lambda r: r.created_by == author.id, rating_reactions):
return {'error': "You can't rate your own thing"}
return
@mutation.field('create_reaction')
@login_required
async def create_reaction(_, info, reaction):
@@ -174,37 +208,9 @@ async def create_reaction(_, info, reaction):
return {'error': 'cannot create reaction without a kind'}
if kind in RATING_REACTIONS:
opposite_kind = (
ReactionKind.DISLIKE.value
if is_positive(kind)
else ReactionKind.LIKE.value
)
q = select(Reaction).filter(
and_(
Reaction.shout == shout_id,
Reaction.created_by == author.id,
Reaction.kind.in_(RATING_REACTIONS),
)
)
reply_to = reaction.get('reply_to')
if reply_to and isinstance(reply_to, int):
q = q.filter(Reaction.reply_to == reply_to)
rating_reactions = session.execute(q).all()
same_rating = filter(
lambda r: r.created_by == author.id and r.kind == opposite_kind,
rating_reactions,
)
opposite_rating = filter(
lambda r: r.created_by == author.id and r.kind == opposite_kind,
rating_reactions,
)
if same_rating:
return {'error': "You can't rate the same thing twice"}
elif opposite_rating:
return {'error': 'Remove opposite vote first'}
elif filter(lambda r: r.created_by == author.id, rating_reactions):
return {'error': "You can't rate your own thing"}
result = check_rating(reaction, shout_id, session, author)
if result:
return result
rdict = await _create_reaction(session, shout, author, reaction)
return {'reaction': rdict}

View File

@@ -1,11 +1,11 @@
from sqlalchemy import func, distinct, select, join, and_
from sqlalchemy import func, distinct, select, join
from sqlalchemy.orm import aliased
from orm.reaction import Reaction, ReactionKind
from orm.topic import TopicFollower, Topic
from resolvers.rating import load_author_ratings
from services.db import local_session
from orm.author import AuthorFollower, Author, AuthorRating
from orm.shout import ShoutTopic, ShoutAuthor, Shout
from orm.author import AuthorFollower, Author
from orm.shout import ShoutTopic, ShoutAuthor
from services.logger import root_logger as logger
@@ -16,11 +16,22 @@ def add_topic_stat_columns(q):
q = (
q.outerjoin(aliased_shout_topic, aliased_shout_topic.topic == Topic.id)
.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.author)).label("authors_stat"))
.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.author)).label('authors_stat')
)
.outerjoin(aliased_topic_follower)
.add_columns(func.count(distinct(aliased_topic_follower.follower)).label("followers_stat"))
.add_columns(
func.count(distinct(aliased_topic_follower.follower)).label(
'followers_stat'
)
)
)
q = q.group_by(Topic.id)
@@ -35,11 +46,21 @@ def add_author_stat_columns(q):
q = (
q.outerjoin(aliased_shout_author, aliased_shout_author.author == Author.id)
.add_columns(func.count(distinct(aliased_shout_author.shout)).label("shouts_stat"))
.add_columns(
func.count(distinct(aliased_shout_author.shout)).label('shouts_stat')
)
.outerjoin(aliased_author_authors, aliased_author_authors.follower == Author.id)
.add_columns(func.count(distinct(aliased_shout_author.author)).label("authors_stat"))
.outerjoin(aliased_author_followers, aliased_author_followers.author == Author.id)
.add_columns(func.count(distinct(aliased_author_followers.follower)).label("followers_stat"))
.add_columns(
func.count(distinct(aliased_shout_author.author)).label('authors_stat')
)
.outerjoin(
aliased_author_followers, aliased_author_followers.author == Author.id
)
.add_columns(
func.count(distinct(aliased_author_followers.follower)).label(
'followers_stat'
)
)
)
q = q.group_by(Author.id)
@@ -47,104 +68,6 @@ def add_author_stat_columns(q):
return q
def count_author_comments_rating(session, author_id) -> int:
replied_alias = aliased(Reaction)
replies_likes = (
session.query(replied_alias)
.join(Reaction, replied_alias.id == Reaction.reply_to)
.where(
and_(
replied_alias.created_by == author_id,
replied_alias.kind == ReactionKind.COMMENT.value,
)
)
.filter(replied_alias.kind == ReactionKind.LIKE.value)
.count()
) or 0
replies_dislikes = (
session.query(replied_alias)
.join(Reaction, replied_alias.id == Reaction.reply_to)
.where(
and_(
replied_alias.created_by == author_id,
replied_alias.kind == ReactionKind.COMMENT.value,
)
)
.filter(replied_alias.kind == ReactionKind.DISLIKE.value)
.count()
) or 0
return replies_likes - replies_dislikes
def count_author_shouts_rating(session, author_id) -> int:
shouts_likes = (
session.query(Reaction, Shout)
.join(Shout, Shout.id == Reaction.shout)
.filter(
and_(
Shout.authors.any(id=author_id),
Reaction.kind == ReactionKind.LIKE.value,
)
)
.count()
or 0
)
shouts_dislikes = (
session.query(Reaction, Shout)
.join(Shout, Shout.id == Reaction.shout)
.filter(
and_(
Shout.authors.any(id=author_id),
Reaction.kind == ReactionKind.DISLIKE.value,
)
)
.count()
or 0
)
return shouts_likes - shouts_dislikes
def load_author_ratings(author: Author):
with local_session() as session:
comments_count = (
session.query(Reaction)
.filter(
and_(
Reaction.created_by == author.id,
Reaction.kind == ReactionKind.COMMENT.value,
Reaction.deleted_at.is_(None),
)
)
.count()
)
likes_count = (
session.query(AuthorRating)
.filter(
and_(AuthorRating.author == author.id, AuthorRating.plus.is_(True))
)
.count()
)
dislikes_count = (
session.query(AuthorRating)
.filter(
and_(
AuthorRating.author == author.id, AuthorRating.plus.is_not(True)
)
)
.count()
)
author.stat['rating'] = likes_count - dislikes_count
author.stat['rating_shouts'] = count_author_shouts_rating(
session, author.id
)
author.stat['rating_comments'] = count_author_comments_rating(
session, author.id
)
author.stat['commented'] = comments_count
return author
def execute_with_ministat(q):
records = []
with local_session() as session:
@@ -176,11 +99,11 @@ def get_topics_with_stat(q):
def author_follows_authors(author_id: int):
af = aliased(AuthorFollower, name="af")
af = aliased(AuthorFollower, name='af')
q = (
select(Author).select_from(
join(Author, af, Author.id == int(af.author))
).where(af.follower == author_id)
select(Author)
.select_from(join(Author, af, Author.id == int(af.author)))
.where(af.follower == author_id)
)
q = add_author_stat_columns(q)
return execute_with_ministat(q)
@@ -188,9 +111,9 @@ def author_follows_authors(author_id: int):
def author_follows_topics(author_id: int):
q = (
select(Topic).select_from(
join(Topic, TopicFollower, Topic.id == TopicFollower.topic)
).where(TopicFollower.follower == author_id)
select(Topic)
.select_from(join(Topic, TopicFollower, Topic.id == TopicFollower.topic))
.where(TopicFollower.follower == author_id)
)
q = add_topic_stat_columns(q)
@@ -207,5 +130,5 @@ def query_follows(author_id: int):
'communities': [{'id': 1, 'name': 'Дискурс', 'slug': 'discours'}],
}
except Exception as e:
logger.exception(f"An error occurred while executing query_follows: {e}")
raise Exception("An error occurred while executing query_follows") from e
logger.exception(f'An error occurred while executing query_follows: {e}')
raise Exception('An error occurred while executing query_follows') from e