Skip to content

Commit

Permalink
notification_infra
Browse files Browse the repository at this point in the history
  • Loading branch information
권효진 authored and 권효진 committed Apr 28, 2024
1 parent a47a2e8 commit c8b7d65
Show file tree
Hide file tree
Showing 9 changed files with 196 additions and 0 deletions.
Empty file added ara/controller/api2.py
Empty file.
42 changes: 42 additions & 0 deletions ara/controller/notification/notification_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from fastapi import APIRouter, Depends, HTTPException
from ara.controller.authentication import get_current_user
from ara.service.notification.notification_service import NotificationService
from ara.domain.user import User
from ara.domain.exceptions import EntityDoesNotExist
from pydantic import BaseModel

router = APIRouter()
notification_service = NotificationService()

class NotificationRead(BaseModel):
notification_id: int

@router.get("/notifications")
async def list_notifications(current_user: User = Depends(get_current_user)):
notifications = notification_service.get_notifications_for_user(current_user)
return notifications

@router.post("/notifications/{notification_id}/read")
async def mark_notification_as_read(notification_id: int, current_user: User = Depends(get_current_user)):
try:
notification_service.mark_notification_as_read(notification_id, current_user)
except EntityDoesNotExist:
raise HTTPException(status_code=404, detail="Notification not found")
except PermissionError:
raise HTTPException(status_code=403, detail="You are not allowed to mark this notification as read")
return {"message": "Notification marked as read successfully"}

@router.post("/notifications/read-all")
async def mark_all_notifications_as_read(current_user: User = Depends(get_current_user)):
notification_service.mark_all_notifications_as_read(current_user)
return {"message": "All notifications marked as read successfully"}

@router.post("/notifications/send-push-notification")
async def send_push_notification(notification: NotificationRead, current_user: User = Depends(get_current_user)):
try:
notification_service.send_push_notification(notification.notification_id, current_user)
except EntityDoesNotExist:
raise HTTPException(status_code=404, detail="Notification not found")
except PermissionError:
raise HTTPException(status_code=403, detail="You are not allowed to send push notification for this notification")
return {"message": "Push notification sent successfully"}
Empty file.
6 changes: 6 additions & 0 deletions ara/domain/notification/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from enum import IntFlag, auto

class NameType(IntFlag):
REGULAR = auto()
ANONYMOUS = auto()
REALNAME = auto()
12 changes: 12 additions & 0 deletions ara/domain/notification/notification_domain.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from ara.domain.notification.type import NotificationInfo
from ara.infra.notification.notification_infra import NotificationInfra

class NotificationDomain:
def __init__(self) -> None:
self.notification_infra = NotificationInfra()

def get_all_notifications(self) -> list[NotificationInfo]:
return self.notification_infra.get_all_notifications()

class Config:
orm_mode = True
11 changes: 11 additions & 0 deletions ara/domain/notification/type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from pydantic import BaseModel
from typing import Optional

class NotificationInfo(BaseModel):
id: int
type: str
title: str
content: str
related_article_id: int | None
related_comment_id: Optional[int]
is_read: bool
Empty file.
56 changes: 56 additions & 0 deletions ara/infra/notification/notification_infra.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from typing import List
import logging
from ara.domain.notification.type import NotificationInfo
from ara.infra.django_infra import AraDjangoInfra
from apps.core.models import Notification, NotificationReadLog


class NotificationInfra(AraDjangoInfra[Notification]):
def __init__(self) -> None:
super().__init__(Notification)

def get_all_notifications(self) -> list[NotificationInfo]:

queryset = Notification.objects.filter(
notification_read_log_set__read_by=self.request.user,
).select_related(
"related_article",
"related_comment",
).prefetch_related(
"related_article__attachments",
NotificationReadLog.prefetch_my_notification_read_log(
self.request.user
),
)

notifications_info = [self._to_notification_info(notification) for notification in queryset]
return notifications_info


def _to_notification_info(self, notification: Notification) -> NotificationInfo:
return NotificationInfo(
id=notification.id,
type=notification.type,
title=notification.title,
content=notification.content,
related_article_id=notification.related_article_id,
related_comment_id=notification.related_comment_id,
is_read=False
)

def read_all_notifications(self) -> None:
notifications = self.get_all_notifications()
NotificationReadLog.objects.filter(notification__in=notifications, read_by=self.request.user).update(is_read=True)

def read_notification(self) -> None:
try:
notification_read_log = self.get_object().notification_read_log_set.get(
read_by=self.request.user,
)

notification_read_log.is_read = True

notification_read_log.save()
except (Notification.DoesNotExist, NotificationReadLog.DoesNotExist) as e:
logging.error(f"Failed to read notification: {e}")

69 changes: 69 additions & 0 deletions ara/service/notification/notification_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from fastapi import HTTPException
from ara.infra.notification.notification_infra import NotificationRepository
from ara.domain.notification.notification_domain import Notification
from ara.domain.exceptions import EntityDoesNotExist
from ara.domain.article import Article, NameType
from ara.domain.user_profile import UserProfile
from ara.domain.comment import Comment

class NotificationService:
def __init__(self, notification_repo: NotificationRepository):
self.notification_repo = notification_repo

def get_display_name(self, article: Article, profile: UserProfile) -> str:

if article.name_type == NameType.REALNAME:
return profile.realname
elif article.name_type == NameType.REGULAR:
return profile.nickname
else:
return "익명"

async def notify_commented(self, comment: Comment) -> None:

article = comment.parent_article if comment.parent_article else comment.parent_comment.parent_article

if comment.created_by != article.created_by:
await self._notify_article_commented(article, comment)

if comment.parent_comment and comment.created_by != comment.parent_comment.created_by:
await self._notify_comment_commented(article, comment)

async def _notify_article_commented(self, parent_article: Article, comment: Comment) -> None:

name = self.get_display_name(parent_article, comment.created_by.profile)
title = f"{name} 님이 새로운 댓글을 작성했습니다."

notification = Notification(
id=None,
type="article_commented",
title=title,
content=comment.content[:32],
related_article=parent_article,
related_comment=None
)
await self.notification_repo.save(notification)

# Send push notification
await fcm_notify_comment(parent_article.created_by, title, comment.content[:32], f"post/{parent_article.id}")

async def _notify_comment_commented(self, parent_article: Article, comment: Comment) -> None:
"""
Notifies the user when a comment is added to their comment.
"""
name = self.get_display_name(parent_article, comment.created_by.profile)
title = f"{name} 님이 새로운 대댓글을 작성했습니다."

# Save the notification
notification = Notification(
id=None, # Since it's a new notification, let the database generate the ID
type="comment_commented",
title=title,
content=comment.content[:32], # Truncate content if necessary
related_article=parent_article,
related_comment=comment.parent_comment
)
await self.notification_repo.save(notification)

# Send push notification
await fcm_notify_comment(comment.parent_comment.created_by, title, comment.content[:32], f"post/{parent_article.id}")

0 comments on commit c8b7d65

Please sign in to comment.