from services.db import local_session from resolvers.model import ( NotificationReaction, Notification as NotificationMessage, NotificationGroup, NotificationShout, NotificationAuthor, NotificationsResult, ) from orm.notification import NotificationSeen from typing import Dict import time, json import strawberry from sqlalchemy.orm import aliased from sqlalchemy import select, and_ async def get_notifications_grouped( author_id: int, after: int = 0, limit: int = 10, offset: int = 0, mark_as_read=False ) -> Dict[str, NotificationGroup]: """ Retrieves notifications for a given author. Args: author_id (int): The ID of the author for whom notifications are retrieved. session: Database connection session after (int, optional): If provided, only notifications created after this timestamp will be considered. limit (int, optional): The maximum number of notifications to retrieve. Returns: Dict[str, NotificationGroup]: A dictionary where keys are thread IDs and values are NotificationGroup objects. 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. reactions: List[Reaction], # List of reactions within the thread. authors: List[Author], # List of authors involved in the thread. } """ NotificationSeenAlias = aliased(NotificationSeen) query = select(NotificationMessage, NotificationSeenAlias.viewer.label("seen")).outerjoin( NotificationSeen, and_(NotificationSeen.viewer == author_id, NotificationSeen.notification == NotificationMessage.id), ) if after: query = query.filter(NotificationMessage.created_at > after) query = query.group_by(NotificationSeen.notification) notifications: Dict[str, NotificationGroup] = {} counter = 0 with local_session() as session: for n, seen in session.execute(query): thread_id = "" payload = json.loads(n.payload) print(f"[resolvers.schema] {n.action} {n.entity}: {payload}") if n.entity == "shout": shout: NotificationShout = payload thread_id += f"{shout.id}" if n.action == "delete": del notifications[thread_id] elif n.action == "create": print(f"[resolvers.schema] create shout: {shout}") notification_group = NotificationGroup( entity=n.entity, shout=shout, authors=shout.authors, updated_at=shout.created_at, reactions=[], action="create", ) # store group in result notifications[thread_id] = notification_group counter += 1 elif n.entity == "reaction": reaction: NotificationReaction = payload shout: NotificationShout = reaction.shout thread_id += f"{reaction.shout}" if reaction.kind == "LIKE" or reaction.kind == "DISLIKE": # TODO: making published reaction vote announce pass elif reaction.kind == "COMMENT": if reaction.reply_to: thread_id += f"{'::' + str(reaction.reply_to)}" notification_group: NotificationGroup | None = notifications.get(thread_id) if notification_group: notification_group.shout = shout notification_group.authors.append(reaction.created_by) if not notification_group.reactions: notification_group.reactions = [] notification_group.reactions.append(reaction.id) # store group in result notifications[thread_id] = notification_group counter += 1 else: counter += 1 if counter > limit: break else: # init notification group reactions = [] reactions.append(reaction.id) notification_group = NotificationGroup( action=n.action, entity=n.entity, updated_at=reaction.created_at, reactions=reactions, shout=shout, authors=[ reaction.created_by, ], ) # store group in result notifications[thread_id] = notification_group elif n.entity == "follower": thread_id = "followers" follower: NotificationAuthor = payload notification_group = notifications.get(thread_id) if not notification_group: notification_group = NotificationGroup( authors=[follower], updated_at=int(time.time()), shout=None, reactions=[], entity="follower", action="follow", ) # store group in result notifications[thread_id] = notification_group counter += 1 if counter > limit: break return notifications @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") notification_groups: Dict[str, NotificationGroup] = {} if author_id: # TODO: add total counter calculation # TODO: add unread counter calculation notification_groups = await get_notifications_grouped(author_id, after, limit, offset) notifications = sorted(notification_groups.values(), key=lambda group: group.updated_at, reverse=True) return NotificationsResult(notifications=notifications, total=0, unread=0, error=None)