259 lines
9.9 KiB
Python
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() |