This commit is contained in:
@@ -34,6 +34,7 @@ async def handle_notification(n: ServiceMessage, channel: str):
|
||||
|
||||
|
||||
async def listen_task(pattern):
|
||||
logger.info(f' listening {pattern} ...')
|
||||
async for message_data, channel in redis.listen(pattern):
|
||||
try:
|
||||
if message_data:
|
||||
|
||||
@@ -8,7 +8,12 @@ from sqlalchemy import and_, select
|
||||
from sqlalchemy.orm import aliased
|
||||
from sqlalchemy.sql import not_
|
||||
|
||||
from orm.notification import Notification, NotificationAction, NotificationEntity, NotificationSeen
|
||||
from orm.notification import (
|
||||
Notification,
|
||||
NotificationAction,
|
||||
NotificationEntity,
|
||||
NotificationSeen,
|
||||
)
|
||||
from resolvers.model import (
|
||||
NotificationAuthor,
|
||||
NotificationGroup,
|
||||
@@ -19,15 +24,22 @@ from resolvers.model import (
|
||||
from services.db import local_session
|
||||
|
||||
|
||||
logger = logging.getLogger('[resolvers.schema]')
|
||||
logger = logging.getLogger("[resolvers.schema]")
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
|
||||
def query_notifications(author_id: int, after: int = 0) -> Tuple[int, int, List[Tuple[Notification, bool]]]:
|
||||
def query_notifications(
|
||||
author_id: int, after: int = 0
|
||||
) -> Tuple[int, int, List[Tuple[Notification, bool]]]:
|
||||
notification_seen_alias = aliased(NotificationSeen)
|
||||
query = select(Notification, notification_seen_alias.viewer.label('seen')).outerjoin(
|
||||
query = select(
|
||||
Notification, notification_seen_alias.viewer.label("seen")
|
||||
).outerjoin(
|
||||
NotificationSeen,
|
||||
and_(NotificationSeen.viewer == author_id, NotificationSeen.notification == Notification.id),
|
||||
and_(
|
||||
NotificationSeen.viewer == author_id,
|
||||
NotificationSeen.notification == Notification.id,
|
||||
),
|
||||
)
|
||||
if after:
|
||||
query = query.filter(Notification.created_at > after)
|
||||
@@ -36,7 +48,12 @@ def query_notifications(author_id: int, after: int = 0) -> Tuple[int, int, List[
|
||||
with local_session() as session:
|
||||
total = (
|
||||
session.query(Notification)
|
||||
.filter(and_(Notification.action == NotificationAction.CREATE.value, Notification.created_at > after))
|
||||
.filter(
|
||||
and_(
|
||||
Notification.action == NotificationAction.CREATE.value,
|
||||
Notification.created_at > after,
|
||||
)
|
||||
)
|
||||
.count()
|
||||
)
|
||||
|
||||
@@ -63,7 +80,9 @@ def query_notifications(author_id: int, after: int = 0) -> Tuple[int, int, List[
|
||||
def process_shout_notification(
|
||||
notification: Notification, seen: bool
|
||||
) -> Union[Tuple[str, NotificationGroup], None] | None:
|
||||
if not isinstance(notification.payload, str) or not isinstance(notification.entity, str):
|
||||
if not isinstance(notification.payload, str) or not isinstance(
|
||||
notification.entity, str
|
||||
):
|
||||
return
|
||||
payload = json.loads(notification.payload)
|
||||
shout: NotificationShout = payload
|
||||
@@ -75,7 +94,7 @@ def process_shout_notification(
|
||||
authors=shout.authors,
|
||||
updated_at=shout.created_at,
|
||||
reactions=[],
|
||||
action='create',
|
||||
action="create",
|
||||
seen=seen,
|
||||
)
|
||||
return thread_id, group
|
||||
@@ -94,8 +113,8 @@ def process_reaction_notification(
|
||||
reaction: NotificationReaction = payload
|
||||
shout: NotificationShout = reaction.shout
|
||||
thread_id = str(reaction.shout)
|
||||
if reaction.kind == 'COMMENT' and reaction.reply_to:
|
||||
thread_id += f'::{reaction.reply_to}'
|
||||
if reaction.kind == "COMMENT" and reaction.reply_to:
|
||||
thread_id += f"::{reaction.reply_to}"
|
||||
group = NotificationGroup(
|
||||
id=thread_id,
|
||||
action=str(notification.action),
|
||||
@@ -116,15 +135,15 @@ def process_follower_notification(
|
||||
return
|
||||
payload = json.loads(notification.payload)
|
||||
follower: NotificationAuthor = payload
|
||||
thread_id = 'followers'
|
||||
thread_id = "followers"
|
||||
group = NotificationGroup(
|
||||
id=thread_id,
|
||||
authors=[follower],
|
||||
updated_at=int(time.time()),
|
||||
shout=None,
|
||||
reactions=[],
|
||||
entity='follower',
|
||||
action='follow',
|
||||
entity="follower",
|
||||
action="follow",
|
||||
seen=seen,
|
||||
)
|
||||
return thread_id, group
|
||||
@@ -133,6 +152,31 @@ def process_follower_notification(
|
||||
async def get_notifications_grouped(
|
||||
author_id: int, after: int = 0, limit: int = 10
|
||||
) -> Tuple[Dict[str, NotificationGroup], int, int]:
|
||||
"""
|
||||
Retrieves notifications for a given author.
|
||||
|
||||
Args:
|
||||
author_id (int): The ID of the author for whom notifications are retrieved.
|
||||
after (int, optional): If provided, selects only notifications created after this timestamp will be considered.
|
||||
limit (int, optional): The maximum number of groupa to retrieve.
|
||||
offset (int, optional): Offset for pagination
|
||||
|
||||
Returns:
|
||||
Dict[str, NotificationGroup], int, int: A dictionary where keys are thread IDs and values are NotificationGroup objects, unread and total amounts.
|
||||
|
||||
This function queries the database to retrieve notifications for the specified author, considering optional filters.
|
||||
The result is a dictionary where each key is a thread ID, and the corresponding value is a NotificationGroup
|
||||
containing information about the notifications within that thread.
|
||||
|
||||
NotificationGroup structure:
|
||||
{
|
||||
entity: str, # Type of entity (e.g., 'reaction', 'shout', 'follower').
|
||||
updated_at: int, # Timestamp of the latest update in the thread.
|
||||
shout: Optional[NotificationShout]
|
||||
reactions: List[int], # List of reaction ids within the thread.
|
||||
authors: List[NotificationAuthor], # List of authors involved in the thread.
|
||||
}
|
||||
"""
|
||||
total, unread, notifications = query_notifications(author_id, after)
|
||||
groups_by_thread: Dict[str, NotificationGroup] = {}
|
||||
groups_amount = 0
|
||||
@@ -141,7 +185,7 @@ async def get_notifications_grouped(
|
||||
if groups_amount >= limit:
|
||||
break
|
||||
|
||||
if str(notification.entity) == 'shout' and str(notification.action) == 'create':
|
||||
if str(notification.entity) == "shout" and str(notification.action) == "create":
|
||||
result = process_shout_notification(notification, seen)
|
||||
if result:
|
||||
thread_id, group = result
|
||||
@@ -168,7 +212,7 @@ async def get_notifications_grouped(
|
||||
groups_by_thread[thread_id] = group
|
||||
groups_amount += 1
|
||||
|
||||
elif str(notification.entity) == 'follower':
|
||||
elif str(notification.entity) == "follower":
|
||||
result = process_follower_notification(notification, seen)
|
||||
if result:
|
||||
thread_id, group = result
|
||||
@@ -181,10 +225,18 @@ async def get_notifications_grouped(
|
||||
@strawberry.type
|
||||
class Query:
|
||||
@strawberry.field
|
||||
async def load_notifications(self, info, after: int, limit: int = 50, offset: int = 0) -> NotificationsResult:
|
||||
author_id = info.context.get('author_id')
|
||||
async def load_notifications(
|
||||
self, info, after: int, limit: int = 50, offset: int = 0
|
||||
) -> NotificationsResult:
|
||||
author_id = info.context.get("author_id")
|
||||
if author_id:
|
||||
groups, unread, total = await get_notifications_grouped(author_id, after, limit)
|
||||
notifications = sorted(groups.values(), key=lambda group: group.updated_at, reverse=True)
|
||||
return NotificationsResult(notifications=notifications, total=total, unread=unread, error=None)
|
||||
groups, unread, total = await get_notifications_grouped(
|
||||
author_id, after, limit
|
||||
)
|
||||
notifications = sorted(
|
||||
groups.values(), key=lambda group: group.updated_at, reverse=True
|
||||
)
|
||||
return NotificationsResult(
|
||||
notifications=notifications, total=total, unread=unread, error=None
|
||||
)
|
||||
return NotificationsResult(notifications=[], total=0, unread=0, error=None)
|
||||
|
||||
Reference in New Issue
Block a user