""" Notification Background Tasks - Async notification sending """ import logging import httpx from typing import Dict, Any from uuid import UUID from fastapi import BackgroundTasks from app.core.database import SessionLocal from app.services.notification_service import NotificationService from app.models.notification import Notification, NotificationChannel logger = logging.getLogger(__name__) notification_service = NotificationService() async def send_notification_async(notification_id: UUID): """ Background task to send notification via external channel (email, SMS, WhatsApp) Args: notification_id: Notification ID to send """ db = SessionLocal() try: # Get notification notification = db.query(Notification).filter(Notification.id == notification_id).first() if not notification: logger.error(f"Notification {notification_id} not found") return if notification.channel == NotificationChannel.IN_APP: # In-app notifications don't need external sending notification.mark_as_delivered() db.commit() return # Get user details user = notification.user if not user: logger.error(f"User not found for notification {notification_id}") notification.mark_as_failed("User not found") db.commit() return # Send based on channel result = {'success': False, 'error': 'Unknown channel'} if notification.channel == NotificationChannel.EMAIL: result = await notification_service.send_email( to_email=user.email, subject=notification.title, template_name='notification', # Generic notification template template_data={ 'title': notification.title, 'message': notification.message, 'user_name': user.full_name or user.email, 'action_url': notification.additional_metadata.get('action_url'), 'notification_type': notification.notification_type } ) elif notification.channel == NotificationChannel.WHATSAPP: if user.phone: # Send WhatsApp message async with httpx.AsyncClient() as client: response = await client.post( f"{notification_service.wasender_api_url}/send-message", headers={ 'Authorization': f'Bearer {notification_service.wasender_api_key}', 'Content-Type': 'application/json' }, json={ 'to': user.phone, 'text': f"*{notification.title}*\n\n{notification.message}" }, timeout=30.0 ) result = {'success': response.status_code == 200, 'error': response.text if response.status_code != 200 else None} else: result = {'success': False, 'error': 'User has no phone number'} elif notification.channel == NotificationChannel.SMS: # SMS not yet implemented result = {'success': False, 'error': 'SMS delivery not yet implemented'} elif notification.channel == NotificationChannel.PUSH: # Push notifications not yet implemented result = {'success': False, 'error': 'Push notifications not yet implemented'} # Update notification status if result['success']: notification.mark_as_sent() logger.info(f"Notification {notification_id} sent via {notification.channel.value}") else: notification.mark_as_failed(result.get('error', 'Unknown error')) logger.error(f"Failed to send notification {notification_id}: {result.get('error')}") db.commit() except Exception as e: logger.error(f"Error sending notification {notification_id}: {str(e)}") if notification: notification.mark_as_failed(str(e)) db.commit() finally: db.close() def queue_notification_send(notification_id: UUID, background_tasks: BackgroundTasks): """ Queue a notification for background sending Args: notification_id: Notification ID background_tasks: FastAPI BackgroundTasks instance """ background_tasks.add_task(send_notification_async, notification_id) logger.info(f"Queued notification {notification_id} for sending") # ============================================ # HELPER FUNCTIONS FOR COMMON NOTIFICATIONS # ============================================ async def notify_ticket_assignment( db, ticket_id: UUID, assignee_id: UUID, assigner_name: str, ticket_number: str ): """ Create notification for ticket assignment Args: db: Database session ticket_id: Ticket ID assignee_id: Assignee user ID assigner_name: Name of person who assigned ticket_number: Ticket number for display """ await notification_service.create_notification( db=db, user_id=assignee_id, title=f"New Ticket Assigned: {ticket_number}", message=f"{assigner_name} assigned you to ticket {ticket_number}", source_type='ticket', source_id=ticket_id, notification_type='assignment', channel='in_app', additional_metadata={ 'action_url': f'/tickets/{ticket_id}', 'ticket_number': ticket_number } ) async def notify_ticket_status_change( db, ticket_id: UUID, user_ids: list, ticket_number: str, old_status: str, new_status: str, changed_by: str ): """ Notify users about ticket status change Args: db: Database session ticket_id: Ticket ID user_ids: List of user IDs to notify ticket_number: Ticket number old_status: Previous status new_status: New status changed_by: Who changed the status """ await notification_service.create_bulk_notifications( db=db, user_ids=user_ids, title=f"Ticket {ticket_number} Status Changed", message=f"{changed_by} changed ticket status from {old_status} to {new_status}", source_type='ticket', source_id=ticket_id, notification_type='status_change', channel='in_app', additional_metadata={ 'action_url': f'/tickets/{ticket_id}', 'ticket_number': ticket_number, 'old_status': old_status, 'new_status': new_status } ) async def notify_mention( db, mentioned_user_id: UUID, mentioner_name: str, context_type: str, context_id: UUID, context_title: str, comment_text: str ): """ Notify user they were mentioned in a comment Args: db: Database session mentioned_user_id: User who was mentioned mentioner_name: User who mentioned context_type: 'ticket', 'project', etc. context_id: ID of the entity context_title: Display title comment_text: Comment excerpt """ await notification_service.create_notification( db=db, user_id=mentioned_user_id, title=f"{mentioner_name} mentioned you", message=f"You were mentioned in {context_type} {context_title}: \"{comment_text[:100]}...\"", source_type=context_type, source_id=context_id, notification_type='mention', channel='in_app', additional_metadata={ 'action_url': f'/{context_type}s/{context_id}', 'context_title': context_title } )