diff --git a/CHANGELOG.md b/CHANGELOG.md index 59b3f01c..c6a84292 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,9 @@ - **Передача сессий в тесты**: `assign_role_to_user`, `get_user_roles_in_community` теперь принимают `session` параметр - **Исправлена логика RBAC**: `if ca.role_list:` → `if not ca.role_list:` в удалении записей - **Устойчивость моков**: Тесты `test_drafts.py` и `test_update_security.py` теперь устойчивы к различиям CI/локальной среды +- Исправления интерфейса уведомлений +- Исправление выдачи всех авторов +- Исправление резолверов для тем ## [0.9.7] - 2025-08-18 diff --git a/resolvers/notifier.py b/resolvers/notifier.py index 5d0ce244..6e138aeb 100644 --- a/resolvers/notifier.py +++ b/resolvers/notifier.py @@ -1,5 +1,5 @@ import time -from datetime import datetime +from datetime import UTC, datetime from typing import Any import orjson @@ -34,36 +34,21 @@ def query_notifications(author_id: int, after: int = 0) -> tuple[int, int, list[ ) if after: # Convert Unix timestamp to datetime for PostgreSQL compatibility - after_datetime = datetime.fromtimestamp(after) + after_datetime = datetime.fromtimestamp(after, tz=UTC) q = q.where(Notification.created_at > after_datetime) q = q.group_by(NotificationSeen.notification, Notification.created_at) with local_session() as session: - # Convert Unix timestamp to datetime for PostgreSQL compatibility - after_datetime = datetime.fromtimestamp(after) if after else None - - total = ( - session.query(Notification) - .where( - and_( - Notification.action == NotificationAction.CREATE.value, - Notification.created_at > after_datetime, - ) - ) - .count() - ) + # Build query conditions + conditions = [Notification.action == NotificationAction.CREATE.value] + if after: + after_datetime = datetime.fromtimestamp(after, tz=UTC) + conditions.append(Notification.created_at > after_datetime) - unread = ( - session.query(Notification) - .where( - and_( - Notification.action == NotificationAction.CREATE.value, - Notification.created_at > after_datetime, - not_(Notification.seen), - ) - ) - .count() - ) + total = session.query(Notification).where(and_(*conditions)).count() + + unread_conditions = [*conditions, not_(Notification.seen)] + unread = session.query(Notification).where(and_(*unread_conditions)).count() notifications_result = session.execute(q) notifications = [] @@ -267,8 +252,11 @@ async def notifications_seen_after(_: None, info: GraphQLResolveInfo, after: int if author_id: with local_session() as session: # Convert Unix timestamp to datetime for PostgreSQL compatibility - after_datetime = datetime.fromtimestamp(after) if after else None - nnn = session.query(Notification).where(and_(Notification.created_at > after_datetime)).all() + after_datetime = datetime.fromtimestamp(after, tz=UTC) if after else None + if after_datetime: + nnn = session.query(Notification).where(and_(Notification.created_at > after_datetime)).all() + else: + nnn = session.query(Notification).all() for notification in nnn: ns = NotificationSeen(notification=notification.id, author=author_id) session.add(ns) @@ -288,27 +276,45 @@ async def notifications_seen_thread(_: None, info: GraphQLResolveInfo, thread: s [shout_id, reply_to_id] = thread.split(":") with local_session() as session: # Convert Unix timestamp to datetime for PostgreSQL compatibility - after_datetime = datetime.fromtimestamp(after) if after else None - + after_datetime = datetime.fromtimestamp(after, tz=UTC) if after else None + # TODO: handle new follower and new shout notifications - new_reaction_notifications = ( - session.query(Notification) - .where( - Notification.action == "create", - Notification.entity == "reaction", - Notification.created_at > after_datetime, + if after_datetime: + new_reaction_notifications = ( + session.query(Notification) + .where( + Notification.action == "create", + Notification.entity == "reaction", + Notification.created_at > after_datetime, + ) + .all() ) - .all() - ) - removed_reaction_notifications = ( - session.query(Notification) - .where( - Notification.action == "delete", - Notification.entity == "reaction", - Notification.created_at > after_datetime, + removed_reaction_notifications = ( + session.query(Notification) + .where( + Notification.action == "delete", + Notification.entity == "reaction", + Notification.created_at > after_datetime, + ) + .all() + ) + else: + new_reaction_notifications = ( + session.query(Notification) + .where( + Notification.action == "create", + Notification.entity == "reaction", + ) + .all() + ) + removed_reaction_notifications = ( + session.query(Notification) + .where( + Notification.action == "delete", + Notification.entity == "reaction", + ) + .all() ) - .all() - ) exclude = set() for nr in removed_reaction_notifications: reaction = orjson.loads(str(nr.payload)) diff --git a/resolvers/topic.py b/resolvers/topic.py index 69fa68a0..9a2fe873 100644 --- a/resolvers/topic.py +++ b/resolvers/topic.py @@ -228,7 +228,7 @@ async def get_topics_with_stats( WHERE st.topic IN ({placeholders}) GROUP BY st.topic """ - params = {f"id{i}": topic_id for i, topic_id in enumerate(topic_ids)} + params: dict[str, int | str] = {f"id{i}": topic_id for i, topic_id in enumerate(topic_ids)} shouts_stats = {row[0]: row[1] for row in session.execute(text(shouts_stats_query), params)} # Запрос на получение статистики по подписчикам для выбранных тем diff --git a/storage/schema.py b/storage/schema.py index be2785d0..d5a67961 100644 --- a/storage/schema.py +++ b/storage/schema.py @@ -11,7 +11,7 @@ from ariadne import ( from orm import collection, community, draft, invite, notification, reaction, shout, topic from orm.author import Author, AuthorBookmark, AuthorFollower, AuthorRating -from storage.db import create_table_if_not_exists, local_session +from storage.db import create_table_if_not_exists # Создаем основные типы query = QueryType() @@ -69,7 +69,7 @@ def create_all_tables() -> None: ] from storage.db import engine - + # Используем одно соединение для всех таблиц, чтобы избежать проблем с транзакциями with engine.connect() as connection: for model in models_in_order: