LinkDesk/backend/utils/notifications.py

259 lines
9.9 KiB
Python

from typing import Optional
from sqlalchemy.orm import Session
from models.user import User
from models.task import Task, Submission, Review
from models.notification import Notification, UserNotificationPreference, NotificationType, NotificationPriority
import logging
# Set up logging for notifications
logger = logging.getLogger(__name__)
class NotificationService:
"""Notification service for creating and managing notifications."""
def _create_notification(
self,
db: Session,
user_id: int,
type: NotificationType,
title: str,
message: str,
priority: NotificationPriority = NotificationPriority.NORMAL,
project_id: Optional[int] = None,
task_id: Optional[int] = None,
submission_id: Optional[int] = None
) -> Notification:
"""Create a notification in the database."""
try:
# Check user preferences
preferences = db.query(UserNotificationPreference).filter(
UserNotificationPreference.user_id == user_id
).first()
# Create default preferences if they don't exist
if not preferences:
preferences = UserNotificationPreference(user_id=user_id)
db.add(preferences)
db.commit()
# Check if in-app notifications are enabled for this type
if not preferences.inapp_enabled:
return None
type_pref_map = {
NotificationType.TASK_ASSIGNED: preferences.inapp_task_assigned,
NotificationType.TASK_STATUS_CHANGED: preferences.inapp_task_status_changed,
NotificationType.SUBMISSION_REVIEWED: preferences.inapp_submission_reviewed,
NotificationType.WORK_SUBMITTED: preferences.inapp_work_submitted,
NotificationType.DEADLINE_APPROACHING: preferences.inapp_deadline_approaching,
NotificationType.PROJECT_UPDATE: preferences.inapp_project_update,
NotificationType.COMMENT_ADDED: preferences.inapp_comment_added,
}
if not type_pref_map.get(type, True):
return None
# Create notification
notification = Notification(
user_id=user_id,
type=type,
priority=priority,
title=title,
message=message,
project_id=project_id,
task_id=task_id,
submission_id=submission_id
)
db.add(notification)
db.commit()
db.refresh(notification)
logger.info(f"Created notification {notification.id} for user {user_id}: {title}")
# TODO: Send email if email notifications are enabled
if preferences.email_enabled and type_pref_map.get(type, True):
self._send_email_notification(notification, preferences)
return notification
except Exception as e:
logger.error(f"Failed to create notification: {str(e)}")
db.rollback()
return None
def _send_email_notification(self, notification: Notification, preferences: UserNotificationPreference):
"""Send email notification (placeholder for future implementation)."""
# TODO: Implement actual email sending
logger.info(f"Email notification would be sent for notification {notification.id}")
def notify_submission_reviewed(
self,
db: Session,
submission: Submission,
review: Review,
reviewer: User
):
"""Notify artist when their submission is reviewed."""
try:
artist = submission.user
decision_text = "approved" if review.decision == "approved" else "requires retakes"
title = f"Submission {decision_text}"
message = f"Your submission for task '{submission.task.name}' has been {decision_text} by {reviewer.first_name} {reviewer.last_name}"
if review.feedback:
message += f"\n\nFeedback: {review.feedback}"
priority = NotificationPriority.HIGH if review.decision == "retake" else NotificationPriority.NORMAL
self._create_notification(
db=db,
user_id=artist.id,
type=NotificationType.SUBMISSION_REVIEWED,
title=title,
message=message,
priority=priority,
project_id=submission.task.project_id,
task_id=submission.task_id,
submission_id=submission.id
)
except Exception as e:
logger.error(f"Failed to send notification for submission {submission.id}: {str(e)}")
def notify_task_assigned(
self,
db: Session,
task: Task,
assigned_user: User,
assigner: User
):
"""Notify user when they are assigned a task."""
try:
title = "New Task Assigned"
message = f"You have been assigned a new task: '{task.name}' by {assigner.first_name} {assigner.last_name}"
if task.deadline:
message += f"\nDeadline: {task.deadline.strftime('%Y-%m-%d')}"
if task.description:
message += f"\nDescription: {task.description}"
self._create_notification(
db=db,
user_id=assigned_user.id,
type=NotificationType.TASK_ASSIGNED,
title=title,
message=message,
priority=NotificationPriority.NORMAL,
project_id=task.project_id,
task_id=task.id
)
except Exception as e:
logger.error(f"Failed to send task assignment notification for task {task.id}: {str(e)}")
def notify_work_submitted(
self,
db: Session,
submission: Submission,
task: Task
):
"""Notify directors/coordinators when work is submitted for review."""
try:
from models.project import ProjectMember
project_members = db.query(ProjectMember).join(User).filter(
ProjectMember.project_id == task.project_id,
User.role.in_(["director", "coordinator"])
).all()
artist_name = f"{submission.user.first_name} {submission.user.last_name}"
title = "New Submission for Review"
message = f"New submission ready for review: '{task.name}' by {artist_name} (Version {submission.version_number})"
if submission.notes:
message += f"\nArtist notes: {submission.notes}"
for member in project_members:
self._create_notification(
db=db,
user_id=member.user_id,
type=NotificationType.WORK_SUBMITTED,
title=title,
message=message,
priority=NotificationPriority.NORMAL,
project_id=task.project_id,
task_id=task.id,
submission_id=submission.id
)
except Exception as e:
logger.error(f"Failed to send submission notification for submission {submission.id}: {str(e)}")
def notify_task_status_changed(
self,
db: Session,
task: Task,
old_status: str,
new_status: str,
changed_by: User
):
"""Notify relevant users when task status changes."""
try:
# Notify task owner if someone else changed the status
if task.assigned_user_id and task.assigned_user_id != changed_by.id:
title = "Task Status Updated"
message = f"Task '{task.name}' status changed from {old_status} to {new_status} by {changed_by.first_name} {changed_by.last_name}"
self._create_notification(
db=db,
user_id=task.assigned_user_id,
type=NotificationType.TASK_STATUS_CHANGED,
title=title,
message=message,
priority=NotificationPriority.NORMAL,
project_id=task.project_id,
task_id=task.id
)
except Exception as e:
logger.error(f"Failed to send task status change notification for task {task.id}: {str(e)}")
def notify_comment_added(
self,
db: Session,
task: Task,
comment_author: User,
comment_text: str
):
"""Notify task assignee when a comment is added."""
try:
if task.assigned_user_id and task.assigned_user_id != comment_author.id:
title = "New Comment on Task"
message = f"{comment_author.first_name} {comment_author.last_name} commented on task '{task.name}'"
if len(comment_text) > 100:
message += f"\n\n{comment_text[:100]}..."
else:
message += f"\n\n{comment_text}"
self._create_notification(
db=db,
user_id=task.assigned_user_id,
type=NotificationType.COMMENT_ADDED,
title=title,
message=message,
priority=NotificationPriority.LOW,
project_id=task.project_id,
task_id=task.id
)
except Exception as e:
logger.error(f"Failed to send comment notification for task {task.id}: {str(e)}")
# Global notification service instance
notification_service = NotificationService()