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()