diff --git a/resolvers/load.py b/resolvers/load.py index 4f76060..7108a33 100644 --- a/resolvers/load.py +++ b/resolvers/load.py @@ -1,13 +1,12 @@ from services.db import local_session from resolvers.model import ( NotificationReaction, - Notification as NotificationMessage, NotificationGroup, NotificationShout, NotificationAuthor, NotificationsResult, ) -from orm.notification import NotificationSeen +from orm.notification import NotificationSeen, Notification from typing import Dict, List import time, json import strawberry @@ -15,20 +14,18 @@ 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 -): +async def get_notifications_grouped(author_id: int, after: int = 0, limit: int = 10, offset: int = 0): """ 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. + 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]: A dictionary where keys are thread IDs and values are NotificationGroup objects. + 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 @@ -43,18 +40,19 @@ async def get_notifications_grouped( } """ NotificationSeenAlias = aliased(NotificationSeen) - query = select(NotificationMessage, NotificationSeenAlias.viewer.label("seen")).outerjoin( + query = select(Notification, NotificationSeenAlias.viewer.label("seen")).outerjoin( NotificationSeen, - and_(NotificationSeen.viewer == author_id, NotificationSeen.notification == NotificationMessage.id), + and_(NotificationSeen.viewer == author_id, NotificationSeen.notification == Notification.id), ) if after: - query = query.filter(NotificationMessage.created_at > after) + query = query.filter(Notification.created_at > after) query = query.group_by(NotificationSeen.notification) - notifications: Dict[str, NotificationGroup] = {} - counter = 0 + groups_amount = 0 unread = 0 total = 0 + notifications_by_thread: Dict[str, List[Notification]] = {} + groups_by_thread: Dict[str, NotificationGroup] = {} with local_session() as session: notifications_result = session.execute(query) for n, seen in notifications_result: @@ -63,26 +61,28 @@ async def get_notifications_grouped( thread_id = "" payload = json.loads(n.payload) print(f"[resolvers.schema] {n.action} {n.entity}: {payload}") - if n.entity == "shout": + if n.entity == "shout" and n.action == "create": 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( - id=thread_id, - 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": + print(f"[resolvers.schema] create shout: {shout}") + group = groups_by_thread.get(thread_id) or NotificationGroup( + id=thread_id, + entity=n.entity, + shout=shout, + authors=shout.authors, + updated_at=shout.created_at, + reactions=[], + action="create", + seen=author_id in n.seen + ) + # store group in result + groups_by_thread[thread_id] = group + notifications = notifications_by_thread.get(thread_id, []) + if n not in notifications: + notifications.append(n) + notifications_by_thread[thread_id] = notifications + groups_amount += 1 + elif n.entity == "reaction" and n.action == "create": reaction: NotificationReaction = payload shout: NotificationShout = reaction.shout thread_id += f"{reaction.shout}" @@ -92,25 +92,31 @@ async def get_notifications_grouped( 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) + group: NotificationGroup | None = groups_by_thread.get(thread_id) + notifications: List[Notification] = notifications_by_thread.get(thread_id) + if group and notifications: + group.seen = False # any not seen notification make it false + group.shout = shout + group.authors.append(reaction.created_by) + if not group.reactions: + group.reactions = [] + group.reactions.append(reaction.id) # store group in result - notifications[thread_id] = notification_group - counter += 1 + groups_by_thread[thread_id] = group + notifications = notifications_by_thread.get(thread_id, []) + if n not in notifications: + notifications.append(n) + notifications_by_thread[thread_id] = notifications + groups_amount += 1 else: - counter += 1 - if counter > limit: + groups_amount += 1 + if groups_amount > limit: break else: # init notification group reactions = [] reactions.append(reaction.id) - notification_group = NotificationGroup( + group = NotificationGroup( id=thread_id, action=n.action, entity=n.entity, @@ -120,15 +126,19 @@ async def get_notifications_grouped( authors=[ reaction.created_by, ], + seen=author_id in n.seen ) # store group in result - notifications[thread_id] = notification_group + groups_by_thread[thread_id] = group + notifications = notifications_by_thread.get(thread_id, []) + if n not in notifications: + notifications.append(n) + notifications_by_thread[thread_id] = notifications + elif n.entity == "follower": thread_id = "followers" follower: NotificationAuthor = payload - notification_group = notifications.get(thread_id) - if not notification_group: - notification_group = NotificationGroup( + group = groups_by_thread.get(thread_id) or NotificationGroup( id=thread_id, authors=[follower], updated_at=int(time.time()), @@ -136,18 +146,22 @@ async def get_notifications_grouped( reactions=[], entity="follower", action="follow", + seen=author_id in n.seen ) - else: - notification_group.authors = [follower, ] - notification_group.updated_at = int(time.time()) + group.authors = [follower, ] + group.updated_at = int(time.time()) # store group in result - notifications[thread_id] = notification_group - counter += 1 + groups_by_thread[thread_id] = group + notifications = notifications_by_thread.get(thread_id, []) + if n not in notifications: + notifications.append(n) + notifications_by_thread[thread_id] = notifications + groups_amount += 1 - if counter > limit: + if groups_amount > limit: break - return notifications, unread, total + return groups_by_thread, unread, total @strawberry.type diff --git a/resolvers/model.py b/resolvers/model.py index fc3bdc2..357f2bf 100644 --- a/resolvers/model.py +++ b/resolvers/model.py @@ -59,6 +59,7 @@ class NotificationGroup: action: Optional[str] shout: Optional[NotificationShout] reactions: Optional[List[int]] + seen: Optional[bool] # latest reaction.created_at for reactions-updates # no timestamp for followers-updates # latest shout.created_at for shouts-updates