notifier/resolvers/load.py

178 lines
8.4 KiB
Python
Raw Normal View History

2023-12-22 09:09:03 +00:00
from services.db import local_session
from resolvers.model import (
NotificationReaction,
NotificationGroup,
NotificationShout,
NotificationAuthor,
NotificationsResult,
)
2023-12-22 14:52:55 +00:00
from orm.notification import NotificationSeen, Notification
2023-12-22 13:42:37 +00:00
from typing import Dict, List
2023-12-22 09:09:03 +00:00
import time, json
import strawberry
from sqlalchemy.orm import aliased
from sqlalchemy import select, and_
2023-12-22 14:52:55 +00:00
async def get_notifications_grouped(author_id: int, after: int = 0, limit: int = 10, offset: int = 0):
2023-12-22 09:09:03 +00:00
"""
Retrieves notifications for a given author.
Args:
author_id (int): The ID of the author for whom notifications are retrieved.
2023-12-22 14:52:55 +00:00
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
2023-12-22 09:09:03 +00:00
Returns:
2023-12-22 14:52:55 +00:00
Dict[str, NotificationGroup], int, int: A dictionary where keys are thread IDs and values are NotificationGroup objects, unread and total amounts.
2023-12-22 09:09:03 +00:00
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.
2023-12-22 15:30:56 +00:00
shout: Optional[NotificationShout]
reactions: List[int], # List of reaction ids within the thread.
authors: List[NotificationAuthor], # List of authors involved in the thread.
2023-12-22 09:09:03 +00:00
}
"""
NotificationSeenAlias = aliased(NotificationSeen)
2023-12-22 14:52:55 +00:00
query = select(Notification, NotificationSeenAlias.viewer.label("seen")).outerjoin(
2023-12-22 09:09:03 +00:00
NotificationSeen,
2023-12-22 14:52:55 +00:00
and_(NotificationSeen.viewer == author_id, NotificationSeen.notification == Notification.id),
2023-12-22 09:09:03 +00:00
)
if after:
2023-12-22 14:52:55 +00:00
query = query.filter(Notification.created_at > after)
2023-12-22 09:09:03 +00:00
query = query.group_by(NotificationSeen.notification)
2023-12-22 14:52:55 +00:00
groups_amount = 0
2023-12-22 13:42:37 +00:00
unread = 0
total = 0
2023-12-22 14:52:55 +00:00
notifications_by_thread: Dict[str, List[Notification]] = {}
groups_by_thread: Dict[str, NotificationGroup] = {}
2023-12-22 09:09:03 +00:00
with local_session() as session:
2023-12-22 15:44:22 +00:00
total = session.query(Notification).filter(and_(Notification.action == "create", Notification.created_at > after)).count()
unread = session.query(Notification).filter(and_(Notification.action == "create", Notification.created_at > after, Notification.seen.is_not(True))).count()
2023-12-22 13:42:37 +00:00
notifications_result = session.execute(query)
for n, seen in notifications_result:
2023-12-22 09:09:03 +00:00
thread_id = ""
payload = json.loads(n.payload)
print(f"[resolvers.schema] {n.action} {n.entity}: {payload}")
2023-12-22 14:52:55 +00:00
if n.entity == "shout" and n.action == "create":
2023-12-22 09:09:03 +00:00
shout: NotificationShout = payload
thread_id += f"{shout.id}"
2023-12-22 14:52:55 +00:00
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":
2023-12-22 09:09:03 +00:00
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)}"
2023-12-22 14:52:55 +00:00
group: NotificationGroup | None = groups_by_thread.get(thread_id)
2023-12-22 15:30:56 +00:00
notifications: List[Notification] = notifications_by_thread.get(thread_id, [])
2023-12-22 14:52:55 +00:00
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)
2023-12-22 09:09:03 +00:00
# store group in result
2023-12-22 14:52:55 +00:00
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
2023-12-22 09:09:03 +00:00
else:
2023-12-22 14:52:55 +00:00
groups_amount += 1
if groups_amount > limit:
2023-12-22 09:09:03 +00:00
break
else:
# init notification group
reactions = []
reactions.append(reaction.id)
2023-12-22 14:52:55 +00:00
group = NotificationGroup(
2023-12-22 10:08:35 +00:00
id=thread_id,
2023-12-22 09:09:03 +00:00
action=n.action,
entity=n.entity,
updated_at=reaction.created_at,
reactions=reactions,
shout=shout,
authors=[
reaction.created_by,
],
2023-12-22 14:52:55 +00:00
seen=author_id in n.seen
2023-12-22 09:09:03 +00:00
)
# store group in result
2023-12-22 14:52:55 +00:00
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
2023-12-22 09:09:03 +00:00
elif n.entity == "follower":
thread_id = "followers"
follower: NotificationAuthor = payload
2023-12-22 14:52:55 +00:00
group = groups_by_thread.get(thread_id) or NotificationGroup(
2023-12-22 10:08:35 +00:00
id=thread_id,
2023-12-22 09:09:03 +00:00
authors=[follower],
updated_at=int(time.time()),
shout=None,
reactions=[],
entity="follower",
action="follow",
2023-12-22 14:52:55 +00:00
seen=author_id in n.seen
2023-12-22 09:09:03 +00:00
)
2023-12-22 14:52:55 +00:00
group.authors = [follower, ]
group.updated_at = int(time.time())
2023-12-22 09:09:03 +00:00
# store group in result
2023-12-22 14:52:55 +00:00
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
2023-12-22 09:09:03 +00:00
2023-12-22 14:52:55 +00:00
if groups_amount > limit:
2023-12-22 09:09:03 +00:00
break
2023-12-22 15:30:56 +00:00
return groups_by_thread, notifications_by_thread, unread, total
2023-12-22 09:09:03 +00:00
@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")
2023-12-22 15:30:56 +00:00
groups: Dict[str, NotificationGroup] = {}
2023-12-22 09:09:03 +00:00
if author_id:
2023-12-22 15:30:56 +00:00
groups, notifications, total, unread = await get_notifications_grouped(author_id, after, limit, offset)
notifications = sorted(groups.values(), key=lambda group: group.updated_at, reverse=True)
2023-12-22 09:09:03 +00:00
return NotificationsResult(notifications=notifications, total=0, unread=0, error=None)