This commit is contained in:
parent
821a4c0df1
commit
47a8493824
|
@ -25,7 +25,7 @@ from resolvers.notifier import (
|
||||||
notifications_seen_after,
|
notifications_seen_after,
|
||||||
notifications_seen_thread,
|
notifications_seen_thread,
|
||||||
)
|
)
|
||||||
from resolvers.rating import rate_author
|
from resolvers.rating import get_my_rates_comments, get_my_rates_shouts, rate_author
|
||||||
from resolvers.reaction import (
|
from resolvers.reaction import (
|
||||||
create_reaction,
|
create_reaction,
|
||||||
delete_reaction,
|
delete_reaction,
|
||||||
|
@ -63,7 +63,6 @@ __all__ = [
|
||||||
"get_author_follows_authors",
|
"get_author_follows_authors",
|
||||||
"get_authors_all",
|
"get_authors_all",
|
||||||
"load_authors_by",
|
"load_authors_by",
|
||||||
"rate_author",
|
|
||||||
"update_author",
|
"update_author",
|
||||||
## "search_authors",
|
## "search_authors",
|
||||||
# community
|
# community
|
||||||
|
@ -110,4 +109,8 @@ __all__ = [
|
||||||
"notifications_seen_thread",
|
"notifications_seen_thread",
|
||||||
"notifications_seen_after",
|
"notifications_seen_after",
|
||||||
"notification_mark_seen",
|
"notification_mark_seen",
|
||||||
|
# rating
|
||||||
|
"rate_author",
|
||||||
|
"get_my_rates_comments",
|
||||||
|
"get_my_rates_shouts",
|
||||||
]
|
]
|
||||||
|
|
|
@ -6,7 +6,71 @@ from orm.reaction import Reaction, ReactionKind
|
||||||
from orm.shout import Shout
|
from orm.shout import Shout
|
||||||
from services.auth import login_required
|
from services.auth import login_required
|
||||||
from services.db import local_session
|
from services.db import local_session
|
||||||
from services.schema import mutation
|
from services.schema import mutation, query
|
||||||
|
|
||||||
|
|
||||||
|
@query.field("get_my_rates_comments")
|
||||||
|
@login_required
|
||||||
|
def get_my_rates_comments(info, comments: list[int], shout: int) -> list[dict]:
|
||||||
|
"""
|
||||||
|
Получение реакций пользователя на комментарии
|
||||||
|
"""
|
||||||
|
author_dict = info.context.get("author") if info.context else None
|
||||||
|
author_id = author_dict.get("id") if author_dict else None
|
||||||
|
if not author_id:
|
||||||
|
return {"error": "Author not found"}
|
||||||
|
|
||||||
|
# Подзапрос для реакций текущего пользователя
|
||||||
|
rated_query = (
|
||||||
|
select(Reaction.shout.label("shout_id"), Reaction.kind.label("my_rate"))
|
||||||
|
.where(
|
||||||
|
and_(
|
||||||
|
Reaction.shout == shout,
|
||||||
|
Reaction.reply_to.in_(comments),
|
||||||
|
Reaction.created_by == author_id,
|
||||||
|
Reaction.deleted_at.is_(None),
|
||||||
|
Reaction.kind.in_([ReactionKind.LIKE.value, ReactionKind.DISLIKE.value]),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.order_by(Reaction.shout, Reaction.created_at.desc())
|
||||||
|
.distinct(Reaction.shout)
|
||||||
|
.subquery()
|
||||||
|
)
|
||||||
|
with local_session() as session:
|
||||||
|
comments_result = session.execute(rated_query).all()
|
||||||
|
return [{"comment_id": row.shout_id, "my_rate": row.my_rate} for row in comments_result]
|
||||||
|
|
||||||
|
|
||||||
|
@query.field("get_my_rates_shouts")
|
||||||
|
@login_required
|
||||||
|
def get_my_rates_shouts(info, shouts):
|
||||||
|
"""
|
||||||
|
Получение реакций пользователя на публикации
|
||||||
|
"""
|
||||||
|
author_dict = info.context.get("author") if info.context else None
|
||||||
|
author_id = author_dict.get("id") if author_dict else None
|
||||||
|
if not author_id:
|
||||||
|
return {"error": "Author not found"}
|
||||||
|
|
||||||
|
# Подзапрос для реакций текущего пользователя
|
||||||
|
rated_query = (
|
||||||
|
select(Reaction.shout.label("shout_id"), Reaction.kind.label("my_rate"))
|
||||||
|
.where(
|
||||||
|
and_(
|
||||||
|
Reaction.shout.in_(shouts),
|
||||||
|
Reaction.reply_to.is_(None),
|
||||||
|
Reaction.created_by == author_id,
|
||||||
|
Reaction.deleted_at.is_(None),
|
||||||
|
Reaction.kind.in_([ReactionKind.LIKE.value, ReactionKind.DISLIKE.value]),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.order_by(Reaction.shout, Reaction.created_at.desc())
|
||||||
|
.distinct(Reaction.shout)
|
||||||
|
.subquery()
|
||||||
|
)
|
||||||
|
with local_session() as session:
|
||||||
|
shouts_result = session.execute(rated_query).all()
|
||||||
|
return [{"shout_id": row.shout_id, "my_rate": row.my_rate} for row in shouts_result]
|
||||||
|
|
||||||
|
|
||||||
@mutation.field("rate_author")
|
@mutation.field("rate_author")
|
||||||
|
|
|
@ -146,7 +146,7 @@ def query_with_stat(info):
|
||||||
|
|
||||||
if has_field(info, "stat"):
|
if has_field(info, "stat"):
|
||||||
logger.info("Начало построения запроса статистики")
|
logger.info("Начало построения запроса статистики")
|
||||||
|
|
||||||
# Подзапрос для статистики реакций
|
# Подзапрос для статистики реакций
|
||||||
stats_subquery = (
|
stats_subquery = (
|
||||||
select(
|
select(
|
||||||
|
@ -163,9 +163,7 @@ def query_with_stat(info):
|
||||||
)
|
)
|
||||||
.filter(Reaction.reply_to.is_(None))
|
.filter(Reaction.reply_to.is_(None))
|
||||||
.label("rating"),
|
.label("rating"),
|
||||||
func.max(Reaction.created_at)
|
func.max(Reaction.created_at).filter(Reaction.reply_to.is_(None)).label("last_reacted_at"),
|
||||||
.filter(Reaction.reply_to.is_(None))
|
|
||||||
.label("last_reacted_at"),
|
|
||||||
)
|
)
|
||||||
.where(Reaction.deleted_at.is_(None))
|
.where(Reaction.deleted_at.is_(None))
|
||||||
.group_by(Reaction.shout)
|
.group_by(Reaction.shout)
|
||||||
|
@ -182,30 +180,8 @@ def query_with_stat(info):
|
||||||
if author_id:
|
if author_id:
|
||||||
logger.info(f"Построение подзапроса реакций пользователя с ID: {author_id}")
|
logger.info(f"Построение подзапроса реакций пользователя с ID: {author_id}")
|
||||||
|
|
||||||
# Подзапрос для реакций текущего пользователя
|
|
||||||
user_reaction_subquery = (
|
|
||||||
select(
|
|
||||||
Reaction.shout.label("shout_id"),
|
|
||||||
Reaction.kind.label("my_rate")
|
|
||||||
)
|
|
||||||
.where(
|
|
||||||
and_(
|
|
||||||
Reaction.created_by == author_id,
|
|
||||||
Reaction.deleted_at.is_(None),
|
|
||||||
Reaction.kind.in_([ReactionKind.LIKE.value, ReactionKind.DISLIKE.value]),
|
|
||||||
Reaction.reply_to.is_(None),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.order_by(Reaction.shout, Reaction.created_at.desc())
|
|
||||||
.distinct(Reaction.shout)
|
|
||||||
.subquery()
|
|
||||||
)
|
|
||||||
logger.info("Подзапрос реакций пользователя построен")
|
|
||||||
|
|
||||||
logger.info("Соединение основного запроса с подзапросом статистики")
|
logger.info("Соединение основного запроса с подзапросом статистики")
|
||||||
q = q.outerjoin(stats_subquery, stats_subquery.c.shout == Shout.id)
|
q = q.outerjoin(stats_subquery, stats_subquery.c.shout == Shout.id)
|
||||||
logger.info("Соединение основного запроса с подзапросом реакций пользователя")
|
|
||||||
q = q.outerjoin(user_reaction_subquery, user_reaction_subquery.c.shout_id == Shout.id)
|
|
||||||
|
|
||||||
logger.info("Добавление колонок статистики в основной запрос")
|
logger.info("Добавление колонок статистики в основной запрос")
|
||||||
q = q.add_columns(
|
q = q.add_columns(
|
||||||
|
@ -216,8 +192,6 @@ def query_with_stat(info):
|
||||||
func.coalesce(stats_subquery.c.rating, 0),
|
func.coalesce(stats_subquery.c.rating, 0),
|
||||||
"last_reacted_at",
|
"last_reacted_at",
|
||||||
stats_subquery.c.last_reacted_at,
|
stats_subquery.c.last_reacted_at,
|
||||||
"my_rate",
|
|
||||||
user_reaction_subquery.c.my_rate
|
|
||||||
).label("stat")
|
).label("stat")
|
||||||
)
|
)
|
||||||
logger.info("Колонки статистики добавлены")
|
logger.info("Колонки статистики добавлены")
|
||||||
|
@ -233,7 +207,7 @@ def query_with_stat(info):
|
||||||
"last_reacted_at",
|
"last_reacted_at",
|
||||||
stats_subquery.c.last_reacted_at,
|
stats_subquery.c.last_reacted_at,
|
||||||
"my_rate",
|
"my_rate",
|
||||||
None
|
None,
|
||||||
).label("stat")
|
).label("stat")
|
||||||
)
|
)
|
||||||
logger.info("Колонки статистики без my_rate добавлены")
|
logger.info("Колонки статистики без my_rate добавлены")
|
||||||
|
@ -263,19 +237,19 @@ def get_shouts_with_links(info, q, limit=20, offset=0):
|
||||||
for idx, row in enumerate(shouts_result):
|
for idx, row in enumerate(shouts_result):
|
||||||
try:
|
try:
|
||||||
logger.debug(f"Обработка строки {idx}")
|
logger.debug(f"Обработка строки {idx}")
|
||||||
|
|
||||||
shout = None
|
shout = None
|
||||||
if hasattr(row, "Shout"):
|
if hasattr(row, "Shout"):
|
||||||
shout = row.Shout
|
shout = row.Shout
|
||||||
else:
|
else:
|
||||||
if not row == 'stat':
|
if not row == "stat":
|
||||||
logger.warning(f"Строка {idx} не содержит атрибута 'Shout': {row}")
|
logger.warning(f"Строка {idx} не содержит атрибута 'Shout': {row}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if shout:
|
if shout:
|
||||||
shout_id = int(f"{shout.id}")
|
shout_id = int(f"{shout.id}")
|
||||||
shout_dict = shout.dict()
|
shout_dict = shout.dict()
|
||||||
|
|
||||||
if has_field(info, "created_by") and shout_dict.get("created_by"):
|
if has_field(info, "created_by") and shout_dict.get("created_by"):
|
||||||
main_author_id = shout_dict.get("created_by")
|
main_author_id = shout_dict.get("created_by")
|
||||||
a = session.query(Author).filter(Author.id == main_author_id).first()
|
a = session.query(Author).filter(Author.id == main_author_id).first()
|
||||||
|
@ -285,7 +259,7 @@ def get_shouts_with_links(info, q, limit=20, offset=0):
|
||||||
"slug": a.slug,
|
"slug": a.slug,
|
||||||
"pic": a.pic,
|
"pic": a.pic,
|
||||||
}
|
}
|
||||||
|
|
||||||
if hasattr(row, "stat"):
|
if hasattr(row, "stat"):
|
||||||
logger.debug(f"Строка {idx} - stat: {row.stat}")
|
logger.debug(f"Строка {idx} - stat: {row.stat}")
|
||||||
stat = {}
|
stat = {}
|
||||||
|
@ -299,7 +273,7 @@ def get_shouts_with_links(info, q, limit=20, offset=0):
|
||||||
shout_dict["stat"] = {**stat, "viewed": viewed, "commented": stat.get("comments_count", 0)}
|
shout_dict["stat"] = {**stat, "viewed": viewed, "commented": stat.get("comments_count", 0)}
|
||||||
else:
|
else:
|
||||||
logger.warning(f"Строка {idx} не содержит атрибута 'stat'")
|
logger.warning(f"Строка {idx} не содержит атрибута 'stat'")
|
||||||
|
|
||||||
if has_field(info, "main_topic") and hasattr(row, "main_topic"):
|
if has_field(info, "main_topic") and hasattr(row, "main_topic"):
|
||||||
shout_dict["main_topic"] = (
|
shout_dict["main_topic"] = (
|
||||||
json.loads(row.main_topic) if isinstance(row.main_topic, str) else row.main_topic
|
json.loads(row.main_topic) if isinstance(row.main_topic, str) else row.main_topic
|
||||||
|
@ -407,7 +381,6 @@ def apply_sorting(q, options):
|
||||||
|
|
||||||
|
|
||||||
@query.field("load_shouts_by")
|
@query.field("load_shouts_by")
|
||||||
@login_accepted
|
|
||||||
async def load_shouts_by(_, info: GraphQLResolveInfo, options):
|
async def load_shouts_by(_, info: GraphQLResolveInfo, options):
|
||||||
"""
|
"""
|
||||||
Загрузка публикаций с фильтрацией, сортировкой и пагинацией.
|
Загрузка публикаций с фильтрацией, сортировкой и пагинацией.
|
||||||
|
@ -426,7 +399,6 @@ async def load_shouts_by(_, info: GraphQLResolveInfo, options):
|
||||||
|
|
||||||
|
|
||||||
@query.field("load_shouts_search")
|
@query.field("load_shouts_search")
|
||||||
@login_accepted
|
|
||||||
async def load_shouts_search(_, info, text, options):
|
async def load_shouts_search(_, info, text, options):
|
||||||
"""
|
"""
|
||||||
Поиск публикаций по тексту.
|
Поиск публикаций по тексту.
|
||||||
|
@ -488,29 +460,17 @@ async def load_shouts_unrated(_, info, options):
|
||||||
.scalar_subquery()
|
.scalar_subquery()
|
||||||
)
|
)
|
||||||
|
|
||||||
q = (
|
q = select(Shout).where(and_(Shout.published_at.is_not(None), Shout.deleted_at.is_(None)))
|
||||||
select(Shout)
|
|
||||||
.where(and_(Shout.published_at.is_not(None), Shout.deleted_at.is_(None)))
|
|
||||||
)
|
|
||||||
q = q.join(Author, Author.id == Shout.created_by)
|
q = q.join(Author, Author.id == Shout.created_by)
|
||||||
q = q.add_columns(
|
q = q.add_columns(
|
||||||
json_builder(
|
json_builder("id", Author.id, "name", Author.name, "slug", Author.slug, "pic", Author.pic).label("main_author")
|
||||||
"id", Author.id,
|
|
||||||
"name", Author.name,
|
|
||||||
"slug", Author.slug,
|
|
||||||
"pic", Author.pic
|
|
||||||
).label("main_author")
|
|
||||||
)
|
)
|
||||||
q = q.join(ShoutTopic, and_(ShoutTopic.shout == Shout.id, ShoutTopic.main.is_(True)))
|
q = q.join(ShoutTopic, and_(ShoutTopic.shout == Shout.id, ShoutTopic.main.is_(True)))
|
||||||
q = q.join(Topic, Topic.id == ShoutTopic.topic)
|
q = q.join(Topic, Topic.id == ShoutTopic.topic)
|
||||||
q = q.add_columns(
|
q = q.add_columns(json_builder("id", Topic.id, "title", Topic.title, "slug", Topic.slug).label("main_topic"))
|
||||||
json_builder(
|
|
||||||
"id", Topic.id, "title", Topic.title, "slug", Topic.slug
|
|
||||||
).label("main_topic")
|
|
||||||
)
|
|
||||||
q = q.where(Shout.id.not_in(rated_shouts))
|
q = q.where(Shout.id.not_in(rated_shouts))
|
||||||
q = q.order_by(func.random())
|
q = q.order_by(func.random())
|
||||||
|
|
||||||
limit = options.get("limit", 5)
|
limit = options.get("limit", 5)
|
||||||
offset = options.get("offset", 0)
|
offset = options.get("offset", 0)
|
||||||
return get_shouts_with_links(info, q, limit, offset)
|
return get_shouts_with_links(info, q, limit, offset)
|
||||||
|
|
|
@ -32,6 +32,10 @@ type Query {
|
||||||
load_shouts_search(text: String!, options: LoadShoutsOptions): [SearchResult]
|
load_shouts_search(text: String!, options: LoadShoutsOptions): [SearchResult]
|
||||||
load_shouts_bookmarked(options: LoadShoutsOptions): [Shout]
|
load_shouts_bookmarked(options: LoadShoutsOptions): [Shout]
|
||||||
|
|
||||||
|
# rating
|
||||||
|
get_my_rates_shouts(shouts: [Int!]!): [MyRateShout]
|
||||||
|
get_my_rates_comments(comments: [Int!]!, shout: Int!): [MyRateComment]
|
||||||
|
|
||||||
# public feeds
|
# public feeds
|
||||||
load_shouts_with_topic(slug: String, options: LoadShoutsOptions): [Shout] # topic feed
|
load_shouts_with_topic(slug: String, options: LoadShoutsOptions): [Shout] # topic feed
|
||||||
load_shouts_random_top(options: LoadShoutsOptions): [Shout] # random order, fixed filter, limit offset can be used
|
load_shouts_random_top(options: LoadShoutsOptions): [Shout] # random order, fixed filter, limit offset can be used
|
||||||
|
|
|
@ -112,7 +112,6 @@ type Stat {
|
||||||
viewed: Int
|
viewed: Int
|
||||||
# followed: Int
|
# followed: Int
|
||||||
last_reacted_at: Int
|
last_reacted_at: Int
|
||||||
my_rate: ReactionKind
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type CommunityStat {
|
type CommunityStat {
|
||||||
|
@ -234,3 +233,15 @@ type NotificationsResult {
|
||||||
total: Int!
|
total: Int!
|
||||||
error: String
|
error: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MyRateShout {
|
||||||
|
shout_id: Int!
|
||||||
|
my_rate: ReactionKind
|
||||||
|
}
|
||||||
|
|
||||||
|
type MyRateComment {
|
||||||
|
shout_id: Int
|
||||||
|
comment_id: Int!
|
||||||
|
my_rate: ReactionKind
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -106,11 +106,11 @@ def login_accepted(f):
|
||||||
async def decorated_function(*args, **kwargs):
|
async def decorated_function(*args, **kwargs):
|
||||||
info = args[1]
|
info = args[1]
|
||||||
req = info.context.get("request")
|
req = info.context.get("request")
|
||||||
|
|
||||||
logger.debug("login_accepted: Проверка авторизации пользователя.")
|
logger.debug("login_accepted: Проверка авторизации пользователя.")
|
||||||
user_id, user_roles = await check_auth(req)
|
user_id, user_roles = await check_auth(req)
|
||||||
logger.debug(f"login_accepted: user_id={user_id}, user_roles={user_roles}")
|
logger.debug(f"login_accepted: user_id={user_id}, user_roles={user_roles}")
|
||||||
|
|
||||||
if user_id and user_roles:
|
if user_id and user_roles:
|
||||||
logger.info(f"login_accepted: Пользователь авторизован: {user_id} с ролями {user_roles}")
|
logger.info(f"login_accepted: Пользователь авторизован: {user_id} с ролями {user_roles}")
|
||||||
info.context["user_id"] = user_id.strip()
|
info.context["user_id"] = user_id.strip()
|
||||||
|
@ -123,7 +123,9 @@ def login_accepted(f):
|
||||||
# Предполагается, что `author` является объектом с атрибутом `id`
|
# Предполагается, что `author` является объектом с атрибутом `id`
|
||||||
info.context["author"] = author.dict()
|
info.context["author"] = author.dict()
|
||||||
else:
|
else:
|
||||||
logger.error(f"login_accepted: Профиль автора не найден для пользователя {user_id}. Используем базовые данные.")# Используем базовую информацию об автор
|
logger.error(
|
||||||
|
f"login_accepted: Профиль автора не найден для пользователя {user_id}. Используем базовые данные."
|
||||||
|
) # Используем базовую информацию об автор
|
||||||
else:
|
else:
|
||||||
logger.debug("login_accepted: Пользователь не авторизован. Очищаем контекст.")
|
logger.debug("login_accepted: Пользователь не авторизован. Очищаем контекст.")
|
||||||
info.context["user_id"] = None
|
info.context["user_id"] = None
|
||||||
|
@ -131,4 +133,5 @@ def login_accepted(f):
|
||||||
info.context["author"] = None
|
info.context["author"] = None
|
||||||
|
|
||||||
return await f(*args, **kwargs)
|
return await f(*args, **kwargs)
|
||||||
|
|
||||||
return decorated_function
|
return decorated_function
|
||||||
|
|
Loading…
Reference in New Issue
Block a user