notifier/resolvers/load.py

219 lines
9.0 KiB
Python
Raw Permalink Normal View History

2024-01-26 00:40:49 +00:00
import json
import logging
import time
from typing import Dict, List
2024-01-23 09:15:10 +00:00
2024-01-26 00:40:49 +00:00
import strawberry
from sqlalchemy import and_, select
from sqlalchemy.orm import aliased
2024-01-23 13:40:53 +00:00
from sqlalchemy.sql import not_
2024-01-26 00:40:49 +00:00
from orm.notification import (
Notification,
NotificationAction,
NotificationEntity,
NotificationSeen,
)
2023-12-22 09:09:03 +00:00
from resolvers.model import (
2024-01-26 00:40:49 +00:00
NotificationAuthor,
2023-12-22 09:09:03 +00:00
NotificationGroup,
2024-01-26 00:40:49 +00:00
NotificationReaction,
2023-12-22 09:09:03 +00:00
NotificationShout,
NotificationsResult,
)
2024-01-26 00:40:49 +00:00
from services.db import local_session
2024-01-22 19:05:19 +00:00
2024-01-26 00:40:49 +00:00
logger = logging.getLogger('[resolvers.schema] ')
2024-01-22 19:05:19 +00:00
logger.setLevel(logging.DEBUG)
2023-12-22 09:09:03 +00:00
2024-01-26 00:40:49 +00:00
async def get_notifications_grouped( # noqa: C901
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
}
"""
2024-01-26 00:40:49 +00:00
seen_alias = aliased(NotificationSeen)
query = select(Notification, seen_alias.viewer.label('seen')).outerjoin(
2023-12-22 09:09:03 +00:00
NotificationSeen,
2024-01-26 00:40:49 +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)
2024-01-23 18:39:20 +00:00
query = query.group_by(NotificationSeen.notification, Notification.created_at)
2023-12-22 09:09:03 +00:00
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:
2024-01-26 00:40:49 +00:00
total = (
session.query(Notification)
.filter(
and_(
Notification.action == NotificationAction.CREATE.value,
Notification.created_at > after,
)
2024-01-23 09:15:10 +00:00
)
2024-01-26 00:40:49 +00:00
.count()
)
unread = (
session.query(Notification)
.filter(
and_(
Notification.action == NotificationAction.CREATE.value,
Notification.created_at > after,
not_(Notification.seen),
)
)
.count()
)
2023-12-22 13:42:37 +00:00
notifications_result = session.execute(query)
2024-01-26 00:40:49 +00:00
for n, _seen in notifications_result:
thread_id = ''
2023-12-22 09:09:03 +00:00
payload = json.loads(n.payload)
2024-01-26 00:40:49 +00:00
logger.debug(f'[resolvers.schema] {n.action} {n.entity}: {payload}')
if n.entity == 'shout' and n.action == 'create':
2023-12-22 09:09:03 +00:00
shout: NotificationShout = payload
2024-01-26 00:40:49 +00:00
thread_id += f'{shout.id}'
logger.debug(f'create shout: {shout}')
2023-12-22 14:52:55 +00:00
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=[],
2024-01-26 00:40:49 +00:00
action='create',
seen=author_id in n.seen,
2023-12-22 14:52:55 +00:00
)
# 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
2024-01-23 09:05:28 +00:00
elif n.entity == NotificationEntity.REACTION.value and n.action == NotificationAction.CREATE.value:
2023-12-22 09:09:03 +00:00
reaction: NotificationReaction = payload
shout: NotificationShout = reaction.shout
2024-01-26 00:40:49 +00:00
thread_id += f'{reaction.shout}'
if not bool(reaction.reply_to) and (reaction.kind == 'LIKE' or reaction.kind == 'DISLIKE'):
2023-12-22 09:09:03 +00:00
# TODO: making published reaction vote announce
pass
2024-01-26 00:40:49 +00:00
elif reaction.kind == 'COMMENT':
2023-12-22 09:09:03 +00:00
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
2024-01-26 00:40:49 +00:00
reactions = [
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,
],
2024-01-26 00:40:49 +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
2024-01-26 00:40:49 +00:00
elif n.entity == 'follower':
thread_id = 'followers'
2023-12-22 09:09:03 +00:00
follower: NotificationAuthor = payload
2023-12-22 14:52:55 +00:00
group = groups_by_thread.get(thread_id) or NotificationGroup(
2024-01-26 00:40:49 +00:00
id=thread_id,
authors=[follower],
updated_at=int(time.time()),
shout=None,
reactions=[],
entity='follower',
action='follow',
seen=author_id in n.seen,
)
group.authors = [
follower,
]
2023-12-22 14:52:55 +00:00
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:
2024-01-26 00:40:49 +00:00
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)